{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"nvidiaTeslaT4","dataSources":[{"sourceId":18613,"sourceType":"datasetVersion","datasetId":5839},{"sourceId":7487364,"sourceType":"datasetVersion","datasetId":4359072},{"sourceId":8631471,"sourceType":"datasetVersion","datasetId":5168271},{"sourceId":8631480,"sourceType":"datasetVersion","datasetId":5168276},{"sourceId":158958765,"sourceType":"kernelVersion"}],"dockerImageVersionId":30646,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"## Import Libraries\n___","metadata":{}},{"cell_type":"code","source":"import gc \nimport ctypes\nimport random\nfrom tqdm import tqdm\nfrom collections import defaultdict\n\nimport os\nimport sys\nfrom tqdm import tqdm\nimport tensorflow as tf\n\nos.environ['CUDA_IS_VISIBLE'] = '0,1'\nprint('tensorflow version: ', tf.__version__)\n\nimport numpy as np, pandas as pd\nimport matplotlib.pyplot as plt\nimport seaborn as sns\n\nfrom glob import glob\nimport cv2\nfrom PIL import Image\n\nfrom sklearn.metrics import confusion_matrix, ConfusionMatrixDisplay\nfrom sklearn.metrics import cohen_kappa_score, accuracy_score, f1_score\nfrom sklearn.model_selection import KFold, StratifiedKFold,GroupKFold\nimport tensorflow.keras.backend as K, gc\n\n\nimport warnings \nimport albumentations as albu\nwarnings.filterwarnings('ignore')\n\ngpus = tf.config.list_physical_devices('GPU')\nif len(gpus) <=1:\n    strategy = tf.distribute.OneDeviceStrategy(device='/gpu:0')\n    print(f'Using {len(gpus)} GPUs')\nelse: \n    strategy = tf.distribute.MirroredStrategy()\n    print(f'Using {len(gpus)} GPUs')\n    \nVER = 1\nLOAD_MODELS_FROM = '/kaggle/input/nih-eff/'","metadata":{"execution":{"iopub.status.busy":"2024-06-08T00:55:59.653560Z","iopub.execute_input":"2024-06-08T00:55:59.653978Z","iopub.status.idle":"2024-06-08T00:56:16.864195Z","shell.execute_reply.started":"2024-06-08T00:55:59.653948Z","shell.execute_reply":"2024-06-08T00:56:16.862993Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def clean_memory():\n    # malloc_trim: 현재 사용되지 않는 메모리를 시스템에서 다시 반환함0\n    ctypes.CDLL('libc.so.6').malloc_trim(0)\n    gc.collect()\nclean_memory()","metadata":{"execution":{"iopub.status.busy":"2024-06-08T00:56:16.866439Z","iopub.execute_input":"2024-06-08T00:56:16.867138Z","iopub.status.idle":"2024-06-08T00:56:17.074775Z","shell.execute_reply.started":"2024-06-08T00:56:16.867084Z","shell.execute_reply":"2024-06-08T00:56:17.073758Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def seed_everything(seed):\n    random.seed(seed)\n    np.random.seed(seed)\n    os.environ['PYTHONHASHSEED'] = str(seed)\n    tf.random.set_seed(seed)\nseed_everything(42)","metadata":{"execution":{"iopub.status.busy":"2024-06-08T00:56:17.076044Z","iopub.execute_input":"2024-06-08T00:56:17.076412Z","iopub.status.idle":"2024-06-08T00:56:17.091683Z","shell.execute_reply.started":"2024-06-08T00:56:17.076387Z","shell.execute_reply":"2024-06-08T00:56:17.090838Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"MIX = True\nif MIX: \n    tf.config.optimizer.set_experimental_options({'auto_mixed_precision':True})\n    print('Mixed Precision enabled')\nelse: \n    print('Using full precision')","metadata":{"execution":{"iopub.status.busy":"2024-06-08T00:56:17.094269Z","iopub.execute_input":"2024-06-08T00:56:17.095185Z","iopub.status.idle":"2024-06-08T00:56:17.102658Z","shell.execute_reply.started":"2024-06-08T00:56:17.095153Z","shell.execute_reply":"2024-06-08T00:56:17.101763Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Road and Read Data\n___","metadata":{}},{"cell_type":"code","source":"df = pd.read_csv('/kaggle/input/data/Data_Entry_2017.csv')\ndf.drop(columns=['Unnamed: 11'],inplace=True)\nprint(f'Shape of DataFrame: {df.shape}')\ndf.head()","metadata":{"execution":{"iopub.status.busy":"2024-06-08T00:56:17.103733Z","iopub.execute_input":"2024-06-08T00:56:17.103983Z","iopub.status.idle":"2024-06-08T00:56:17.448922Z","shell.execute_reply.started":"2024-06-08T00:56:17.103962Z","shell.execute_reply":"2024-06-08T00:56:17.447990Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**폐 구조 및 기능 이상**\n- Empysema\n- Fibrosis\n- Atelectasis\n\n**폐 감염 및 염증**\n- Pneumonia\n- Consolidation\n- Infiltration\n\n**종양 및 종양 유사 병변**\n- Nodule\n- Mass\n\n**흉막 관련 질환**\n- Effusion\n- Pleural_Thickening\n- Pneumothorax\n\n**심장 관련 질환**\n- Cardiomegaly\n- Edema","metadata":{}},{"cell_type":"code","source":"df = df[df['Finding Labels'] != 'Hernia']","metadata":{"execution":{"iopub.status.busy":"2024-06-08T00:56:17.450345Z","iopub.execute_input":"2024-06-08T00:56:17.450703Z","iopub.status.idle":"2024-06-08T00:56:17.483898Z","shell.execute_reply.started":"2024-06-08T00:56:17.450667Z","shell.execute_reply":"2024-06-08T00:56:17.483157Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**View Position=PA & |Width - Height| <=500**","metadata":{}},{"cell_type":"code","source":"df = df[abs(df['OriginalImage[Width'] - df['Height]']) <= 500]\ndf = df[df['View Position'] == 'PA']","metadata":{"execution":{"iopub.status.busy":"2024-06-08T00:56:17.485027Z","iopub.execute_input":"2024-06-08T00:56:17.485631Z","iopub.status.idle":"2024-06-08T00:56:17.522237Z","shell.execute_reply.started":"2024-06-08T00:56:17.485604Z","shell.execute_reply":"2024-06-08T00:56:17.521521Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**Remove Outlier**\n___","metadata":{}},{"cell_type":"code","source":"print(df['Patient Age'].sort_values(ascending=False).head(20))\ndf = df[df['Patient Age'] < 100]","metadata":{"execution":{"iopub.status.busy":"2024-06-08T00:56:17.523255Z","iopub.execute_input":"2024-06-08T00:56:17.523551Z","iopub.status.idle":"2024-06-08T00:56:17.540028Z","shell.execute_reply.started":"2024-06-08T00:56:17.523523Z","shell.execute_reply":"2024-06-08T00:56:17.539164Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**Make Path**\n___","metadata":{}},{"cell_type":"code","source":"%%time\ntmp = {os.path.basename(x): x for x in glob(os.path.join('/kaggle', 'input', 'data','images*','images', '*.png'))}    \n    \ndf['path'] = df['Image Index'].map(tmp)","metadata":{"execution":{"iopub.status.busy":"2024-06-08T00:56:17.541525Z","iopub.execute_input":"2024-06-08T00:56:17.542145Z","iopub.status.idle":"2024-06-08T00:56:21.870235Z","shell.execute_reply.started":"2024-06-08T00:56:17.542095Z","shell.execute_reply":"2024-06-08T00:56:21.869271Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df = df[~df['Finding Labels'].str.contains('\\|')]\n\nnormal_df = df[df['Finding Labels'] == 'No Finding'].sample(1_000, random_state=42)\n\nother_df = df[df['Finding Labels'] != 'No Finding']\n\ndf = pd.concat([normal_df, other_df])","metadata":{"execution":{"iopub.status.busy":"2024-06-08T00:56:21.873594Z","iopub.execute_input":"2024-06-08T00:56:21.873889Z","iopub.status.idle":"2024-06-08T00:56:21.946639Z","shell.execute_reply.started":"2024-06-08T00:56:21.873865Z","shell.execute_reply":"2024-06-08T00:56:21.945874Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df = df[df['Finding Labels'].isin(['Fibrosis','Emphysema','No Finding'])]","metadata":{"execution":{"iopub.status.busy":"2024-06-08T00:56:21.947666Z","iopub.execute_input":"2024-06-08T00:56:21.947943Z","iopub.status.idle":"2024-06-08T00:56:21.959475Z","shell.execute_reply.started":"2024-06-08T00:56:21.947921Z","shell.execute_reply":"2024-06-08T00:56:21.958587Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"labels = df['Finding Labels'].unique()\nlabel_counts = df['Finding Labels'].value_counts()\n\nplt.style.use('Solarize_Light2')\n\nplt.figure(figsize=(12,6))\nplt.subplot(1,2,1)\nplt.pie(df['Finding Labels'].value_counts(), labels=labels, autopct='%.1f%%')\nplt.legend()\nplt.subplot(1,2,2)\nsns.countplot(x=df['Finding Labels'], order= label_counts.index)\nplt.xticks(rotation=90)\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2024-06-08T00:56:21.960772Z","iopub.execute_input":"2024-06-08T00:56:21.961150Z","iopub.status.idle":"2024-06-08T00:56:22.291057Z","shell.execute_reply.started":"2024-06-08T00:56:21.961084Z","shell.execute_reply":"2024-06-08T00:56:22.290164Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**Label Smoothing**","metadata":{}},{"cell_type":"code","source":"for col in df['Finding Labels'].unique():\n    df[col] = np.where(df['Finding Labels'].str.contains(col), 0.9, 0.05)\nTARGET = df.columns[-3:].tolist()","metadata":{"execution":{"iopub.status.busy":"2024-06-08T00:56:22.292445Z","iopub.execute_input":"2024-06-08T00:56:22.292744Z","iopub.status.idle":"2024-06-08T00:56:22.304404Z","shell.execute_reply.started":"2024-06-08T00:56:22.292719Z","shell.execute_reply":"2024-06-08T00:56:22.303392Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Gan(Generative Adversial Network)\n___\nGANS(Generative Adversial Network) have seen a lot of advancements and research over the recent years. GANS are Generative networks that learn to generate believalbe images throughout their training and are then used to generate almost real data instances\n\n\n**Architecture**\n___\n![](data:image/png;base64,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)","metadata":{}},{"cell_type":"code","source":"def load_images(data):\n    \n    imgs = []\n    target = 1\n    labels = []\n    for i in tqdm(range(data.shape[0])):\n        path = data.iloc[i]['path']\n        img = Image.open(path).convert('L')\n        img = img.resize((256,256), Image.Resampling.LANCZOS)\n        img = np.array(img)\n        \n        clahe = cv2.createCLAHE(clipLimit=8.0, tileGridSize=(8, 8))\n        img = clahe.apply(img)\n        \n        # Standardization\n        ep = 1e-7\n        m = np.nanmean(img)\n        s = np.nanstd(img)\n        img = (img-m)/(s+ep) # Z-score\n        img = np.nan_to_num(img, nan=0.0)\n        \n        imgs.append(img)\n        labels.append(1)\n     \n    imgs = np.array(imgs)\n    labels = np.array(labels)\n    \n    return imgs, labels","metadata":{"execution":{"iopub.status.busy":"2024-06-08T00:56:22.305728Z","iopub.execute_input":"2024-06-08T00:56:22.306116Z","iopub.status.idle":"2024-06-08T00:56:22.315440Z","shell.execute_reply.started":"2024-06-08T00:56:22.306065Z","shell.execute_reply":"2024-06-08T00:56:22.314534Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"%%time\nLOAD_GANS_FROM = '/kaggle/input/nih-gan/'\ndata = df[df['Finding Labels'] != 'No Finding']\n\n\nif LOAD_GANS_FROM is None:\n    data, _ = load_images(data)\n    data = data.reshape(-1,256,256,1) # Adding Channel","metadata":{"execution":{"iopub.status.busy":"2024-06-08T00:56:22.316571Z","iopub.execute_input":"2024-06-08T00:56:22.316847Z","iopub.status.idle":"2024-06-08T00:56:22.327901Z","shell.execute_reply.started":"2024-06-08T00:56:22.316824Z","shell.execute_reply":"2024-06-08T00:56:22.326930Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**Generator Architecture**","metadata":{}},{"cell_type":"code","source":"def build_generator():\n    \"\"\"\n       Input: Random Noise / Latent Vector\n       Output: Generated Image (128x128)\n    \"\"\"\n    \n    ## Conv2D: Feature Map, DownSampling\n    ## Conv2DTranspose: Generate Image, Up Sampling \n    \n    model = tf.keras.Sequential([\n\n        tf.keras.layers.Dense(32*32*256, input_dim=100),\n        tf.keras.layers.LeakyReLU(alpha=0.2),\n        tf.keras.layers.Reshape((32,32,256)),\n        \n        tf.keras.layers.Conv2DTranspose(128, (4, 4), strides=2, padding='same'), # 64X64X128\n        tf.keras.layers.LeakyReLU(alpha=0.2),\n\n        tf.keras.layers.Conv2DTranspose(128, (4, 4), strides=2, padding='same'), # 128X128X128\n        tf.keras.layers.LeakyReLU(alpha=0.2),\n\n        tf.keras.layers.Conv2DTranspose(128, (4, 4), strides=2, padding='same'), # 256X256X128\n        tf.keras.layers.LeakyReLU(alpha=0.2),\n        \n        tf.keras.layers.Conv2D(1, (4, 4), padding='same', activation='tanh') # 256X256X1\n\n    ],\n    name=\"generator\")\n    \n    return model","metadata":{"execution":{"iopub.status.busy":"2024-06-08T00:56:22.329286Z","iopub.execute_input":"2024-06-08T00:56:22.329949Z","iopub.status.idle":"2024-06-08T00:56:22.339156Z","shell.execute_reply.started":"2024-06-08T00:56:22.329913Z","shell.execute_reply":"2024-06-08T00:56:22.338295Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"g_model = build_generator()","metadata":{"execution":{"iopub.status.busy":"2024-06-08T00:56:22.340369Z","iopub.execute_input":"2024-06-08T00:56:22.340684Z","iopub.status.idle":"2024-06-08T00:56:22.525669Z","shell.execute_reply.started":"2024-06-08T00:56:22.340655Z","shell.execute_reply":"2024-06-08T00:56:22.524813Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**Discriminator Architecture**","metadata":{}},{"cell_type":"code","source":"def build_discriminator():\n    \"\"\"\n       Input: Generated Image/ Real Image\n       Output: Validity of Image (Fake or Real)\n    \"\"\"\n    \n    model = tf.keras.Sequential([\n        tf.keras.layers.Conv2D(64, (3, 3), padding='same', input_shape=(256,256,1)),\n        tf.keras.layers.LeakyReLU(alpha=0.2),\n        tf.keras.layers.Dropout(0.1),\n\n        tf.keras.layers.Conv2D(128, (3, 3), strides=2, padding='same'), # 128X128X128\n        tf.keras.layers.LeakyReLU(alpha=0.2),\n        tf.keras.layers.Dropout(0.1),\n\n        tf.keras.layers.Conv2D(128, (3, 3), strides=2, padding='same'), # 64X64X128\n        tf.keras.layers.LeakyReLU(alpha=0.2),\n        tf.keras.layers.Dropout(0.1),\n        \n        tf.keras.layers.Conv2D(256, (3, 3), strides=2, padding='same'), # 32X32X256\n        tf.keras.layers.LeakyReLU(alpha=0.2),\n        tf.keras.layers.Dropout(0.1),\n        \n        tf.keras.layers.Flatten(),\n        tf.keras.layers.Dropout(0.1),\n        tf.keras.layers.Dense(1, activation=\"sigmoid\")\n        \n    ], name='discriminator')\n    \n    loss = tf.keras.losses.BinaryCrossentropy()\n    opt = tf.keras.optimizers.Adam(0.0005, 0.5)\n    model.compile(loss=loss, optimizer=opt)\n    \n    return model","metadata":{"execution":{"iopub.status.busy":"2024-06-08T00:56:22.526859Z","iopub.execute_input":"2024-06-08T00:56:22.527192Z","iopub.status.idle":"2024-06-08T00:56:22.537884Z","shell.execute_reply.started":"2024-06-08T00:56:22.527165Z","shell.execute_reply":"2024-06-08T00:56:22.536843Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"d_model = build_discriminator()","metadata":{"execution":{"iopub.status.busy":"2024-06-08T00:56:22.539087Z","iopub.execute_input":"2024-06-08T00:56:22.539469Z","iopub.status.idle":"2024-06-08T00:56:22.675660Z","shell.execute_reply.started":"2024-06-08T00:56:22.539438Z","shell.execute_reply":"2024-06-08T00:56:22.674728Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def build_gan(generator, discriminator):\n    discriminator.trainable = False  # Discriminator를 GAN 학습 시에는 고정\n    gan_input = tf.keras.Input(shape=(100,))\n    fake_image = generator(gan_input)\n    gan_output = discriminator(fake_image)\n    gan = tf.keras.Model(inputs=gan_input, outputs=gan_output, name='gan_model')\n    \n    loss = tf.keras.losses.BinaryCrossentropy()\n    opt = tf.keras.optimizers.Adam(0.001, 0.5)\n    gan.compile(loss=loss, optimizer=opt)\n    \n    return gan","metadata":{"execution":{"iopub.status.busy":"2024-06-08T00:56:22.676831Z","iopub.execute_input":"2024-06-08T00:56:22.677121Z","iopub.status.idle":"2024-06-08T00:56:22.683972Z","shell.execute_reply.started":"2024-06-08T00:56:22.677084Z","shell.execute_reply":"2024-06-08T00:56:22.683039Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"gan = build_gan(g_model, d_model)","metadata":{"execution":{"iopub.status.busy":"2024-06-08T00:56:22.685290Z","iopub.execute_input":"2024-06-08T00:56:22.685592Z","iopub.status.idle":"2024-06-08T00:56:22.778782Z","shell.execute_reply.started":"2024-06-08T00:56:22.685570Z","shell.execute_reply":"2024-06-08T00:56:22.777798Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def sample_images(noise, subplots):\n    generated_images = g_model.predict(noise)\n    plt.figure(figsize=(12,6))\n    \n    for i, image in enumerate(generated_images):\n        plt.subplot(subplots[0], subplots[1], i+1)\n        plt.imshow(image[:,:,0], cmap='bone')\n    \n    plt.tight_layout()\n    plt.show()","metadata":{"execution":{"iopub.status.busy":"2024-06-08T00:56:22.780907Z","iopub.execute_input":"2024-06-08T00:56:22.781599Z","iopub.status.idle":"2024-06-08T00:56:22.788514Z","shell.execute_reply.started":"2024-06-08T00:56:22.781549Z","shell.execute_reply":"2024-06-08T00:56:22.787340Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"if LOAD_GANS_FROM is None:\n    batch_size = 16\n    all_history = defaultdict(list)\n\n\n    for epoch in range(5):\n        print('#'*25)\n        print(f'### Epoch: {epoch+1}')\n        print('#'*25)\n    \n        for batch in tqdm(range(data.shape[0]//batch_size)):\n            noise = np.random.normal(0, 1,size=(batch_size,100)) \n            fake_X = g_model.predict(noise) \n        \n            idx = np.random.randint(0, data.shape[0], size=batch_size)\n            real_X = data[idx] \n        \n            real_discriminator_y = np.ones(batch_size) \n            real_discriminator_y[:] = 0.9 # Real Image # Label Smoothing \n            fake_discriminator_y = np.zeros(batch_size)\n        \n            d_model.trainable = True\n            d_real = d_model.train_on_batch(real_X, real_discriminator_y)\n            d_fake = d_model.train_on_batch(fake_X, fake_discriminator_y)\n            d_loss = d_real + d_fake \n            \n            y_gen = np.ones(batch_size)\n            d_model.trainable = False\n            g_loss = gan.train_on_batch(noise, y_gen)\n        \n            all_history['g_loss'].append(g_loss)\n            all_history['d_loss'].append(d_loss)\n        \n            print(f\"EPOCH: {epoch + 1} D_Loss: {d_loss:.4f}  G_Loss: {g_loss:.4f}\")\n        \n            if batch == (data.shape[0]//batch_size - 1):\n                print(f'CXR Image in Epoch{epoch+1}')\n                noise = np.random.normal(size=(4, 100))\n                sample_images(noise, (1,4))    \n\n            \n    g_model.save_weights(f'GAN_v{VER}.weights.h5')        \n    \nelse:\n    g_model.load_weights(f'{LOAD_GANS_FROM}GAN_v{VER}.weights.h5')","metadata":{"execution":{"iopub.status.busy":"2024-06-08T00:56:22.790214Z","iopub.execute_input":"2024-06-08T00:56:22.790602Z","iopub.status.idle":"2024-06-08T00:56:24.253995Z","shell.execute_reply.started":"2024-06-08T00:56:22.790546Z","shell.execute_reply":"2024-06-08T00:56:24.252989Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"if LOAD_GANS_FROM is None: \n    plt.figure(figsize=(12,6))\n    plt.subplot(1,2,1)\n    plt.title('generator_loss')\n    plt.plot(all_history['g_loss'])\n    plt.xlabel('Steps')\n    plt.ylabel('Loss')\n    plt.subplot(1,2,2)\n    plt.title('discriminator_loss')\n    plt.plot(all_history['d_loss'])\n    plt.xlabel('Steps')\n    plt.ylabel('Loss')\n    plt.tight_layout()\n    plt.show()","metadata":{"execution":{"iopub.status.busy":"2024-06-08T00:56:24.255285Z","iopub.execute_input":"2024-06-08T00:56:24.255624Z","iopub.status.idle":"2024-06-08T00:56:24.262514Z","shell.execute_reply.started":"2024-06-08T00:56:24.255588Z","shell.execute_reply":"2024-06-08T00:56:24.261506Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"#### Let's Make CXR Dataset with GAN Model","metadata":{}},{"cell_type":"code","source":"noise = np.random.normal(0, 1,size=(8,100))\nimgs = g_model(noise)\n\nplt.figure(figsize=(10,5))\nfor i in range(8):\n    plt.subplot(2,4,i+1)\n    plt.imshow(imgs[i], cmap='bone')  \n\nplt.tight_layout()\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2024-06-08T00:56:24.263614Z","iopub.execute_input":"2024-06-08T00:56:24.264333Z","iopub.status.idle":"2024-06-08T00:56:28.294752Z","shell.execute_reply.started":"2024-06-08T00:56:24.264308Z","shell.execute_reply":"2024-06-08T00:56:28.293773Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"%%time\n\nif LOAD_GANS_FROM is None:\n    directory_path = 'Gan_Chest_X_Ray/'\n    if not os.path.exists(directory_path):\n        os.makedirs(directory_path)    \n\n    for i,img_id in enumerate(range(1000)): # Original: 1089, New:1000\n        if (i%100==0)&(i!=0): print(i,', ',end='')\n        \n        noise = np.random.normal(0, 1,size=(1,100))\n        img = g_model(noise)\n        img = np.array(img) # tensor -> np.array\n        \n        img_id = f'gan_00{i}' # gan_001.png\n        \n        # SAVE TO DISK\n        np.save(f'{directory_path}{img_id}',img[:]) # 256X256","metadata":{"execution":{"iopub.status.busy":"2024-06-08T00:56:28.295867Z","iopub.execute_input":"2024-06-08T00:56:28.296188Z","iopub.status.idle":"2024-06-08T00:56:28.303648Z","shell.execute_reply.started":"2024-06-08T00:56:28.296163Z","shell.execute_reply":"2024-06-08T00:56:28.302766Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Image Preprocessing\n___","metadata":{}},{"cell_type":"markdown","source":"**Split Train and Test Data**","metadata":{}},{"cell_type":"code","source":"train_list = pd.read_csv('/kaggle/input/data/train_val_list.txt', header = None)\ntrain_index = train_list[0].unique()\ntest_list = pd.read_csv('/kaggle/input/data/test_list.txt', header = None)\ntest_index = test_list[0].unique()\n\ntrain = df.loc[df['Image Index'].isin(train_index)]\ntest = df.loc[df['Image Index'].isin(test_index)]\ntrain.reset_index(drop=True, inplace=True)\ntest.reset_index(drop=True, inplace=True)","metadata":{"execution":{"iopub.status.busy":"2024-06-08T00:56:28.304722Z","iopub.execute_input":"2024-06-08T00:56:28.305047Z","iopub.status.idle":"2024-06-08T00:56:28.437352Z","shell.execute_reply.started":"2024-06-08T00:56:28.304998Z","shell.execute_reply":"2024-06-08T00:56:28.436528Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print('Shape of Train DataFrame: ', train.shape)\nprint('#'*25)\nprint('Shape of Test DataFrame: ', test.shape)","metadata":{"execution":{"iopub.status.busy":"2024-06-08T00:56:28.444064Z","iopub.execute_input":"2024-06-08T00:56:28.444396Z","iopub.status.idle":"2024-06-08T00:56:28.449774Z","shell.execute_reply.started":"2024-06-08T00:56:28.444371Z","shell.execute_reply":"2024-06-08T00:56:28.448787Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**Histogram Eqaulization**","metadata":{}},{"cell_type":"code","source":"def eq_hist(hist):\n    cdf = hist.cumsum()\n    cdf_m = np.ma.masked_equal(cdf,0)\n    \n    cdf_m = (cdf_m - cdf_m.min())*255/(cdf_m.max()-cdf_m.min())\n    cdf = np.ma.filled(cdf_m,0).astype('uint8')\n             \n    return cdf","metadata":{"execution":{"iopub.status.busy":"2024-06-08T00:56:28.451007Z","iopub.execute_input":"2024-06-08T00:56:28.451399Z","iopub.status.idle":"2024-06-08T00:56:28.460803Z","shell.execute_reply.started":"2024-06-08T00:56:28.451369Z","shell.execute_reply":"2024-06-08T00:56:28.459867Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"path = train.iloc[4]['path']\nimg = cv2.imread(path, cv2.IMREAD_GRAYSCALE)\n\nplt.style.use('seaborn-v0_8-whitegrid')\n\nplt.figure(figsize=(12,6))\nplt.subplot(2,2,1)\nplt.title('Before Histogram Eqaulization')\nplt.hist(img.flatten(),256,[0,256])\nplt.subplot(2,2,2)\nplt.imshow(img, cmap='bone')\n\nhist, _ = np.histogram(img.flatten(),256,[0,256])\ncdf = eq_hist(hist)\nimg2 = cdf[img]\n\nplt.subplot(2,2,3)\nplt.title('After Histogram Eqaulization')\nplt.hist(img2.flatten(), 256, [0,256])\nplt.subplot(2,2,4)\nplt.imshow(img2, cmap='bone')\nplt.tight_layout()\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2024-06-08T00:56:28.463823Z","iopub.execute_input":"2024-06-08T00:56:28.464644Z","iopub.status.idle":"2024-06-08T00:56:30.239244Z","shell.execute_reply.started":"2024-06-08T00:56:28.464611Z","shell.execute_reply":"2024-06-08T00:56:30.238249Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"#### Using Cv2.Canny","metadata":{}},{"cell_type":"code","source":"path = df.iloc[1]['path']\n\nimg = Image.open(path).convert('L')\nimg = img.resize((512,512), Image.Resampling.LANCZOS)\n\nimg = np.array(img)\nplt.subplot(1,2,1)\nplt.imshow(img, cmap='bone')\n\nmin_intensity_grad, max_intensity_grad = 20, 150\n# min_intensity_grad: 값이 작아질수록, 사소한 색의 변화도 감지한다.\n# max_intesnity_grad: max 이상의 경계선을 보다 더 강하게 표시한다. 즉, 값이 커질수록 색변화가 엄청 큰 녀석만 표시한다. \ncanny = cv2.Canny(img, min_intensity_grad, max_intensity_grad)\nplt.subplot(1,2,2)\nplt.imshow(canny)\n\nplt.tight_layout()\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2024-06-08T00:56:30.240667Z","iopub.execute_input":"2024-06-08T00:56:30.241452Z","iopub.status.idle":"2024-06-08T00:56:30.709678Z","shell.execute_reply.started":"2024-06-08T00:56:30.241424Z","shell.execute_reply":"2024-06-08T00:56:30.708398Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"#### Using Clahe\nCLAHE(Contrast Limited Adaptive Histogram Equalization)","metadata":{}},{"cell_type":"code","source":"clahe_obj = cv2.createCLAHE(clipLimit=8.0, tileGridSize=(8, 8))\n\n# clipLimit: 사진을 뚜렷하게 만들 때 너무 뚜렷해도 문제가 발생할 수 있기에 정도를 잡아준다. \n# tileGridSize: 값이 작을수록, 계산비용은 커지지만, 그만큼 지역적 특징을 더 잘잡아준다","metadata":{"execution":{"iopub.status.busy":"2024-06-08T00:56:30.711115Z","iopub.execute_input":"2024-06-08T00:56:30.711438Z","iopub.status.idle":"2024-06-08T00:56:30.717960Z","shell.execute_reply.started":"2024-06-08T00:56:30.711413Z","shell.execute_reply":"2024-06-08T00:56:30.716937Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"path = df.iloc[70]['path']\n\nimg = Image.open(path).convert('L')\nimg = img.resize((512,512), Image.Resampling.LANCZOS)\nimg = np.array(img)\nclahe = clahe_obj.apply(img)\nhist, _ = np.histogram(img.flatten(),256,[0,256])\ncdf = eq_hist(hist)\neq = cdf[img]\n\n\nplt.figure(figsize=(12,6))\nplt.subplot(1,3,1)\nplt.title('Original', size=10)\nplt.imshow(img, cmap='bone')\n\nplt.subplot(1,3,2)\nplt.title('Clahe', size=10)\nplt.imshow(clahe, cmap='bone')\n\nplt.subplot(1,3,3)\nplt.title(\"Histogram Eqaulization\", size=10)\nplt.imshow(eq, cmap='bone')\n\nplt.tight_layout()\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2024-06-08T00:56:30.719282Z","iopub.execute_input":"2024-06-08T00:56:30.719583Z","iopub.status.idle":"2024-06-08T00:56:31.589276Z","shell.execute_reply.started":"2024-06-08T00:56:30.719559Z","shell.execute_reply":"2024-06-08T00:56:31.588216Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"#### Filtering(Gaussian Blur, Median Blur, BilateralFilter)","metadata":{}},{"cell_type":"code","source":"path = df.iloc[70]['path']\n\nimg = Image.open(path).convert('L')\nimg = img.resize((512,512), Image.Resampling.LANCZOS)\nimg = np.array(img)\nimg = clahe_obj.apply(img)\n\ngaussian = cv2.GaussianBlur(img,(7,7),0)\nmedian = cv2.medianBlur(img,3)\nbilateral = cv2.bilateralFilter(img,20,10,150)\n\nplt.figure(figsize=(12,6))\nplt.subplot(1,4,1)\nplt.imshow(img, cmap='bone')\nplt.title('Original', size=10)\nplt.subplot(1,4,2)\nplt.imshow(gaussian, cmap='bone')\nplt.title('Gaussian Filtering', size=10)\nplt.subplot(1,4,3)\nplt.imshow(median, cmap='bone')\nplt.title('Median Filtering', size=10)\nplt.subplot(1,4,4)\nplt.imshow(bilateral, cmap='bone')\nplt.title('Bilateral Filtering', size=10)\n\nplt.tight_layout()\nplt.show()\n","metadata":{"execution":{"iopub.status.busy":"2024-06-08T00:56:31.590551Z","iopub.execute_input":"2024-06-08T00:56:31.590845Z","iopub.status.idle":"2024-06-08T00:56:32.706034Z","shell.execute_reply.started":"2024-06-08T00:56:31.590821Z","shell.execute_reply":"2024-06-08T00:56:32.705151Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## DataLoader\n___","metadata":{}},{"cell_type":"code","source":"class DataGenerator(tf.keras.utils.Sequence):\n    def __init__(self, data, batch_size=32, shuffle=False, mode='train', augment=False, augment2=False):\n        super().__init__()\n        self.data = data\n        self.batch_size = batch_size\n        self.shuffle = shuffle\n        self.mode = mode\n        self.augment = augment\n        self.augment2 = augment2\n        self.on_epoch_end()\n        self.clahe = cv2.createCLAHE(clipLimit=8.0, tileGridSize=(8, 8))\n        self.min_intensity_grad = 75\n        self.max_intensity_grad = 220\n        \n    def __len__(self):\n        ct = int(np.ceil(len(self.data)/self.batch_size))\n        return ct\n    \n    def __getitem__(self,index):\n        indexes = self.indexes[index*self.batch_size:(index+1)*self.batch_size]\n        X, y = self.__data_generation(indexes)\n        if self.augment: X = self.__augment(X)\n        elif self.augment2: X = self.__mixup(X)\n        return X,y\n    \n    def on_epoch_end(self,):\n        self.indexes = np.arange(len(self.data))\n        if self.shuffle: np.random.shuffle(self.indexes)\n        \n    def __data_generation(self, indexes):\n        \n        X = np.zeros((len(indexes),256, 256,4), dtype='float32') \n        y = np.zeros((len(indexes),3), dtype='float32')\n        \n        for j,i in enumerate(indexes):\n            row = self.data.iloc[i]\n            path = row['path']\n            if 'gan' not in path:\n                img = Image.open(path).convert('L')\n                img = img.resize((256,256), Image.Resampling.LANCZOS)\n                img = np.array(img) # PIL Object -> np.array(uint8)\n                \n            else:\n                img = np.load(path)\n                img = img.reshape(256,256)\n                img = (img + 1) / 2\n                img = (img*255).astype('uint8')\n            \n            # Clahe\n            img = self.clahe.apply(img)\n            # Canny\n            img2 = cv2.Canny(img, self.min_intensity_grad, self.max_intensity_grad)\n    \n            \n            # Standardize For Image: -1~1\n            ep = 1e-7\n            m = np.nanmean(img)\n            s = np.nanstd(img)\n            img = (img-m)/(s+ep) # Z-score\n            img = np.nan_to_num(img, nan=0.0)\n            \n            m = np.nanmean(img2)\n            s = np.nanstd(img2)\n            img2 = (img2-m)/(s+ep) # Z-score\n            img2 = np.nan_to_num(img2, nan=0.0)    \n            \n            # Zoom in\n            img3 = img.copy()\n            img3 = img3[int(256*0.15):int(256*0.85):,:128] \n            img3 = cv2.resize(img3, (256, 256)) \n            \n            # Zoom in\n            img4 = img.copy()\n            img4 = img4[int(256*0.15):int(256*0.85):,128:] \n            img4 = cv2.resize(img4, (256, 256))\n            \n            X[j,:,:,0] = img\n            X[j,:,:,1] = img2\n            X[j,:,:,2] = img3\n            X[j,:,:,3] = img4\n            if self.mode != 'test':\n                y[j,] = row[TARGET]\n        \n        return X,y    \n    \n    \n    def __augment(self,img_batch):\n        composition = albu.Compose([\n            albu.HorizontalFlip(p=0.5),\n        ])\n        for i in range(img_batch.shape[0]): # shape[0]: batch_size\n            img = composition(image=img_batch[i,])['image']\n            img_batch[i,] = img\n        return img_batch\n    \n    def __mixup(self, img_batch, mixup_prob=0.5):\n        batch_size, height, width, channels = img_batch.shape\n    \n        idx = np.random.permutation(batch_size)\n        if np.random.rand() < mixup_prob:  \n            lam = np.random.beta(2.0, 2.0)  \n            mixed_batch = []\n            for i in range(batch_size):\n                j = idx[i]\n                mixed_img = img_batch[i,] * lam + (1-lam) * img_batch[j,]  \n                \n                mixed_batch.append(mixed_img)\n            mixed_batch = np.array(mixed_batch)\n            return mixed_batch\n        else:\n            return img_batch","metadata":{"execution":{"iopub.status.busy":"2024-06-08T00:56:32.707660Z","iopub.execute_input":"2024-06-08T00:56:32.707998Z","iopub.status.idle":"2024-06-08T00:56:32.995170Z","shell.execute_reply.started":"2024-06-08T00:56:32.707969Z","shell.execute_reply":"2024-06-08T00:56:32.994149Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"#### Display DataLoader","metadata":{}},{"cell_type":"code","source":"ROWS = 1\nCOLS = 3\nBATCHES = 0\n\n\nfor col in train['Finding Labels'].unique()[1:3]:\n    print('#'*25)\n    print(f'# Finding Labels: {col}')\n    print('#'*25)\n    \n    tmp = train[train['Finding Labels']==col]\n    gen = DataGenerator(tmp, batch_size=32, shuffle=True, augment=True, augment2=True)\n\n    plt.style.use('seaborn-v0_8-whitegrid')\n    plt.figure(figsize=(12,6))\n    for batch_idx, (X,y) in enumerate(gen):\n        for j in range(ROWS):\n            for i in range(COLS):\n                plt.subplot(ROWS,COLS,j*COLS+i+1)\n                image_id = tmp.iloc[j*COLS+i]['Image Index']\n                label = tmp.iloc[j*COLS+i]['Finding Labels']\n                plt.imshow(X[j*COLS+i,:,:,0], cmap='bone')\n                plt.title(f'ID: {image_id}\\n Label: {label}', size=10)\n            plt.tight_layout()\n            plt.show()\n        if BATCHES == batch_idx: break","metadata":{"execution":{"iopub.status.busy":"2024-06-08T00:56:32.996272Z","iopub.execute_input":"2024-06-08T00:56:32.996593Z","iopub.status.idle":"2024-06-08T00:56:37.257051Z","shell.execute_reply.started":"2024-06-08T00:56:32.996569Z","shell.execute_reply":"2024-06-08T00:56:37.256151Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"#### 4 Channels","metadata":{}},{"cell_type":"code","source":"BATCHES=0\n\ngen = DataGenerator(train, batch_size=32, shuffle=True, augment=False, augment2=False)\n\nplt.style.use('Solarize_Light2')\nplt.figure(figsize=(10,5))\nfor batch_idx, (X,y) in enumerate(gen):\n    plt.subplot(1,4,1)\n    plt.imshow(X[0,:,:,0], cmap='bone')\n    plt.title('Original')\n    \n    plt.subplot(1,4,2)\n    plt.imshow(X[0,:,:,1])\n    plt.title('Canny')\n    \n    plt.subplot(1,4,3)\n    plt.imshow(X[0,:,:,2], cmap='bone')\n    plt.title('Left Cropped')\n    \n    plt.subplot(1,4,4)\n    plt.imshow(X[0,:,:,3], cmap='bone')\n    plt.title('Right Cropped')\n\n    plt.tight_layout()\n    plt.show()\n    if BATCHES == batch_idx: break","metadata":{"execution":{"iopub.status.busy":"2024-06-08T00:56:37.258294Z","iopub.execute_input":"2024-06-08T00:56:37.258653Z","iopub.status.idle":"2024-06-08T00:56:39.286591Z","shell.execute_reply.started":"2024-06-08T00:56:37.258617Z","shell.execute_reply":"2024-06-08T00:56:39.285614Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Build Image Classification Model\n___","metadata":{}},{"cell_type":"markdown","source":"#### Step Training Scheduler","metadata":{}},{"cell_type":"code","source":"import math\nEPOCHS = 6\n\ndef step_lrfn(epochs):\n    return [1e-3,1e-4,1e-4,1e-5,1e-5,1e-6][epochs]\n\ndef cosine_lrfn(epochs, initial_lr=1e-3, min_lr=1e-6, total_epochs=EPOCHS):\n    return min_lr + (initial_lr - min_lr) * 0.5 * (1 + math.cos(math.pi * epochs / total_epochs))\n\n\nLR = tf.keras.callbacks.LearningRateScheduler(step_lrfn, verbose = True)\n\nplt.figure(figsize=(10,4))\nplt.subplot(1,2,1)\nplt.title('Step Training Scheduler')\nrng = [i for i in range(EPOCHS)]\ny = [step_lrfn(x) for x in rng]\nplt.plot(rng, y, '-o')\nplt.subplot(1,2,2)\nplt.title('Cosine Training Scheduler')\nrng = [i for i in range(EPOCHS)]\ny = [cosine_lrfn(x) for x in rng]\nplt.plot(rng,y, '-o')\nplt.tight_layout()\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2024-06-08T00:56:39.287711Z","iopub.execute_input":"2024-06-08T00:56:39.287988Z","iopub.status.idle":"2024-06-08T00:56:39.701273Z","shell.execute_reply.started":"2024-06-08T00:56:39.287964Z","shell.execute_reply":"2024-06-08T00:56:39.700244Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"!pip install -q --no-index --find-links=/kaggle/input/tf-efficientnet-whl-files /kaggle/input/tf-efficientnet-whl-files/efficientnet-1.1.1-py3-none-any.whl","metadata":{"execution":{"iopub.status.busy":"2024-06-08T00:56:39.702399Z","iopub.execute_input":"2024-06-08T00:56:39.702697Z","iopub.status.idle":"2024-06-08T00:56:53.520441Z","shell.execute_reply.started":"2024-06-08T00:56:39.702672Z","shell.execute_reply":"2024-06-08T00:56:53.519155Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import efficientnet.tfkeras as efn\nimport tensorflow.keras.backend as K\n\n\ndef build_model():\n    inp = tf.keras.Input(shape=(256,256,4))\n    base_model = efn.EfficientNetB0(include_top=False, weights=None, input_shape=(512,512,3))\n    base_model.load_weights('/kaggle/input/tf-efficientnet-noisy-student-weights/efficientnet-b0_noisy-student_notop.h5')\n    \n    ## Freezing Layers\n    for layer in base_model.layers[:len(base_model.layers)//10]:\n        layer.trainable = False\n    \n    x1 = tf.keras.layers.Concatenate(axis=2)([inp[:,:,:,0:1], inp[:,:,:,1:2]]) # 256X512\n    x2 = tf.keras.layers.Concatenate(axis=2)([inp[:,:,:,2:3], inp[:,:,:,3:4]]) # 256X512\n    x = tf.keras.layers.Concatenate(axis=1)([x1,x2]) #512X512\n    x = tf.keras.layers.Concatenate(axis=3)([x,x,x])\n    \n    # Output\n    x = base_model(x)\n    x = tf.keras.layers.GlobalAveragePooling2D()(x) # MeanPooling\n    x = tf.keras.layers.Dense(3, activation='softmax', dtype='float32')(x)\n    \n    # Compile\n    model = tf.keras.Model(inputs=inp, outputs=x)\n    loss = tf.keras.losses.KLDivergence()\n    opt = tf.keras.optimizers.AdamW(learning_rate=1e-3)\n    model.compile(loss=loss, optimizer=opt, metrics=['accuracy'])\n    \n    return model","metadata":{"execution":{"iopub.status.busy":"2024-06-08T00:56:53.522290Z","iopub.execute_input":"2024-06-08T00:56:53.522677Z","iopub.status.idle":"2024-06-08T00:56:53.560887Z","shell.execute_reply.started":"2024-06-08T00:56:53.522641Z","shell.execute_reply":"2024-06-08T00:56:53.559912Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model = build_model()\n#model.summary()\nfrom tensorflow.keras.utils import plot_model\n\nplot_model(model, show_shapes=True, show_layer_names=True)","metadata":{"execution":{"iopub.status.busy":"2024-06-08T00:56:53.562260Z","iopub.execute_input":"2024-06-08T00:56:53.564269Z","iopub.status.idle":"2024-06-08T00:56:57.417838Z","shell.execute_reply.started":"2024-06-08T00:56:53.564233Z","shell.execute_reply":"2024-06-08T00:56:57.416935Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Training Model\n___","metadata":{}},{"cell_type":"code","source":"all_oof = []\nall_true = []\nall_history = defaultdict(list)\n\ngkf = GroupKFold(n_splits=5)\nfor i, (train_index, valid_index) in enumerate(gkf.split(train, train['Finding Labels'], train['Patient ID'])):  \n    \n    print('#'*25)\n    print(f'### Fold {i+1}')\n    \n    train_gen = DataGenerator(train.iloc[train_index], shuffle=True, batch_size=8, augment=True, augment2=True)\n    valid_gen = DataGenerator(train.iloc[valid_index], shuffle=False, batch_size=16, mode='valid')\n    \n    print(f'### train size {len(train_index)}, valid size {len(valid_index)}')\n    print('#'*25)\n    \n    K.clear_session()\n    with strategy.scope():\n        model = build_model()\n    if LOAD_MODELS_FROM is None:\n        history =  model.fit(train_gen, verbose=1,\n              validation_data = valid_gen,\n              epochs=EPOCHS, callbacks = [LR])\n        model.save_weights(f'EffNet_v{VER}_f{i}.h5')\n        all_history['Train Loss'].append(history.history['loss'])\n        all_history['Valid Loss'].append(history.history['val_loss'])\n    else:\n        model.load_weights(f'{LOAD_MODELS_FROM}EffNet_v{VER}_f{i}.h5')\n        \n    oof = model.predict(valid_gen, verbose=1)\n    all_oof.append(oof)\n    all_true.append(train.iloc[valid_index][TARGET].values)\n    \n    plt.style.use('default')\n    cm = confusion_matrix(np.argmax(oof, axis=1), np.argmax(train.iloc[valid_index][TARGET].values, axis=1), labels=[x for x in range(3)])\n    disp = ConfusionMatrixDisplay(confusion_matrix=cm,\n                              display_labels=[x for x in range(3)])  \n    disp.plot()\n    plt.show()\n    \n    del model, oof, train_gen, valid_gen\n    gc.collect()\n    \nall_oof = np.concatenate(all_oof)\nall_true = np.concatenate(all_true)","metadata":{"execution":{"iopub.status.busy":"2024-06-08T00:56:57.419279Z","iopub.execute_input":"2024-06-08T00:56:57.419897Z","iopub.status.idle":"2024-06-08T00:59:00.657562Z","shell.execute_reply.started":"2024-06-08T00:56:57.419865Z","shell.execute_reply":"2024-06-08T00:59:00.656752Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"if LOAD_MODELS_FROM is None:\n    plt.figure(figsize=(12, 6))\n    plt.subplot(1,2,1)\n    plt.title('Train Loss')\n    for i, train_loss in enumerate(all_history['Train Loss']):\n        plt.plot(train_loss, label=f'Fold {i+1} Train Loss')\n    plt.xlabel('Epochs')\n    plt.ylabel('Loss')    \n    plt.legend()  \n    \n    plt.subplot(1,2,2)\n    plt.title('Valid Loss')\n    for i, valid_loss in enumerate(all_history['Valid Loss']):\n        plt.plot(valid_loss, label=f'Fold {i+1} Validation Loss', color='orange')\n    plt.xlabel('Epochs')\n    plt.ylabel('Loss')\n\n    plt.legend()\n    plt.tight_layout()\n    plt.show()","metadata":{"execution":{"iopub.status.busy":"2024-06-08T00:59:00.658896Z","iopub.execute_input":"2024-06-08T00:59:00.659369Z","iopub.status.idle":"2024-06-08T00:59:00.669277Z","shell.execute_reply.started":"2024-06-08T00:59:00.659334Z","shell.execute_reply":"2024-06-08T00:59:00.668328Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Cross Validation(Accuracy & F1 Score)\n___","metadata":{}},{"cell_type":"code","source":"# Accuracy\naccuracy = accuracy_score(np.argmax(all_true, axis=1), np.argmax(all_oof, axis=1))\nprint('Accuracy for EfficientNetB0 =', accuracy)\n\n# F1 Score\nf1 = f1_score(np.argmax(all_true, axis=1), np.argmax(all_oof, axis=1), average='weighted')\nprint('F1 Score for EfficientNetB0 =', f1)","metadata":{"execution":{"iopub.status.busy":"2024-06-08T00:59:00.670567Z","iopub.execute_input":"2024-06-08T00:59:00.670846Z","iopub.status.idle":"2024-06-08T00:59:00.691459Z","shell.execute_reply.started":"2024-06-08T00:59:00.670823Z","shell.execute_reply":"2024-06-08T00:59:00.690307Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Grad Cam in CXR ","metadata":{}},{"cell_type":"markdown","source":"**Build Grad Cam Model**","metadata":{}},{"cell_type":"code","source":"def build_cam_model():\n    inp = tf.keras.Input(shape=(256,256,4))\n    base_model = efn.EfficientNetB0(include_top=False, weights=None, input_shape=(512,512,3))\n    base_model.load_weights('/kaggle/input/tf-efficientnet-noisy-student-weights/efficientnet-b0_noisy-student_notop.h5')\n    \n    ## Freezing Layers\n    for layer in base_model.layers[:len(base_model.layers)//10]:\n        layer.trainable = False\n    \n    x1 = tf.keras.layers.Concatenate(axis=2)([inp[:,:,:,0:1], inp[:,:,:,1:2]]) # 256X512\n    x2 = tf.keras.layers.Concatenate(axis=2)([inp[:,:,:,2:3], inp[:,:,:,3:4]]) # 256X512\n    x = tf.keras.layers.Concatenate(axis=1)([x1,x2]) #512X512\n    x = tf.keras.layers.Concatenate(axis=3)([x,x,x])\n    \n    # Output\n    x0 = base_model(x)\n    x = tf.keras.layers.GlobalAveragePooling2D()(x0) # MeanPooling\n    x = tf.keras.layers.Dense(3, activation='softmax', dtype='float32')(x)\n    \n    # Compile\n    model = tf.keras.Model(inputs=inp, outputs=[x,x0])\n    loss = tf.keras.losses.KLDivergence()\n    opt = tf.keras.optimizers.AdamW(learning_rate=1e-3)\n    model.compile(loss=loss, optimizer=opt, metrics=['accuracy'])\n    \n    return model","metadata":{"execution":{"iopub.status.busy":"2024-06-08T01:01:10.323667Z","iopub.execute_input":"2024-06-08T01:01:10.324037Z","iopub.status.idle":"2024-06-08T01:01:10.336094Z","shell.execute_reply.started":"2024-06-08T01:01:10.324010Z","shell.execute_reply":"2024-06-08T01:01:10.335151Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"K.clear_session()\nwith strategy.scope():\n    cam_model = build_cam_model()\nif LOAD_MODELS_FROM:\n    cam_model.load_weights(f'{LOAD_MODELS_FROM}EffNet_v1_f0.h5')\n    emp_weights = cam_model.layers[-1].get_weights()[0][:,1]\n    fib_weights = cam_model.layers[-1].get_weights()[0][:,2]\n    \nprint('Using Fold 0 Model')","metadata":{"execution":{"iopub.status.busy":"2024-06-08T01:01:12.614197Z","iopub.execute_input":"2024-06-08T01:01:12.615078Z","iopub.status.idle":"2024-06-08T01:01:19.329082Z","shell.execute_reply.started":"2024-06-08T01:01:12.615038Z","shell.execute_reply":"2024-06-08T01:01:19.328139Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**Helper Function**","metadata":{}},{"cell_type":"code","source":"def mask2contour(mask, width=5):\n    h = mask.shape[0]\n    w = mask.shape[1]\n    \n    mask2 = np.concatenate([mask[:,width:], np.zeros((h,width))], axis=1)\n    mask2 = np.logical_xor(mask, mask2)\n    mask3 = np.concatenate([mask[width:,:], np.zeros((width,w))], axis=0)\n    mask3 = np.logical_xor(mask,mask3)\n\n    return np.logical_or(mask2,mask3)","metadata":{"execution":{"iopub.status.busy":"2024-06-08T01:06:23.640334Z","iopub.execute_input":"2024-06-08T01:06:23.640859Z","iopub.status.idle":"2024-06-08T01:06:23.647883Z","shell.execute_reply.started":"2024-06-08T01:06:23.640826Z","shell.execute_reply":"2024-06-08T01:06:23.646871Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**Let's see What is important in grad gam**","metadata":{}},{"cell_type":"code","source":"BATCHES = 0\n\nfor label in ['Emphysema','Fibrosis']:\n    tmp = train[train['Finding Labels'] == label]\n    valid_gen = DataGenerator(tmp, shuffle=False, batch_size=16, mode='test')\n    \n    _, x0 = cam_model.predict(valid_gen, verbose=1)\n    \n    print('#'*25)\n    print(f'### {label}')\n    print('#'*25)\n    \n    plt.figure(figsize=(12,6))\n    for batch_idx, (X,y) in enumerate(valid_gen):\n        for i in range(4):\n            plt.subplot(1,4,i+1)\n            img_id = tmp.iloc[16*batch_idx + i]['Image Index']\n            plt.title(f'Label: {label}\\n Id: {img_id}', size=10)\n            \n            if label == 'Emphysema':\n                weights = emp_weights\n            else: \n                weights = fib_weights\n            \n            img2 = np.sum(x0[i,] * weights, axis=-1)\n            img2 = cv2.resize(img2,(512,512))\n            \n            cut = np.percentile(img2.flatten(), [95])[0]\n            cntr = img2.copy()\n            cntr[cntr>cut] = 100\n            cntr[cntr<cut] = 0\n            cntr = mask2contour(cntr)\n            \n            \n            x1 = [X[i,:,:,k:k+1] for k in range(2)]\n            x2 = [X[i,:,:,k+2:k+3] for k in range(2)]\n            x1 = np.concatenate(x1,axis=0)  \n            x2 = np.concatenate(x2,axis=0)\n            x3 = np.concatenate([x1,x2],axis=1)\n            x3 = x3.reshape(512,512)\n            \n            mx = np.max(x3)\n            x3[cntr>0] = mx\n            plt.imshow(x3, cmap='bone')\n            \n        \n        plt.tight_layout()\n        plt.show()\n        if BATCHES == batch_idx: break","metadata":{"execution":{"iopub.status.busy":"2024-06-08T01:23:06.229895Z","iopub.execute_input":"2024-06-08T01:23:06.230557Z","iopub.status.idle":"2024-06-08T01:23:33.602691Z","shell.execute_reply.started":"2024-06-08T01:23:06.230525Z","shell.execute_reply":"2024-06-08T01:23:33.601827Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Pseduo Labeling","metadata":{}},{"cell_type":"code","source":"%%time\n\nImage_Index = []\nfor i in range(1000):\n    index = f'gan_00{i}.npy'\n    Image_Index.append(index)\n    \ngan_train = pd.DataFrame({'Image Index' : Image_Index})\n\ntmp = {os.path.basename(x): x for x in glob(os.path.join('/kaggle', 'input', '*','*', '*.npy'))}    \n    \ngan_train['path'] = gan_train['Image Index'].map(tmp)","metadata":{"execution":{"iopub.status.busy":"2024-06-08T00:59:07.828497Z","iopub.status.idle":"2024-06-08T00:59:07.828888Z","shell.execute_reply.started":"2024-06-08T00:59:07.828707Z","shell.execute_reply":"2024-06-08T00:59:07.828722Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"preds = []\nmodel = build_model()\n\ngan_gen = DataGenerator(gan_train, shuffle=False, batch_size=64, mode='test')\n\nfor i in range(5):\n    print(f'Fold {i+1}') \n    if LOAD_MODELS_FROM:\n        model.load_weights(f'{LOAD_MODELS_FROM}EffNet_v{VER}_f{i}.h5')\n    else: \n        model.load_weights(f'EffNet_v{VER}_f{i}.h5')\n    \n    pred = model.predict(gan_gen, verbose=1) \n    \npreds.append(pred)\npred = np.mean(preds,axis=0)\nprint('Test preds shape',pred.shape)","metadata":{"execution":{"iopub.status.busy":"2024-06-08T00:59:07.830510Z","iopub.status.idle":"2024-06-08T00:59:07.830850Z","shell.execute_reply.started":"2024-06-08T00:59:07.830688Z","shell.execute_reply":"2024-06-08T00:59:07.830702Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train = train[['Image Index', 'path','No Finding', 'Emphysema','Fibrosis']]\n\npred1 = pd.DataFrame(pred)\ngan_train = pd.concat([gan_train, pred1], axis=1)\ngan_train = gan_train.rename(columns = {0: 'No Finding', 1: 'Emphysema', 2: 'Fibrosis'})\npseudo_train = pd.concat([train, gan_train])","metadata":{"execution":{"iopub.status.busy":"2024-06-08T00:59:07.832294Z","iopub.status.idle":"2024-06-08T00:59:07.832753Z","shell.execute_reply.started":"2024-06-08T00:59:07.832511Z","shell.execute_reply":"2024-06-08T00:59:07.832531Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"LOAD_MODELS_FROM = None","metadata":{"execution":{"iopub.status.busy":"2024-06-08T00:59:07.834423Z","iopub.status.idle":"2024-06-08T00:59:07.834728Z","shell.execute_reply.started":"2024-06-08T00:59:07.834577Z","shell.execute_reply":"2024-06-08T00:59:07.834589Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"all_oof = []\nall_true = []\nall_history = defaultdict(list)\n\nkf = KFold(n_splits=5, shuffle=True, random_state=42)\nfor i, (train_index, valid_index) in enumerate(kf.split(pseudo_train)):  \n    \n    print('#'*25)\n    print(f'### Fold {i+1}')\n    \n    train_gen = DataGenerator(pseudo_train.iloc[train_index], shuffle=True, batch_size=8, augment=True, augment2=True)\n    valid_gen = DataGenerator(pseudo_train.iloc[valid_index], shuffle=False, batch_size=16, mode='valid')\n\n    print(f'### train size {len(train_index)}, valid size {len(valid_index)}')\n    print('#'*25)\n    \n    K.clear_session()\n    with strategy.scope():\n        model = build_model()\n    if LOAD_MODELS_FROM is None:\n        history =  model.fit(train_gen, verbose=1,\n              validation_data = valid_gen,\n              epochs=EPOCHS, callbacks = [LR])\n        model.save_weights(f'EffNet_GAN_v{VER}_f{i}.h5')\n        all_history['Train Loss'].append(history.history['loss'])\n        all_history['Valid Loss'].append(history.history['val_loss'])\n    else:\n        model.load_weights(f'{LOAD_MODELS_FROM}EffNet_GAN_v{VER}_f{i}.h5')\n        \n    oof = model.predict(valid_gen, verbose=1)\n    all_oof.append(oof)\n    all_true.append(pseudo_train.iloc[valid_index][TARGET].values)\n    \n    plt.style.use('default')\n    cm = confusion_matrix(np.argmax(oof, axis=1), np.argmax(pseudo_train.iloc[valid_index][TARGET].values, axis=1), labels=[x for x in range(3)])\n    disp = ConfusionMatrixDisplay(confusion_matrix=cm,\n                              display_labels=[x for x in range(3)])  \n    disp.plot()\n    plt.show()\n    \n    del model, oof, train_gen, valid_gen\n    gc.collect()\n    \nall_oof = np.concatenate(all_oof)\nall_true = np.concatenate(all_true)","metadata":{"execution":{"iopub.status.busy":"2024-06-08T00:59:07.836145Z","iopub.status.idle":"2024-06-08T00:59:07.836485Z","shell.execute_reply.started":"2024-06-08T00:59:07.836323Z","shell.execute_reply":"2024-06-08T00:59:07.836337Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Accuracy\naccuracy = accuracy_score(np.argmax(all_true, axis=1), np.argmax(all_oof, axis=1))\nprint('Accuracy for EfficientNetB0 =', accuracy)\n\n# F1 Score\nf1 = f1_score(np.argmax(all_true, axis=1), np.argmax(all_oof, axis=1), average='weighted')\nprint('F1 Score for EfficientNetB0 =', f1)","metadata":{"execution":{"iopub.status.busy":"2024-06-08T00:59:07.837987Z","iopub.status.idle":"2024-06-08T00:59:07.838351Z","shell.execute_reply.started":"2024-06-08T00:59:07.838178Z","shell.execute_reply":"2024-06-08T00:59:07.838192Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Inference","metadata":{}},{"cell_type":"code","source":"preds = []\nmodel = build_model()\n\n\n## No TTA  Yet\ntest_gen = DataGenerator(test, shuffle=False, batch_size=64, mode='test')\n\nfor i in range(5):\n    print(f'Fold {i+1}') \n    if LOAD_MODELS_FROM:\n        model.load_weights(f'{LOAD_MODELS_FROM}EffNet_GAN_v{VER}_f{i}.h5')\n    else: \n        model.load_weights(f'EffNet_GAN_v{VER}_f{i}.h5')\n    \n    pred = model.predict(test_gen, verbose=1) \n    \npreds.append(pred)\npred = np.mean(preds,axis=0)\nprint('Test preds shape',pred.shape)","metadata":{"execution":{"iopub.status.busy":"2024-06-08T00:59:07.840391Z","iopub.status.idle":"2024-06-08T00:59:07.841044Z","shell.execute_reply.started":"2024-06-08T00:59:07.840802Z","shell.execute_reply":"2024-06-08T00:59:07.840821Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Accuracy \naccuracy = accuracy_score(np.argmax(test[TARGET].values, axis=1), np.argmax(pred, axis=1))\nprint('Accuracy for EfficientNetB0 =', accuracy)\n\n# F1 Score \nf1 = f1_score(np.argmax(test[TARGET].values, axis=1), np.argmax(pred, axis=1), average='weighted') \nprint('F1 Score for EfficientNetB0 =', f1)","metadata":{"execution":{"iopub.status.busy":"2024-06-08T00:59:07.842383Z","iopub.status.idle":"2024-06-08T00:59:07.842850Z","shell.execute_reply.started":"2024-06-08T00:59:07.842601Z","shell.execute_reply":"2024-06-08T00:59:07.842620Z"},"trusted":true},"execution_count":null,"outputs":[]}]}