{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"from IPython.display import clear_output\n!pip install git+https://github.com/shijianjian/EfficientNet-PyTorch-3D\nclear_output()","metadata":{"id":"wX0WZIB9eXIx","executionInfo":{"status":"ok","timestamp":1634106676759,"user_tz":-330,"elapsed":6388,"user":{"displayName":"vivek joshi","photoUrl":"https://lh3.googleusercontent.com/a/default-user=s64","userId":"14701461628611770995"}},"execution":{"iopub.status.busy":"2022-06-22T09:12:01.212304Z","iopub.execute_input":"2022-06-22T09:12:01.212874Z","iopub.status.idle":"2022-06-22T09:12:12.633648Z","shell.execute_reply.started":"2022-06-22T09:12:01.212821Z","shell.execute_reply":"2022-06-22T09:12:12.632740Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import os\nimport sys \nimport json\nfrom glob import glob\nimport random\nimport collections\nimport time\nimport re\nimport warnings\n\nimport numpy as np\nimport pandas as pd\nimport cv2\nimport matplotlib.pyplot as plt\nimport seaborn as sns\n\nimport torch\nfrom torch import nn\nfrom torch.utils import data as torch_data\nfrom sklearn import model_selection as sk_model_selection\nfrom torch.nn import functional as torch_functional\nimport torch.nn.functional as F\nfrom torchvision import transforms\nimport torchvision.models as models\nimport pydicom\nfrom pydicom.pixel_data_handlers.util import apply_voi_lut\nfrom sklearn.model_selection import StratifiedKFold\nfrom sklearn.metrics import roc_auc_score\nfrom efficientnet_pytorch_3d import EfficientNet3D\nimport joblib\nfrom tqdm.notebook import tqdm\n\ndef set_seed(seed):\n    random.seed(seed)\n    os.environ[\"PYTHONHASHSEED\"] = str(seed)\n    np.random.seed(seed)\n    torch.manual_seed(seed)\n    if torch.cuda.is_available():\n        torch.cuda.manual_seed_all(seed)\n        torch.backends.cudnn.deterministic = True\n\nset_seed(1)\nwarnings.filterwarnings(\"ignore\") \nwarnings.filterwarnings(\"ignore\", category=DeprecationWarning) \n\ntorch.hub._validate_not_a_forked_repo=lambda a,b,c: True","metadata":{"id":"ABMUd569Qp5Z","executionInfo":{"status":"ok","timestamp":1634113508098,"user_tz":-330,"elapsed":1342,"user":{"displayName":"vivek joshi","photoUrl":"https://lh3.googleusercontent.com/a/default-user=s64","userId":"14701461628611770995"}},"execution":{"iopub.status.busy":"2022-06-22T09:12:12.635544Z","iopub.execute_input":"2022-06-22T09:12:12.636014Z","iopub.status.idle":"2022-06-22T09:12:15.430450Z","shell.execute_reply.started":"2022-06-22T09:12:12.635973Z","shell.execute_reply":"2022-06-22T09:12:15.429655Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"data_directory = './'\nmri_type = 'FLAIR'\ntrain = pd.read_csv(\"../input/rsna-miccai-brain-tumor-radiogenomic-classification/train_labels.csv\")\nl = [x.split('/')[-1] for x in glob('../input/rsna-miccai-brain-tumor-radiogenomic-classification/train/*')]\ntrain.BraTS21ID = l\n\nif mri_type=='all':\n  train_df = train.iloc[50::]  \n  SIZE = 256\n  NUM_IMAGES = 64\n  batch_size = 4\n  in_channels = 3\n  lr = 0.000001\n\nif mri_type=='FLAIR':\n  train_df = train.iloc[0:50] \n  SIZE = 256\n  NUM_IMAGES = 64\n  batch_size = 4\n  in_channels = 3\n  lr = 0.00002\n\nelif mri_type=='T1w':\n  SIZE = 256\n  NUM_IMAGES = 64\n  batch_size = 4\n  in_channels = 3\n  lr = 0.00002\n\nelif mri_type=='T1wCE':\n  SIZE = 256\n  NUM_IMAGES = 64\n  batch_size = 4\n  in_channels = 3\n  lr = 0.00002\n\nelif mri_type=='T2w':\n  SIZE = 256\n  NUM_IMAGES = 64\n  batch_size = 4\n  in_channels = 3\n  lr = 0.00002","metadata":{"id":"2QcqYwzrQpjJ","executionInfo":{"status":"ok","timestamp":1634113508603,"user_tz":-330,"elapsed":511,"user":{"displayName":"vivek joshi","photoUrl":"https://lh3.googleusercontent.com/a/default-user=s64","userId":"14701461628611770995"}},"execution":{"iopub.status.busy":"2022-06-22T09:12:15.433491Z","iopub.execute_input":"2022-06-22T09:12:15.434039Z","iopub.status.idle":"2022-06-22T09:12:15.564145Z","shell.execute_reply.started":"2022-06-22T09:12:15.434011Z","shell.execute_reply":"2022-06-22T09:12:15.563459Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"SIZE = 256\n\ndef read_mri(path, voi_lut=False, fix_monochrome=True):\n    dicom = pydicom.read_file(path)    \n    data = dicom.pixel_array.astype(float)\n    \n    if voi_lut:\n        data = apply_voi_lut(data, dicom)\n    \n    if fix_monochrome:\n        if dicom.PhotometricInterpretation == \"MONOCHROME1\":\n            data = np.amax(data) - data\n        \n    data = data - np.min(data)\n    if np.max(data)!=0:\n        data = data / np.max(data)\n    data = (data * 255).astype(np.uint8)\n            \n    data = cv2.resize(data, (SIZE, SIZE))\n    \n    return data\n\ndef id2path(img_id, is_test):\n    if is_test: \n        return glob(f'../input/rsna-miccai-brain-tumor-radiogenomic-classification/test/{img_id}/*/*.dcm')\n    else: \n        return glob(f'../input/rsna-miccai-brain-tumor-radiogenomic-classification/train/{img_id}/*/*.dcm')\n    \ndef save_train_img(_id):\n    fnames = id2path(_id, False)\n    os.mkdir('train/'+str(_id))\n    os.mkdir('train/'+str(_id)+'/FLAIR')\n    os.mkdir('train/'+str(_id)+'/T1w')\n    os.mkdir('train/'+str(_id)+'/T1wCE')\n    os.mkdir('train/'+str(_id)+'/T2w')\n    for fname in fnames:\n        im = read_mri(fname)\n        n = fname.replace('../input/rsna-miccai-brain-tumor-radiogenomic-classification/','')\n        n = n.replace('dcm','png')\n        cv2.imwrite(n, im)    \n\n!mkdir train\n    \n_ = joblib.Parallel(n_jobs=-1)(joblib.delayed(save_train_img)(_id) for _id in tqdm(train_df.BraTS21ID))","metadata":{"execution":{"iopub.status.busy":"2022-06-22T09:12:15.565556Z","iopub.execute_input":"2022-06-22T09:12:15.565964Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def get_transforms():\n    return transforms.Compose([\n        transforms.ToPILImage(),                               \n        transforms.RandomVerticalFlip(p=0.05),\n        transforms.RandomAutocontrast(),\n        transforms.RandomGrayscale(),\n    ]) \n\ndef load_dicom_image(path, img_size=SIZE, agument=False):\n    img = cv2.imread(path)\n    img = cv2.resize(img, (SIZE,SIZE))\n\n    if agument:\n        img = get_transforms()(img)\n\n    return img\n\ndef load_dicom_images_3d(scan_id, num_imgs=NUM_IMAGES, img_size=SIZE, mri_type=\"FLAIR\", split=\"train\", agument=False):\n\n    files = sorted(glob(f\"{data_directory}/{split}/{scan_id}/{mri_type}/*.png\"), \n               key=lambda var:[int(x) if x.isdigit() else x for x in re.findall(r'[^0-9]|[0-9]+', var)])\n\n    middle = len(files)//2\n    num_imgs2 = num_imgs//2\n    p1 = max(0, middle - num_imgs2)\n    p2 = min(len(files), middle + num_imgs2)\n    img3d = np.stack([load_dicom_image(f, agument=agument) for f in files[p1:p2]]).T \n    if img3d.shape[-1] < num_imgs:\n        n_zero = np.zeros((img3d.shape[:-1] + (num_imgs - img3d.shape[-1],)))\n        img3d = np.concatenate((img3d,  n_zero), axis=-1)\n            \n    return img3d","metadata":{"id":"C7xAHxK3QpzJ","executionInfo":{"status":"ok","timestamp":1634113508604,"user_tz":-330,"elapsed":16,"user":{"displayName":"vivek joshi","photoUrl":"https://lh3.googleusercontent.com/a/default-user=s64","userId":"14701461628611770995"}},"execution":{"iopub.status.busy":"2022-06-22T09:00:22.395156Z","iopub.execute_input":"2022-06-22T09:00:22.395866Z","iopub.status.idle":"2022-06-22T09:00:22.406633Z","shell.execute_reply.started":"2022-06-22T09:00:22.395829Z","shell.execute_reply":"2022-06-22T09:00:22.405851Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"a = load_dicom_images_3d(\"00452\")\nz = np.zeros((a.shape[:-1]+(4,)))\nprint(a.shape)\nprint(np.min(a), np.max(a), np.mean(a), np.median(a))","metadata":{"id":"bKjNnnEPQpqB","executionInfo":{"status":"ok","timestamp":1634113508605,"user_tz":-330,"elapsed":15,"user":{"displayName":"vivek joshi","photoUrl":"https://lh3.googleusercontent.com/a/default-user=s64","userId":"14701461628611770995"}},"outputId":"bd220c79-bc3f-426e-99f4-5a21e1154741","execution":{"iopub.status.busy":"2022-06-22T09:01:02.308239Z","iopub.execute_input":"2022-06-22T09:01:02.308616Z","iopub.status.idle":"2022-06-22T09:01:02.939894Z","shell.execute_reply.started":"2022-06-22T09:01:02.308584Z","shell.execute_reply":"2022-06-22T09:01:02.939077Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df_train, df_valid = sk_model_selection.train_test_split(\n    train_df, \n    test_size=0.1, \n    random_state=1, \n    stratify=train_df[\"MGMT_value\"],\n)\n\ndf_train.shape, df_valid.shape","metadata":{"id":"Viv6hxhTQpfh","executionInfo":{"status":"ok","timestamp":1634113508607,"user_tz":-330,"elapsed":14,"user":{"displayName":"vivek joshi","photoUrl":"https://lh3.googleusercontent.com/a/default-user=s64","userId":"14701461628611770995"}},"outputId":"4f1c14aa-f5c9-4cb9-cf8c-35c84a4631fc","execution":{"iopub.status.busy":"2022-06-22T09:01:05.979877Z","iopub.execute_input":"2022-06-22T09:01:05.980672Z","iopub.status.idle":"2022-06-22T09:01:06.005455Z","shell.execute_reply.started":"2022-06-22T09:01:05.980626Z","shell.execute_reply":"2022-06-22T09:01:06.004748Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def onehot(size, target):\n    vec = torch.zeros(size, dtype=torch.float64)\n    vec[target] = 1.\n    return vec  \n\nclass Dataset(torch_data.Dataset):\n    def __init__(self, paths, targets=None, mri_type=None, split=\"train\", augment=False):\n        self.paths = paths\n        self.targets = targets\n        self.mri_type = mri_type\n        self.split = split\n        self.augment = augment\n          \n    def __len__(self):\n        return len(self.paths)\n    \n    def __getitem__(self, index):\n        scan_id = self.paths[index]\n        if self.targets is None:\n            data = load_dicom_images_3d(str(scan_id).zfill(5), mri_type=self.mri_type[index], split=self.split)\n        else:\n            if self.augment:\n                data = load_dicom_images_3d(str(scan_id).zfill(5), mri_type=self.mri_type[index], split=\"train\", agument=True)\n            else:\n                data = load_dicom_images_3d(str(scan_id).zfill(5), mri_type=self.mri_type[index], split=\"train\")\n\n        if self.targets is None:\n            return {\"X\": torch.tensor(data).float(), \"id\": scan_id}\n        else:\n            y = torch.tensor(abs(self.targets[index]- 0.01), dtype=torch.float)\n            return {\"X\": torch.tensor(data).float(), \"y\": y}\n\nclass Model(nn.Module):\n    def __init__(self):\n        super().__init__()\n        self.net = EfficientNet3D.from_name(\"efficientnet-b7\", override_params={'num_classes': 2}, in_channels=in_channels)\n        n_features = self.net._fc.in_features\n        self.net._fc = nn.Linear(in_features=n_features, out_features=1, bias=True)\n\n    def forward(self, x):\n        out = self.net(x)\n        return out\n    \nclass Trainer:\n    def __init__(\n        self, \n        model, \n        device, \n        optimizer, \n        criterion,\n        scheduler\n    ):\n        self.model = model\n        self.device = device\n        self.optimizer = optimizer\n        self.criterion = criterion\n        self.scheduler = scheduler\n\n        self.best_valid_auc = 0\n        self.n_patience = 0\n        self.lastmodel = None\n        \n    def fit(self, epochs, train_loader, valid_loader, save_path, patience):        \n        for n_epoch in range(1, epochs + 1):\n            self.info_message(\"EPOCH: {}, \\nLR: {}\", n_epoch, self.optimizer.param_groups[0]['lr'])\n            \n            train_loss, train_time = self.train_epoch(train_loader)\n\n            valid_loss, valid_auc, valid_time = self.valid_epoch(valid_loader)\n            \n            self.scheduler.step(valid_loss)\n            \n            self.info_message(\n                \"[Epoch Train: {}] loss: {:.4f}, time: {:.2f} s            \",\n                n_epoch, train_loss, train_time\n            )\n            \n            self.info_message(\n                \"[Epoch Valid: {}] loss: {:.4f}, auc: {:.4f}, time: {:.2f} s\",\n                n_epoch, valid_loss, valid_auc, valid_time\n            )\n\n            if self.best_valid_auc < valid_auc: \n                self.save_model(n_epoch, save_path, valid_loss, valid_auc)\n                self.info_message(\n                     \"auc improved from {:.4f} to {:.4f}. Saved model to '{}'\", \n                    self.best_valid_auc, valid_auc, self.lastmodel\n                )\n                self.best_valid_auc = valid_auc\n                self.n_patience = 0\n            else:\n                self.n_patience += 1\n\n            if self.n_patience >= patience:\n                self.info_message(\"\\nValid auc didn't improve last {} epochs.\", patience)\n                break\n            \n    def train_epoch(self, train_loader):\n        self.model.train()\n        t = time.time()\n        sum_loss = 0\n\n        for step, batch in enumerate(train_loader, 1):\n            X = batch[\"X\"].to(self.device)\n            targets = batch[\"y\"].to(self.device)\n            self.optimizer.zero_grad()\n            outputs = self.model(X).squeeze(1)\n            \n            loss = self.criterion(outputs, targets)\n            loss.backward()\n\n            sum_loss += loss.detach().item()\n\n            self.optimizer.step()\n            \n            message = 'Train Step {}/{}, train_loss: {:.4f}'\n            self.info_message(message, step, len(train_loader), sum_loss/step, end=\"\\r\")\n        \n        return sum_loss/len(train_loader), int(time.time() - t)\n    \n    def valid_epoch(self, valid_loader):\n        self.model.eval()\n        t = time.time()\n        sum_loss = 0\n        y_all = []\n        outputs_all = []\n\n        for step, batch in enumerate(valid_loader, 1):\n            with torch.no_grad():\n                X = batch[\"X\"].to(self.device)\n                targets = batch[\"y\"].to(self.device)\n\n                outputs = self.model(X).squeeze(1)\n                loss = self.criterion(outputs, targets)\n\n                sum_loss += loss.detach().item()\n                y_all.extend(batch[\"y\"].tolist())\n                outputs_all.extend(torch.sigmoid(outputs).tolist())\n\n            message = 'Valid Step {}/{}, valid_loss: {:.4f}'\n            self.info_message(message, step, len(valid_loader), sum_loss/step, end=\"\\r\")\n            \n        y_all = [1 if x > 0.5 else 0 for x in y_all]\n        auc = roc_auc_score(y_all, outputs_all)\n        \n        return sum_loss/len(valid_loader), auc, int(time.time() - t)\n    \n    def save_model(self, n_epoch, save_path, loss, auc):\n        self.lastmodel = f\"{save_path}-e{n_epoch}-loss{loss:.3f}-auc{auc:.3f}.pth\"\n        torch.save(\n            {\n                \"model_state_dict\": self.model.state_dict(),\n                \"optimizer_state_dict\": self.optimizer.state_dict(),\n                \"best_valid_score\": self.best_valid_auc,\n                \"n_epoch\": n_epoch,\n            },\n            self.lastmodel,\n        )\n    \n    @staticmethod\n    def info_message(message, *args, end=\"\\n\"):\n        print(message.format(*args), end=end)","metadata":{"id":"FbYXcOBmQpcC","executionInfo":{"status":"ok","timestamp":1634113509053,"user_tz":-330,"elapsed":456,"user":{"displayName":"vivek joshi","photoUrl":"https://lh3.googleusercontent.com/a/default-user=s64","userId":"14701461628611770995"}},"execution":{"iopub.status.busy":"2022-06-22T09:01:11.594619Z","iopub.execute_input":"2022-06-22T09:01:11.594984Z","iopub.status.idle":"2022-06-22T09:01:11.628893Z","shell.execute_reply.started":"2022-06-22T09:01:11.594951Z","shell.execute_reply":"2022-06-22T09:01:11.628049Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n\ndef train_mri_type(df_train, df_valid, mri_type):\n    if mri_type==\"all\":\n        train_list = []\n        valid_list = []\n        for mri_type in mri_types:\n          if mri_type=='FLAIR':\n            train_df = pd.read_csv(\"train_labels.csv\")\n            train_df = train_df.iloc[0:50]\n            df_train, df_valid = sk_model_selection.train_test_split(train_df, test_size=0.1, random_state=1, stratify=train_df[\"MGMT_value\"])\n        \n          if mri_type=='T1w':\n            train_df = pd.read_csv(\"train_labels.csv\")\n            train_df = pd.concat([train_df.iloc[0:50], train_df.iloc[200::]]).iloc[50:100]\n            df_train, df_valid = sk_model_selection.train_test_split(train_df, test_size=0.1, random_state=1, stratify=train_df[\"MGMT_value\"])\n          \n          if mri_type=='T1wCE':\n            train_df = pd.read_csv(\"train_labels.csv\")\n            train_df = pd.concat([train_df.iloc[0:250], train_df.iloc[300:350], train_df.iloc[400::]]).iloc[0:50]\n            df_train, df_valid = sk_model_selection.train_test_split(train_df, test_size=0.1, random_state=1, stratify=train_df[\"MGMT_value\"])\n\n          if mri_type=='T2w':\n            train_df = pd.read_csv(\"train_labels.csv\")\n            train_df = pd.concat([train_df.iloc[50:550]]).iloc[0:50]\n            df_train, df_valid = sk_model_selection.train_test_split(train_df, test_size=0.1, random_state=1, stratify=train_df[\"MGMT_value\"])\n          \n          df_train.loc[:,\"MRI_Type\"] = mri_type\n          train_list.append(df_train.copy())\n          df_valid.loc[:,\"MRI_Type\"] = mri_type\n          valid_list.append(df_valid.copy())\n\n        df_train = pd.concat(train_list)\n        df_valid = pd.concat(valid_list)\n    else:\n        df_train.loc[:,\"MRI_Type\"] = mri_type\n        df_valid.loc[:,\"MRI_Type\"] = mri_type\n\n    print(df_train.shape, df_valid.shape)\n    display(df_train.head())\n    \n    train_data_retriever = Dataset(\n        df_train[\"BraTS21ID\"].values, \n        df_train[\"MGMT_value\"].values, \n        df_train[\"MRI_Type\"].values,\n        augment=True\n    )\n\n    valid_data_retriever = Dataset(\n        df_valid[\"BraTS21ID\"].values, \n        df_valid[\"MGMT_value\"].values,\n        df_valid[\"MRI_Type\"].values\n    )\n\n    train_loader = torch_data.DataLoader(\n        train_data_retriever,\n        batch_size=batch_size,\n        shuffle=True,\n        num_workers=2,pin_memory = True,\n    )\n\n    valid_loader = torch_data.DataLoader(\n        valid_data_retriever, \n        batch_size=batch_size,\n        shuffle=False,\n        num_workers=2,pin_memory = True\n    )\n\n    model = Model()\n    model.to(device)\n\n    param_optimizer = list(model.named_parameters())\n    no_decay = ['bias', 'LayerNorm.bias', 'LayerNorm.weight']\n    optimizer_grouped_parameters = [\n            {'params': [p for n, p in param_optimizer if not any(nd in n for nd in no_decay)], 'weight_decay': 0.001},\n            {'params': [p for n, p in param_optimizer if any(nd in n for nd in no_decay)], 'weight_decay': 0.0}\n        ]\n    optimizer = torch.optim.Adam(optimizer_grouped_parameters, lr=lr)\n\n    criterion = torch_functional.binary_cross_entropy_with_logits\n    scheduler = torch.optim.lr_scheduler.ReduceLROnPlateau(optimizer, mode='max', factor=0.5, patience=5, \n                                                           threshold=0.0001, threshold_mode='abs', cooldown=0, \n                                                           min_lr=0, eps=1e-08, verbose=False)\n    trainer = Trainer(\n        model, \n        device, \n        optimizer, \n        criterion,\n        scheduler\n    )\n\n    history = trainer.fit( \n        1,  \n        train_loader, \n        valid_loader, \n        f\"./{mri_type}\",  \n        1,\n    )\n    \n    return trainer.lastmodel\n","metadata":{"id":"Z1tb5GIbnwo_","executionInfo":{"status":"ok","timestamp":1634113509055,"user_tz":-330,"elapsed":27,"user":{"displayName":"vivek joshi","photoUrl":"https://lh3.googleusercontent.com/a/default-user=s64","userId":"14701461628611770995"}},"execution":{"iopub.status.busy":"2022-06-22T09:10:22.351960Z","iopub.execute_input":"2022-06-22T09:10:22.352462Z","iopub.status.idle":"2022-06-22T09:10:22.386444Z","shell.execute_reply.started":"2022-06-22T09:10:22.352414Z","shell.execute_reply":"2022-06-22T09:10:22.385678Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_mri_type(df_train, df_valid, mri_type)","metadata":{"id":"yn-YjyoCo47w","outputId":"0b72b341-b1cb-418e-dd51-661f137ba763","execution":{"iopub.status.busy":"2022-06-22T09:10:23.031514Z","iopub.execute_input":"2022-06-22T09:10:23.031852Z","iopub.status.idle":"2022-06-22T09:10:46.030341Z","shell.execute_reply.started":"2022-06-22T09:10:23.031822Z","shell.execute_reply":"2022-06-22T09:10:46.029323Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}