{"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":"markdown","source":"# Baseline 3D classification\n\nThe model used for training is taken from the `classification_models_3D` [repo](https://github.com/ZFTurbo/classification_models_3D).\n\n#### 📔 [Data preparation notebook](https://www.kaggle.com/code/vmuzhichenko/rsna-22-dicom-to-numpy-3d)\n#### 📔 [Trainnig notebook](https://www.kaggle.com/code/vmuzhichenko/rsna-22-resnet-50-3d-train)\n#### 📔 [Inference notebook](https://www.kaggle.com/code/vmuzhichenko/rsna-22-resnet-50-3d-inference)","metadata":{}},{"cell_type":"code","source":"import os\nimport glob\nimport random\nimport collections\nimport gc\nimport math\n\nimport numpy as np\nimport pandas as pd\n\nimport plotly.offline as py\nimport plotly.graph_objs as go\n\nimport pydicom\nimport matplotlib.pyplot as plt\nimport cv2\nimport scipy\nimport tensorflow as tf\nimport tensorflow_addons as tfa\nfrom tensorflow.keras import backend as K\nfrom tensorflow import keras\nfrom tensorflow.keras import layers as L\n\nfrom sklearn.model_selection import KFold, StratifiedKFold\n\n","metadata":{"execution":{"iopub.status.busy":"2022-08-18T19:33:30.422589Z","iopub.execute_input":"2022-08-18T19:33:30.423047Z","iopub.status.idle":"2022-08-18T19:33:37.555189Z","shell.execute_reply.started":"2022-08-18T19:33:30.422963Z","shell.execute_reply":"2022-08-18T19:33:37.554015Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#set desired image size and depth (number of patient's images to load)\nclass Config:\n    img_size = 256\n    depth = 128\n    train_one_fold = True\n    epochs = 15\n\n\nIMG_PATH_TRAIN = '../input/rsna-22-dicom-to-numpy-3d/train_arrays/'\n\nTRAIN_CSV_PATH = '../input/rsna-2022-cervical-spine-fracture-detection/train.csv'\nTEST_CSV_PATH = '../input/rsna-2022-cervical-spine-fracture-detection/test.csv'\n\ntrain_images = os.listdir(IMG_PATH_TRAIN)\n\ntrain=pd.read_csv(TRAIN_CSV_PATH)\ntest=pd.read_csv(TEST_CSV_PATH)","metadata":{"execution":{"iopub.status.busy":"2022-08-18T19:33:37.560917Z","iopub.execute_input":"2022-08-18T19:33:37.563507Z","iopub.status.idle":"2022-08-18T19:33:37.831724Z","shell.execute_reply.started":"2022-08-18T19:33:37.563449Z","shell.execute_reply":"2022-08-18T19:33:37.830789Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train['numpy_path'] = train['StudyInstanceUID'].apply(lambda x: f'{IMG_PATH_TRAIN}{x}.npy')","metadata":{"execution":{"iopub.status.busy":"2022-08-18T19:33:37.836282Z","iopub.execute_input":"2022-08-18T19:33:37.838583Z","iopub.status.idle":"2022-08-18T19:33:37.855521Z","shell.execute_reply.started":"2022-08-18T19:33:37.83855Z","shell.execute_reply":"2022-08-18T19:33:37.854665Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class SampleGenerator(tf.keras.utils.Sequence):\n    def __init__(self, df: pd.DataFrame, batch_size, resample_rate: float = None, steps_per_epoch: int = 10000, is_train=True, shuffle=True):\n        self.is_train      = is_train\n        self.numpy_path  = df.numpy_path\n        self.df  = df\n        self.batch_size = batch_size\n        self.length = len(df)\n        self.resample = resample_rate\n        self.shuffle = shuffle\n        self.steps_per_epoch= steps_per_epoch\n        \n    def __len__(self):\n        return  min(int(np.ceil(self.length / float(self.batch_size))), self.steps_per_epoch)\n    \n    def on_epoch_end(self):\n        if self.shuffle:\n            self.df = self.df.sample(frac=1).reset_index(drop=True)\n            self.numpy_path  = self.df.numpy_path\n    \n    def __getitem__(self, index):\n                  \n        if self.is_train:         \n            \n            batch_x = []\n            batch_y = []\n            \n            targets = self.df[['patient_overall', 'C1', 'C2', 'C3', 'C4', 'C5', 'C6', 'C7']]\n            #targets = self.df[['C1', 'C2', 'C3', 'C4', 'C5', 'C6', 'C7']]\n            \n            for i in range(self.batch_size):\n                cur_ind = self.batch_size*index + i\n                if cur_ind < self.length:\n                    batch_x.append(np.load(self.numpy_path.iloc[cur_ind]))\n                    batch_y.append(targets.iloc[cur_ind])\n              \n            if self.resample is not None:\n                n_images = batch_x[0].shape[0]\n                im_ids = sorted(np.random.choice(list(range(n_images)), int(n_images * self.resample), replace=False))\n                batch_x = np.array(batch_x)[:,im_ids]\n                   \n            #return np.array(batch_x), np.expand_dims(np.array(batch_y), -1).astype(np.float32)\n            return np.array(batch_x), np.array(batch_y).astype(np.float32)\n\n        else:\n            batch_x = []\n            for i in range(self.batch_size):\n                cur_ind = self.batch_size*index + i\n                if cur_ind < self.length:\n                    batch_x.append(np.load(self.numpy_path.iloc[cur_ind]))\n            \n            return np.array(batch_x)\n\n","metadata":{"execution":{"iopub.status.busy":"2022-08-18T19:33:37.861046Z","iopub.execute_input":"2022-08-18T19:33:37.863178Z","iopub.status.idle":"2022-08-18T19:33:37.883379Z","shell.execute_reply.started":"2022-08-18T19:33:37.863143Z","shell.execute_reply":"2022-08-18T19:33:37.88238Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"!pip install classification-models-3D\n!pip install keras_applications","metadata":{"execution":{"iopub.status.busy":"2022-08-18T19:33:37.889063Z","iopub.execute_input":"2022-08-18T19:33:37.892048Z","iopub.status.idle":"2022-08-18T19:34:01.9542Z","shell.execute_reply.started":"2022-08-18T19:33:37.892012Z","shell.execute_reply":"2022-08-18T19:34:01.952995Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#https://www.kaggle.com/competitions/rsna-2022-cervical-spine-fracture-detection/discussion/341854#1884562\ndef competiton_loss(y_true, y_pred):\n\n    competition_weights = {\n        '-' : tf.constant([1, 1, 1, 1, 1, 1, 1], dtype=tf.float32),\n        '+' : tf.constant([2, 2, 2, 2, 2, 2, 2], dtype=tf.float32)\n    }\n    \n    loss = tf.keras.losses.BinaryCrossentropy(reduction=tf.keras.losses.Reduction.NONE)(tf.expand_dims(y_true, -1),tf.expand_dims(y_pred,-1))\n    weights  = y_true*competition_weights['+'] + (1-y_true)*competition_weights['-'] \n    \n    loss = tf.reduce_mean(tf.reduce_sum(loss * weights, axis=1)) / tf.reduce_sum(weights)\n    return loss\n    \n    ","metadata":{"execution":{"iopub.status.busy":"2022-08-18T19:34:01.95607Z","iopub.execute_input":"2022-08-18T19:34:01.956774Z","iopub.status.idle":"2022-08-18T19:34:01.964767Z","shell.execute_reply.started":"2022-08-18T19:34:01.956731Z","shell.execute_reply":"2022-08-18T19:34:01.963651Z"},"_kg_hide-input":true,"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#https://www.kaggle.com/competitions/rsna-2022-cervical-spine-fracture-detection/discussion/341854#1884562\ndef competiton_loss(y_true, y_pred):\n\n    competition_weights = {\n        '-' : tf.constant([7, 1, 1, 1, 1, 1, 1, 1], dtype=tf.float32),\n        '+' : tf.constant([14, 2, 2, 2, 2, 2, 2, 2], dtype=tf.float32)\n    }\n    \n    loss = tf.keras.losses.BinaryCrossentropy(reduction=tf.keras.losses.Reduction.NONE)(tf.expand_dims(y_true, -1),tf.expand_dims(y_pred,-1))\n    weights  = y_true*competition_weights['+'] + (1-y_true)*competition_weights['-'] \n    \n    loss = tf.reduce_mean(tf.reduce_sum(loss * weights, axis=1)) / tf.reduce_sum(weights)\n    return loss\n    \n    ","metadata":{"execution":{"iopub.status.busy":"2022-08-18T19:34:01.966406Z","iopub.execute_input":"2022-08-18T19:34:01.967137Z","iopub.status.idle":"2022-08-18T19:34:01.97868Z","shell.execute_reply.started":"2022-08-18T19:34:01.967102Z","shell.execute_reply":"2022-08-18T19:34:01.977711Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from classification_models_3D.tfkeras import Classifiers\n\nmodel_arch = 'resnet50' \n#model_arch = 'resnet34'\n\ndef concat_max_avg(tensor):\n    _avr = L.GlobalAveragePooling3D()(tensor)\n    _max = L.GlobalMaxPooling3D()(tensor)\n    return L.Concatenate()([_avr, _max])\n\ndef create_model(input_shape, num_classes):\n    inputs = L.Input((*input_shape, 1), name='inputs')\n    x = L.Rescaling(1/255.)(inputs)\n    x = L.Conv3D(3, (1, 1, 1), padding='same')(x)\n    \n    net, preprocess_input = Classifiers.get(model_arch)\n    #x = preprocess_input(x)\n    x = net(input_shape=(*input_shape, 3), include_top=False, weights='imagenet')(x)\n    \n    x = concat_max_avg(x)\n    x = L.Dropout(rate=0.1)(x)\n    x = L.Dense(512, activation='relu')(x)\n    x = L.Dropout(rate=0.1)(x)\n    x = L.Dense(128, activation='relu')(x)\n    x = L.Dropout(rate=0.1)(x)\n    x = L.Dense(32, activation='relu')(x)\n    \n    outputs = L.Dense(num_classes, activation='sigmoid')(x)\n    model  = tf.keras.Model(inputs, outputs)\n   \n    model.compile(loss= competiton_loss, #'binary_crossentropy',\n                      optimizer=keras.optimizers.Adam(learning_rate=1e-3),\n                       metrics=['AUC',tfa.metrics.F1Score(num_classes=num_classes, threshold=0.25, average='macro')])\n    return model\n\ncreate_model((None, Config.img_size, Config.img_size), num_classes = 7).summary()","metadata":{"execution":{"iopub.status.busy":"2022-08-18T19:34:01.981375Z","iopub.execute_input":"2022-08-18T19:34:01.982738Z","iopub.status.idle":"2022-08-18T19:35:02.849263Z","shell.execute_reply.started":"2022-08-18T19:34:01.98271Z","shell.execute_reply":"2022-08-18T19:35:02.848212Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"LR_MAX = 1e-3\nLR_DECAY = 0.75\n\ndef lr_func(epoch):   \n    lr = np.pi/2 * LR_DECAY**epoch \n    lr = np.sin(lr) * LR_MAX\n    return lr\n    \nlr_list = [lr_func(x) for x in list(range(Config.epochs))]\nplt.scatter(range(Config.epochs), lr_list)\nplt.show()\n\nLR = tf.keras.callbacks.LearningRateScheduler(lr_func, verbose = True)","metadata":{"execution":{"iopub.status.busy":"2022-08-18T19:35:18.508897Z","iopub.execute_input":"2022-08-18T19:35:18.509949Z","iopub.status.idle":"2022-08-18T19:35:18.726295Z","shell.execute_reply.started":"2022-08-18T19:35:18.509912Z","shell.execute_reply":"2022-08-18T19:35:18.725358Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"n_folds = 5\npred_columns = ['patient_overall_pred', 'C1_pred', 'C2_pred', 'C3_pred', 'C4_pred', 'C5_pred', 'C6_pred', 'C7_pred']\n#pred_columns = ['C1_pred', 'C2_pred', 'C3_pred', 'C4_pred', 'C5_pred', 'C6_pred', 'C7_pred']\ntrain[pred_columns] = 0\n\nestop = tf.keras.callbacks.EarlyStopping(monitor='val_loss', patience=5, verbose=0, mode='min',restore_best_weights=True)\nkf_split = StratifiedKFold(n_splits=n_folds, shuffle=True, random_state=0)\n\nfor fold,(tr_idx, val_idx) in enumerate(kf_split.split(train, train.patient_overall)):\n    \n    X_train = train.iloc[tr_idx]\n    X_test = train.iloc[val_idx]\n    \n    train_data = SampleGenerator(X_train, 4, steps_per_epoch = 250, resample_rate = 0.8, shuffle = True)\n    test_data = SampleGenerator(X_test, 2, resample_rate = 0.8, shuffle = False)\n    \n    path_to_checkpoints = f\"{model_arch}_best_fold_{fold+1}.hdf5\"\n    checkpointer = tf.keras.callbacks.ModelCheckpoint(filepath=path_to_checkpoints, monitor=\"val_loss\", mode='min', verbose=1, save_best_only=True)\n\n    #model = get_model()\n    model = create_model((None, Config.img_size, Config.img_size), num_classes = 8)\n\n    print(f'Training Model Fold {fold+1}...')\n    \n    history = model.fit(\n        train_data,\n        epochs = Config.epochs,\n        callbacks = [estop, LR, checkpointer],\n        validation_data = (test_data),\n    )   \n    \n    del X_train, train_data\n    \n    model.save(f\"{model_arch}_end_fold_{fold+1}\")\n    \n    test_preds = model.predict(test_data)\n    train.loc[val_idx, pred_columns] = test_preds\n    \n    del X_test,test_preds, test_data\n    K.clear_session()\n    gc.collect()\n    \n    if Config.train_one_fold: break","metadata":{"execution":{"iopub.status.busy":"2022-08-18T19:37:15.01976Z","iopub.execute_input":"2022-08-18T19:37:15.020741Z","iopub.status.idle":"2022-08-18T19:43:31.673328Z","shell.execute_reply.started":"2022-08-18T19:37:15.020705Z","shell.execute_reply":"2022-08-18T19:43:31.670125Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#model(test_data.__getitem__(1)[0])","metadata":{"execution":{"iopub.status.busy":"2022-08-18T19:43:46.83255Z","iopub.execute_input":"2022-08-18T19:43:46.833193Z","iopub.status.idle":"2022-08-18T19:43:47.753381Z","shell.execute_reply.started":"2022-08-18T19:43:46.833157Z","shell.execute_reply":"2022-08-18T19:43:47.752438Z"},"trusted":true},"execution_count":null,"outputs":[]}]}