{"metadata":{"kaggle":{"accelerator":"nvidiaTeslaT4","dataSources":[{"sourceType":"competition","sourceId":24800,"datasetId":1042002,"databundleVersionId":1831594},{"sourceType":"datasetVersion","sourceId":7944294,"datasetId":4670999,"databundleVersionId":8053257},{"sourceType":"datasetVersion","sourceId":8081382,"datasetId":4769903,"databundleVersionId":8197530}],"dockerImageVersionId":30684,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true},"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"codemirror_mode":{"name":"ipython","version":3},"file_extension":".py","mimetype":"text/x-python","name":"python","nbconvert_exporter":"python","pygments_lexer":"ipython3","version":"3.10.13"},"papermill":{"default_parameters":{},"duration":17324.565701,"end_time":"2024-03-26T23:26:42.769088","environment_variables":{},"exception":null,"input_path":"__notebook__.ipynb","output_path":"__notebook__.ipynb","parameters":{},"start_time":"2024-03-26T18:37:58.203387","version":"2.5.0"}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"!pip install monai thop pydicom\n# !pip install gdcm pylibjpeg pylibjpeg-libjpeg","metadata":{"papermill":{"duration":14.580562,"end_time":"2024-03-26T18:38:15.480097","exception":false,"start_time":"2024-03-26T18:38:00.899535","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2024-04-10T10:18:29.494562Z","iopub.execute_input":"2024-04-10T10:18:29.494846Z","iopub.status.idle":"2024-04-10T10:18:44.365319Z","shell.execute_reply.started":"2024-04-10T10:18:29.49482Z","shell.execute_reply":"2024-04-10T10:18:44.364406Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"!python -c \"import monai\" || pip install -q \"monai-weekly[pillow, tqdm]\"\n!python -c \"import matplotlib\" || pip install -q matplotlib\n%matplotlib inline","metadata":{"papermill":{"duration":45.198596,"end_time":"2024-03-26T18:39:00.69051","exception":false,"start_time":"2024-03-26T18:38:15.491914","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2024-04-10T10:18:44.367575Z","iopub.execute_input":"2024-04-10T10:18:44.368306Z","iopub.status.idle":"2024-04-10T10:19:27.75811Z","shell.execute_reply.started":"2024-04-10T10:18:44.368267Z","shell.execute_reply":"2024-04-10T10:19:27.756997Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"!pip install monai","metadata":{"execution":{"iopub.status.busy":"2024-04-10T10:19:27.75947Z","iopub.execute_input":"2024-04-10T10:19:27.759765Z","iopub.status.idle":"2024-04-10T10:19:40.284956Z","shell.execute_reply.started":"2024-04-10T10:19:27.759738Z","shell.execute_reply":"2024-04-10T10:19:40.28386Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import os\nimport shutil\nimport tempfile\nimport matplotlib.pyplot as plt\nimport PIL\nimport torch\nimport numpy as np\nfrom sklearn.metrics import classification_report\n\nfrom monai.apps import download_and_extract\nfrom monai.config import print_config\nfrom monai.data import decollate_batch, DataLoader\nfrom monai.metrics import ROCAUCMetric\nfrom monai.networks.nets import DenseNet121\nfrom monai.transforms import (\n    Activations,\n    EnsureChannelFirst,\n    AsDiscrete,\n    Compose,\n    LoadImage,\n    RandFlip,\n    RandRotate,\n    RandZoom,\n    ScaleIntensity,\n)\nfrom monai.utils import set_determinism\n\nprint_config()","metadata":{"papermill":{"duration":29.259606,"end_time":"2024-03-26T18:39:29.962134","exception":false,"start_time":"2024-03-26T18:39:00.702528","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2024-04-10T10:19:46.246113Z","iopub.execute_input":"2024-04-10T10:19:46.246942Z","iopub.status.idle":"2024-04-10T10:20:16.599298Z","shell.execute_reply.started":"2024-04-10T10:19:46.246903Z","shell.execute_reply":"2024-04-10T10:20:16.598215Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import pandas as pd\nimport pydicom\nfrom torchvision import transforms\nimport pickle\nimport cv2\n\nfrom sklearn.model_selection import train_test_split\n\n# Timing utility\nfrom timeit import default_timer as timer\nfrom tqdm import tqdm\n\nimport torch\nfrom sklearn.metrics import roc_auc_score, accuracy_score, precision_score, recall_score, f1_score, roc_curve, auc\nfrom sklearn.metrics import roc_auc_score\nfrom sklearn.metrics import multilabel_confusion_matrix\nimport seaborn as sn\n\nimport warnings\nwarnings.filterwarnings('ignore', category=FutureWarning)\nwarnings.filterwarnings('ignore', category=UserWarning)\n\n\nprint(\"all imported\")\n\nset_determinism(seed=0)","metadata":{"papermill":{"duration":0.217627,"end_time":"2024-03-26T18:39:30.195448","exception":false,"start_time":"2024-03-26T18:39:29.977821","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2024-04-10T10:20:16.601674Z","iopub.execute_input":"2024-04-10T10:20:16.602592Z","iopub.status.idle":"2024-04-10T10:20:16.794118Z","shell.execute_reply.started":"2024-04-10T10:20:16.60255Z","shell.execute_reply":"2024-04-10T10:20:16.793133Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"diseases = ['Aortic enlargement', 'Atelectasis', 'Calcification', 'Cardiomegaly',\n 'Consolidation', 'ILD', 'Infiltration', 'Lung Opacity', 'Nodule/Mass',\n 'Other lesion', 'Pleural effusion', 'Pleural thickening', 'Pneumothorax',\n 'Pulmonary fibrosis']\n\n# decided on the basis of frequency of occurence of individual diseases in images.\n\n# Drop columns not in the list\ncolumns_to_keep = diseases.copy()\ncolumns_to_keep.append('image_id')\n\nprint(diseases)\nprint(columns_to_keep)","metadata":{"papermill":{"duration":0.02171,"end_time":"2024-03-26T18:39:30.230676","exception":false,"start_time":"2024-03-26T18:39:30.208966","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2024-04-10T10:20:16.79552Z","iopub.execute_input":"2024-04-10T10:20:16.795868Z","iopub.status.idle":"2024-04-10T10:20:16.802174Z","shell.execute_reply.started":"2024-04-10T10:20:16.795837Z","shell.execute_reply":"2024-04-10T10:20:16.801065Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# some helper functions:-\n\ndef delete_columns(df, columns_to_keep=columns_to_keep, root_folder='/train'):\n    columns_to_drop = [col for col in df.columns if col not in columns_to_keep]\n    print(\"COLUMNS to drop:\", columns_to_drop)\n    df.drop(columns=columns_to_drop, inplace=True)\n    print(f\"Deleted columns from {root_folder.split('/')[-1]} folder\")\n    return df\n\ndef remove_all_zeros(df, diseases=diseases, root_folder='/train'):\n    # Remove rows where all values are 0 in the disease labels\n    df = df[(df[diseases] != 0).any(axis=1)]\n    df.reset_index(drop=True, inplace=True)\n    return df\n\ndef add_file_path_column(df, root_folder='/train'):\n    df['file_path'] = df['image_id'].apply(lambda x: os.path.join(root_folder, f\"{x}.npy\")) # adjust dicom or png\n    print(f\"Added file_path column to {root_folder.split('/')[-1]} folder\")\n    return df\n\n","metadata":{"papermill":{"duration":0.023135,"end_time":"2024-03-26T18:39:30.267193","exception":false,"start_time":"2024-03-26T18:39:30.244058","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2024-04-10T10:20:16.804622Z","iopub.execute_input":"2024-04-10T10:20:16.805016Z","iopub.status.idle":"2024-04-10T10:20:16.814264Z","shell.execute_reply.started":"2024-04-10T10:20:16.804987Z","shell.execute_reply":"2024-04-10T10:20:16.813337Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import pandas as pd\n\n# Load the original CSV file\ntrain_data = pd.read_csv(\"/kaggle/input/vinbigdata-chest-xray-abnormalities-detection/train.csv\")\n\n\n# Extract unique pairs of class_id and class_name\nclass_mapping = train_data[['class_id', 'class_name']].drop_duplicates()\n\n# Sort the mapping by class_id\nclass_mapping = class_mapping.sort_values(by='class_id')\n\n# Display the sorted mapping without the index\nprint(class_mapping.to_string(index=False))\n\ndisease_labels = class_mapping['class_name'].values\nprint(\"Disease LABELS :\", disease_labels)\nprint('-'*100)\n\nprint(\"Now converting into one hot vector format\")\n### Convert the csv into image_id -> 0,0,0, ..... 1., 0, 1 (15 labels format)\n\n# Convert class_id to string to enable one-hot encoding\ntrain_data['class_id'] = train_data['class_id'].astype(str)\n\n# Perform one-hot encoding to convert class IDs into binary columns\none_hot_encoded = pd.get_dummies(train_data['class_id'])\n\n# Convert boolean values to integers (0 for False, 1 for True)\none_hot_encoded = one_hot_encoded.astype(int)\n\n# Concatenate one-hot encoded columns with original DataFrame\ntrain_data = pd.concat([train_data, one_hot_encoded], axis=1)\n# print(train_data.columns)\n\n# Group by image ID and aggregate the one-hot encoded class columns for each class separately\ntrain_data = train_data.groupby('image_id').agg({\n#     'width': 'first',\n#     'height': 'first',\n    '0': 'max', '1': 'max', '2': 'max', '3': 'max', '4': 'max',\n    '5': 'max', '6': 'max', '7': 'max', '8': 'max', '9': 'max',\n    '10': 'max', '11': 'max', '12': 'max', '13': 'max', '14': 'max'\n}).reset_index()\n\n\n\nprint(\"UNIQUE images IN THE DATASET:\", len(train_data.image_id.unique()))\nprint(\"num rows in dataset  :\", len(train_data))\n# train_data.head()\n\n# Count occurrences of each class\nclass_counts = train_data.iloc[:, 1:].sum()\nprint(\"TOTAL Individual class counts:-\\n\", class_counts)\nprint('-'*100)\n\n\n# the 14 th class is no finding...\n\n\n# Save the new DataFrame to a new CSV file\n# train_data.to_csv(\"one_hot_vector_data.csv\", index=False)\n\n\n# Check if 'no-finding' class is marked while any other abnormality class is also marked\n# for index, row in train_data.iterrows():\n#     if row['14'] == 1 and row.iloc[1:].sum() > 1:\n#         print(\"Issue found in row:\", index)\n#         print(\"Other abnormality classes also marked in this row.\")\n\n\n\n# Rename the one-hot encoded columns\ntrain_data = train_data.rename(columns=dict(zip(train_data.columns[1:], disease_labels)))\nprint(\"Renamed the columns from numbers to disease_label names\")\n\n\n### Removing the 'no finding label' ###\ntrain_data = delete_columns(train_data, columns_to_keep=columns_to_keep, root_folder='train')\ntrain_data = remove_all_zeros(train_data, diseases=diseases, root_folder='train')\n\n\n\n## Adding file_path column\ntrain_data = add_file_path_column(train_data, root_folder='/kaggle/input/vinbigdata-512-voi-clahe/vinbigdata-512-resized-clahe2-8,8_train')\n\n# Split 10% of the training data as validation data, with random_state=42, for reproducibility\ntrain_data, val_data = train_test_split(train_data, test_size=0.2, random_state=42)\nval_data, test_data = train_test_split(val_data, test_size=0.5,random_state=42)\n\n\n# reducing the size (comment this)\n# train_data = train_data.head(500)\n# val_data = val_data.head(500)\n# test_data = test_data.head(500)\n\n# Resetting the index, very important\ntrain_data.reset_index(drop=True, inplace=True)\nval_data.reset_index(drop=True, inplace=True)\ntest_data.reset_index(drop=True, inplace=True)\n\n### ADJUST diseases & disease_labels ###\n# collecting & storing the labels separately\n\n#adjust diseases or disease_labels\ntrain_paths = train_data['file_path'].values\ntrain_labels = train_data[diseases].values\n\nval_paths = val_data['file_path'].values\nval_labels = val_data[diseases].values\n\ntest_paths = test_data['file_path'].values\ntest_labels = test_data[diseases].values\n\n\n\nprint(\"length of train:\", len(train_data), len(train_paths), len(train_labels))\nprint(\"length of val:\", len(val_data), len(val_data), len(val_data))\nprint(\"length of test:\", len(test_data), len(test_data), len(test_data))\n\n\nprint(\"Unique image_ids in train:\", len(train_data['image_id'].unique()))\nprint(\"Unique image_ids in val:\", len(val_data['image_id'].unique()))\nprint(\"Unique image_ids in test:\", len(test_data['image_id'].unique()))\n\n# train_data.info() except image_id all the other columns are of int datatype\n\n","metadata":{"papermill":{"duration":0.392919,"end_time":"2024-03-26T18:39:30.673449","exception":false,"start_time":"2024-03-26T18:39:30.28053","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2024-04-10T10:20:16.815687Z","iopub.execute_input":"2024-04-10T10:20:16.816064Z","iopub.status.idle":"2024-04-10T10:20:17.160181Z","shell.execute_reply.started":"2024-04-10T10:20:16.816022Z","shell.execute_reply":"2024-04-10T10:20:17.159212Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Count occurrences of each class\nclass_counts = train_data.iloc[:, 1:-1].sum()\nprint(\"TRAIN Individual class counts:-\\n\", class_counts)\n\n# Count occurrences of each class\nclass_counts = val_data.iloc[:, 1:-1].sum()\nprint(\"VAL Individual class counts:-\\n\", class_counts)\n\n# Count occurrences of each class\nclass_counts = test_data.iloc[:, 1:-1].sum()\nprint(\"TEST Individual class counts:-\\n\", class_counts)","metadata":{"papermill":{"duration":0.02778,"end_time":"2024-03-26T18:39:30.715166","exception":false,"start_time":"2024-03-26T18:39:30.687386","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2024-04-10T10:20:17.16158Z","iopub.execute_input":"2024-04-10T10:20:17.161919Z","iopub.status.idle":"2024-04-10T10:20:17.172551Z","shell.execute_reply.started":"2024-04-10T10:20:17.161891Z","shell.execute_reply":"2024-04-10T10:20:17.171516Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_data.head()","metadata":{"papermill":{"duration":0.034975,"end_time":"2024-03-26T18:39:30.763917","exception":false,"start_time":"2024-03-26T18:39:30.728942","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2024-04-10T10:20:23.566085Z","iopub.execute_input":"2024-04-10T10:20:23.566877Z","iopub.status.idle":"2024-04-10T10:20:23.584747Z","shell.execute_reply.started":"2024-04-10T10:20:23.566846Z","shell.execute_reply":"2024-04-10T10:20:23.583864Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"val_data.head()","metadata":{"papermill":{"duration":0.030583,"end_time":"2024-03-26T18:39:30.808584","exception":false,"start_time":"2024-03-26T18:39:30.778001","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2024-04-10T10:20:24.485598Z","iopub.execute_input":"2024-04-10T10:20:24.48639Z","iopub.status.idle":"2024-04-10T10:20:24.501266Z","shell.execute_reply.started":"2024-04-10T10:20:24.486356Z","shell.execute_reply":"2024-04-10T10:20:24.500089Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"test_data.head()","metadata":{"papermill":{"duration":0.031117,"end_time":"2024-03-26T18:39:30.854238","exception":false,"start_time":"2024-03-26T18:39:30.823121","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2024-04-10T10:20:25.564256Z","iopub.execute_input":"2024-04-10T10:20:25.56495Z","iopub.status.idle":"2024-04-10T10:20:25.581036Z","shell.execute_reply.started":"2024-04-10T10:20:25.564918Z","shell.execute_reply":"2024-04-10T10:20:25.579883Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Visualising the images","metadata":{"papermill":{"duration":0.014869,"end_time":"2024-03-26T18:39:30.884307","exception":false,"start_time":"2024-03-26T18:39:30.869438","status":"completed"},"tags":[]}},{"cell_type":"code","source":"train_image_files = [\n    os.path.join('/', train_paths[i]) for i in range(len(train_paths))\n]\nprint(len(train_image_files))\n\nval_image_files = [\n    os.path.join('/', val_paths[i]) for i in range(len(val_paths))\n]\nprint(len(val_image_files))\n\ntest_image_files = [\n    os.path.join('/', test_paths[i]) for i in range(len(test_paths))\n]\nprint(len(test_image_files))","metadata":{"papermill":{"duration":0.049028,"end_time":"2024-03-26T18:39:30.948027","exception":false,"start_time":"2024-03-26T18:39:30.898999","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2024-04-10T10:20:27.555685Z","iopub.execute_input":"2024-04-10T10:20:27.5566Z","iopub.status.idle":"2024-04-10T10:20:27.571412Z","shell.execute_reply.started":"2024-04-10T10:20:27.556567Z","shell.execute_reply":"2024-04-10T10:20:27.570149Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import numpy as np\nimport pydicom\nfrom pydicom.pixel_data_handlers.util import apply_voi_lut\n\nimport matplotlib.pyplot as plt\n%matplotlib inline\n\n\ndef read_xray(path, voi_lut = True, fix_monochrome = True, apply_clahe=True, clipLimit=2.0, tileGridSize=(8,8)):\n    dicom = pydicom.read_file(path)\n    \n    # VOI LUT (if available by DICOM device) is used to transform raw DICOM data to \"human-friendly\" view\n    if voi_lut:\n        data = apply_voi_lut(dicom.pixel_array, dicom)\n    else:\n        data = dicom.pixel_array\n    \n    # depending on this value, X-ray may look inverted - fix that:\n    if fix_monochrome and dicom.PhotometricInterpretation == \"MONOCHROME1\":\n        data = np.amax(data) - data\n        \n    data = data - np.min(data)\n    data = data / np.max(data)\n#     data = data.astype(np.float32)\n#     data = (data * 255.0).astype(np.float32) # no need for this I think \n\n    if apply_clahe:\n        data = apply_clahe_to_image(data, clipLimit=clipLimit, tileGridSize=tileGridSize)\n        \n    return data\n\n\n\ndef apply_clahe_to_image(image, clipLimit=2.0, tileGridSize=(8,8)):\n    # Convert image to uint16\n    image = (image * 65535).astype(np.uint16)\n    \n    # Create a CLAHE object\n    clahe = cv2.createCLAHE(clipLimit=clipLimit, tileGridSize=tileGridSize)\n    \n    # Apply CLAHE\n    clahe_image = clahe.apply(image)\n    \n    # Convert image back to float32 in range [0, 1]\n    clahe_image = clahe_image.astype(np.float32) / 65535.0\n    \n    return clahe_image\n\n","metadata":{"papermill":{"duration":0.030672,"end_time":"2024-03-26T18:39:30.993912","exception":false,"start_time":"2024-03-26T18:39:30.96324","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2024-04-10T10:20:29.211229Z","iopub.execute_input":"2024-04-10T10:20:29.211864Z","iopub.status.idle":"2024-04-10T10:20:29.227641Z","shell.execute_reply.started":"2024-04-10T10:20:29.211834Z","shell.execute_reply":"2024-04-10T10:20:29.226699Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def plot_images(images, titles, rows, cols):\n    fig, axes = plt.subplots(rows, cols, figsize=(9, 9))\n    for i, (image, title) in enumerate(zip(images, titles)):\n        ax = axes[i // cols, i % cols]\n        ax.imshow(image, cmap='gray')\n        ax.set_title(title)\n        ax.axis('off')\n    plt.show()\n\n\npath = '/kaggle/input/vinbigdata-chest-xray-abnormalities-detection/train/0005e8e3701dfb1dd93d53e2ff537b6e.dicom'\n# path = '/kaggle/input/vinbigdata-chest-xray-abnormalities-detection/train/0007d316f756b3fa0baea2ff514ce945.dicom'\n# path = '/kaggle/input/vinbigdata-chest-xray-abnormalities-detection/train/003cfe5ce5c0ec5163138eb3b740e328.dicom'\n# path = '/kaggle/input/vinbigdata-chest-xray-abnormalities-detection/train/0076d6a1e3139927fd62459c54276c3c.dicom'\n# path = '/kaggle/input/vinbigdata-chest-xray-abnormalities-detection/train/0101ad90f31ddb8fb24e9935a3dac9db.dicom'\n\n\nclipLimits = [2.0, 3.0]\ntileGridSizes = [(6,6),(8,8)]\nimages = []\ntitles = []\n\n\nfor clipLimit in clipLimits:\n    for tileSize in tileGridSizes:\n        images.append(read_xray(path, clipLimit=clipLimit, tileGridSize=tileSize))\n        titles.append(f\"clipLimit={clipLimit}, tileGridSize={tileSize}\")\n    \ntitles = [f\"CL={clipLimit}, GS={tileSize}\" for clipLimit in clipLimits for tileSize in tileGridSizes]\nplot_images(images, titles, len(clipLimits), len(tileGridSizes))","metadata":{"papermill":{"duration":4.125995,"end_time":"2024-03-26T18:39:35.134647","exception":false,"start_time":"2024-03-26T18:39:31.008652","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2024-04-10T10:20:30.590507Z","iopub.execute_input":"2024-04-10T10:20:30.591328Z","iopub.status.idle":"2024-04-10T10:20:34.68092Z","shell.execute_reply.started":"2024-04-10T10:20:30.591298Z","shell.execute_reply":"2024-04-10T10:20:34.680005Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"img = read_xray(path, voi_lut=False, apply_clahe=False)\nplt.figure(figsize = (8,8))\n# plt.imshow(img, cmap='bone')\nplt.imshow(img, cmap='gray')","metadata":{"papermill":{"duration":1.362619,"end_time":"2024-03-26T18:39:36.51676","exception":false,"start_time":"2024-03-26T18:39:35.154141","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2024-04-10T10:20:34.682892Z","iopub.execute_input":"2024-04-10T10:20:34.683259Z","iopub.status.idle":"2024-04-10T10:20:35.976217Z","shell.execute_reply.started":"2024-04-10T10:20:34.683228Z","shell.execute_reply":"2024-04-10T10:20:35.975206Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"img = read_xray(path, voi_lut=True, apply_clahe=False)\nplt.figure(figsize = (8,8))\n# plt.imshow(img, cmap='bone')\nplt.imshow(img, cmap='gray')","metadata":{"papermill":{"duration":1.486622,"end_time":"2024-03-26T18:39:38.025894","exception":false,"start_time":"2024-03-26T18:39:36.539272","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2024-04-10T10:20:35.977611Z","iopub.execute_input":"2024-04-10T10:20:35.977944Z","iopub.status.idle":"2024-04-10T10:20:37.429931Z","shell.execute_reply.started":"2024-04-10T10:20:35.977916Z","shell.execute_reply":"2024-04-10T10:20:37.429046Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# the best configuration\nimg = read_xray(path, apply_clahe=True, clipLimit=2.0, tileGridSize=(8,8))\nplt.figure(figsize = (8,8))\n# plt.imshow(img, cmap='bone')\nplt.imshow(img, cmap='gray')","metadata":{"papermill":{"duration":1.495401,"end_time":"2024-03-26T18:39:39.547959","exception":false,"start_time":"2024-03-26T18:39:38.052558","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2024-04-10T10:20:37.431847Z","iopub.execute_input":"2024-04-10T10:20:37.432381Z","iopub.status.idle":"2024-04-10T10:20:38.861937Z","shell.execute_reply.started":"2024-04-10T10:20:37.432346Z","shell.execute_reply":"2024-04-10T10:20:38.861009Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"#### The preprocessed saved images:-","metadata":{"papermill":{"duration":0.030494,"end_time":"2024-03-26T18:39:39.610165","exception":false,"start_time":"2024-03-26T18:39:39.579671","status":"completed"},"tags":[]}},{"cell_type":"code","source":"import pydicom\nimport numpy as np\nimport matplotlib.pyplot as plt\nimport cv2\n\n# Assuming train_image_files is a list of DICOM file paths\n# Example:\n# train_image_files = [\"path/to/dicom/file1.dcm\", \"path/to/dicom/file2.dcm\", ...]\n\nfor i, k in enumerate(np.random.randint(len(train_image_files), size=9)):\n\n\n#     print(\"Image path\", train_image_files[k])\n    img_mat = np.load(train_image_files[k])\n\n#     img_mat = cv2.imread(train_image_files[k])\n# #     img_mat = cv2.resize(img_mat, (224, 224))\n#     img_black = cv2.cvtColor(img_mat, cv2.COLOR_BGR2GRAY)\n    float64_image_array = img_mat.astype(np.float64)\n\n    \n    print(\"image_shape:\", img_mat.shape)\n    \n    # Visualization using Matplotlib\n    plt.subplot(3, 3, i + 1)\n    plt.imshow(float64_image_array, cmap='gray')  # Assuming grayscale DICOM images\n    plt.title(f\"Image {i + 1}\")\n    plt.axis('off')\n\nplt.show()\n","metadata":{"papermill":{"duration":0.877317,"end_time":"2024-03-26T18:39:40.516851","exception":false,"start_time":"2024-03-26T18:39:39.639534","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2024-04-10T10:20:40.195802Z","iopub.execute_input":"2024-04-10T10:20:40.196514Z","iopub.status.idle":"2024-04-10T10:20:41.13444Z","shell.execute_reply.started":"2024-04-10T10:20:40.196479Z","shell.execute_reply":"2024-04-10T10:20:41.133466Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"BATCH_SIZE = 8\nIMAGE_SIZE = (224,224) # the size to which the image would be resized before passing to the model\n# num_classes = len(diseases)\nnum_classes = len(train_data.columns) - 2 \n## ADJUST num_classes accordingly \n\nprint(\"BATCH_SIZE: \", BATCH_SIZE) \nprint(\"IMAGE_SIZE: \", IMAGE_SIZE) \nprint(\"num_classes: \", num_classes) ","metadata":{"papermill":{"duration":0.041129,"end_time":"2024-03-26T18:39:40.592071","exception":false,"start_time":"2024-03-26T18:39:40.550942","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2024-04-10T10:20:41.136013Z","iopub.execute_input":"2024-04-10T10:20:41.136388Z","iopub.status.idle":"2024-04-10T10:20:41.14257Z","shell.execute_reply.started":"2024-04-10T10:20:41.136361Z","shell.execute_reply":"2024-04-10T10:20:41.141477Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from monai.transforms import Compose, Lambda, EnsureChannelFirst, ScaleIntensity,\\\n RandRotate, RandFlip, RandZoom, RandSpatialCrop, RandRotate90, ResizeWithPadOrCrop, Resize\n\ndef load_tensor(data):\n    # Convert the data to a supported data type (e.g., float32)\n    return data.astype(np.float32)\n\n\ntrain_transforms = Compose(\n    [\n#         LoadImage(image_only=True), \n        Lambda(load_tensor),\n        EnsureChannelFirst(channel_dim=0),\n#         RandSpatialCrop(IMAGE_SIZE[0],IMAGE_SIZE[1]), random_size=False), # crops randomly ; loss of information\n        Resize(spatial_size=(IMAGE_SIZE[0],IMAGE_SIZE[1])),\n#         ResizeWithPadOrCrop(spatial_size=IMAGE_SIZE), # this will centrally crop : loss of information\n#         RandRotate90(prob=0.5, spatial_axes=(0, 1)),\n        ScaleIntensity(),\n#         RandRotate(range_x=np.pi / 12, prob=0.5, keep_size=True),\n#         RandFlip(spatial_axis=0, prob=0.5),\n        RandZoom(min_zoom=0.9, max_zoom=1.1, prob=0.5),\n    ]\n)\n\nval_transforms = Compose(\n    [\n#         LoadImage(image_only=True),\n        Lambda(load_tensor),\n        EnsureChannelFirst(channel_dim=0),\n#         ResizeWithPadOrCrop(spatial_size=IMAGE_SIZE), # this will centrally crop : loss of information\n        Resize(spatial_size=(IMAGE_SIZE[0],IMAGE_SIZE[1])),\n\n        ScaleIntensity(),\n\n    ]\n)\n\n# y_pred_trans = Compose([Activations(softmax=True)])\n# y_trans = Compose([AsDiscrete(to_onehot=num_class)])\n\ny_pred_trans = Compose([Activations(sigmoid=True)]) # for multi-label classfication\ny_trans = Compose([AsDiscrete(threshold_values=True)])\n","metadata":{"papermill":{"duration":0.047102,"end_time":"2024-03-26T18:39:40.670556","exception":false,"start_time":"2024-03-26T18:39:40.623454","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2024-04-10T10:20:42.043536Z","iopub.execute_input":"2024-04-10T10:20:42.044004Z","iopub.status.idle":"2024-04-10T10:20:42.058261Z","shell.execute_reply.started":"2024-04-10T10:20:42.043972Z","shell.execute_reply":"2024-04-10T10:20:42.05722Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class VINDR_BigData_Dataset(torch.utils.data.Dataset):\n    def __init__(self, paths, labels, in_chans=1, transforms=None):\n        self.paths = paths\n        self.labels = labels\n        self.transforms = transforms\n        self.target_size = IMAGE_SIZE\n        self.in_chans = in_chans\n        \n    def __len__(self):\n        return len(self.paths)\n    \n    def __getitem__(self, index):\n#         print(index)\n        img_path = self.paths[index]\n#         print(img_path, index)\n        labels = self.labels[index]\n#         image = read_xray(img_path, voi_lut=False, fix_monochrome=False, normalize=False)\n        \n#         img_mat = cv2.imread(img_path)\n#         img_black = cv2.cvtColor(img_mat, cv2.COLOR_BGR2GRAY)\n        \n        image = np.load(img_path)\n        \n        # Expand the dimensions to add a channel dimension\n        image = np.expand_dims(image, axis=0)\n        \n        if self.transforms:\n            image = self.transforms(image)\n        \n        return image, labels\n\n\ntrain_ds = VINDR_BigData_Dataset(train_paths, train_labels, in_chans=1, transforms=train_transforms)\ntrain_loader = DataLoader(train_ds, batch_size=BATCH_SIZE, shuffle=True, num_workers=4)\n\nval_ds = VINDR_BigData_Dataset(val_paths, val_labels, in_chans=1, transforms=val_transforms)\nval_loader = DataLoader(val_ds, batch_size=BATCH_SIZE, num_workers=4)\n\ntest_ds = VINDR_BigData_Dataset(test_paths, test_labels, in_chans=1, transforms=val_transforms)\ntest_loader = DataLoader(test_ds, batch_size=BATCH_SIZE, num_workers=4)\n\nprint(\"No. of TRAIN batches:\", len(train_loader))\nprint(\"No. of VAL batches:\", len(val_loader))\nprint(\"No. of TEST batches:\", len(test_loader))\n","metadata":{"papermill":{"duration":0.04539,"end_time":"2024-03-26T18:39:40.747279","exception":false,"start_time":"2024-03-26T18:39:40.701889","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2024-04-10T10:20:43.140338Z","iopub.execute_input":"2024-04-10T10:20:43.140725Z","iopub.status.idle":"2024-04-10T10:20:43.154107Z","shell.execute_reply.started":"2024-04-10T10:20:43.140695Z","shell.execute_reply":"2024-04-10T10:20:43.152957Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from thop import profile\nfrom thop import clever_format\nimport torch\n\ndef display_params_flops(model):\n    #params\n    num_params = sum(p.numel() for p in model.parameters())\n    num_params_millions = num_params / 1e6\n    print(f\"Number of parameters in millions: {num_params_millions:.2f} M\")\n\n    num_params = sum(p.numel() for p in model.parameters() if p.requires_grad)\n    num_params_millions = num_params / 1e6\n    print(f\"Number of trainable parameters in millions: {num_params_millions:.2f} M\")\n\n\n    #FLOPS\n    input_size = (1, 1, IMAGE_SIZE[0], IMAGE_SIZE[1])  \n\n\n    # Move the model to GPU if available\n    if torch.cuda.is_available():\n        model = model.cuda()\n\n    # Use thop.profile to count FLOPs\n    input_tensor = torch.randn(*input_size)\n    if torch.cuda.is_available():\n        input_tensor = input_tensor.cuda()\n    flops, params = profile(model, inputs=(input_tensor,))\n\n    # Convert FLOPs to gigaFLOPs and format the results\n    flops, params = clever_format([flops, params], \"%.2f\")\n    print(f\"FLOPs: {flops}, Params: {params}\")\n    ","metadata":{"execution":{"iopub.status.busy":"2024-04-10T10:20:43.946418Z","iopub.execute_input":"2024-04-10T10:20:43.946773Z","iopub.status.idle":"2024-04-10T10:20:43.958831Z","shell.execute_reply.started":"2024-04-10T10:20:43.946744Z","shell.execute_reply":"2024-04-10T10:20:43.957883Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Model Definition","metadata":{"papermill":{"duration":0.030913,"end_time":"2024-03-26T18:39:40.809626","exception":false,"start_time":"2024-03-26T18:39:40.778713","status":"completed"},"tags":[]}},{"cell_type":"code","source":"import timm\nimport torch.nn as nn\nfrom monai.networks.blocks.dynunet_block import UnetOutBlock\nfrom monai.networks.blocks.unetr_block import UnetrBasicBlock, UnetrPrUpBlock, UnetrUpBlock\n\n\nclass Swin_Unetr(nn.Module):\n    def __init__(self, num_classes, pretrained=True, fine_tune=False):\n        super(Swin_Unetr, self).__init__()\n        \n        self.swin = timm.create_model(\n            'swin_large_patch4_window7_224.ms_in22k', \n            pretrained=True,\n            num_classes=num_classes,\n            in_chans=1,\n            features_only=True\n        )\n        \n        \n        if not fine_tune:\n            for param in self.swin.parameters():\n                param.requires_grad = False\n        \n        \n        spatial_dims = 2 \n        in_channels = 1 # R,G,B\n        feature_size = 192\n        norm_name = \"instance\"\n        hidden_size = 192\n        res_block = True\n        conv_block = False\n\n        self.encoder1 = UnetrBasicBlock(\n            spatial_dims=spatial_dims,\n            in_channels=in_channels,\n            out_channels=feature_size,\n            kernel_size=3,\n            stride=2,\n            norm_name=norm_name,\n            res_block=res_block,\n        )\n        self.encoder2 = UnetrPrUpBlock(\n            spatial_dims=spatial_dims,\n            in_channels=hidden_size,\n            out_channels=feature_size * 2,\n            num_layer=2,\n            kernel_size=3,\n            stride=1,\n            upsample_kernel_size=1,\n            norm_name=norm_name,\n            conv_block=conv_block,\n            res_block=res_block,\n        )\n        self.encoder3 = UnetrPrUpBlock(\n            spatial_dims=spatial_dims,\n            in_channels=hidden_size*2,\n            out_channels=feature_size * 4,\n            num_layer=1,\n            kernel_size=3,\n            stride=1,\n            upsample_kernel_size=1,\n            norm_name=norm_name,\n            conv_block=conv_block,\n            res_block=res_block,\n        )\n        self.encoder4 = UnetrPrUpBlock(\n            spatial_dims=spatial_dims,\n            in_channels=hidden_size*4,\n            out_channels=feature_size * 8,\n            num_layer=0,\n            kernel_size=3,\n            stride=1,\n            upsample_kernel_size=1,\n            norm_name=norm_name,\n            conv_block=conv_block,\n            res_block=res_block,\n        )\n        self.decoder5 = UnetrUpBlock(\n            spatial_dims=spatial_dims,\n            in_channels=hidden_size * 8,\n            out_channels=feature_size * 8,\n            kernel_size=3,\n            upsample_kernel_size=2,\n            norm_name=norm_name,\n            res_block=res_block,\n        )\n        self.decoder4 = UnetrUpBlock(\n            spatial_dims=spatial_dims,\n            in_channels=feature_size * 8,\n            out_channels=feature_size * 4,\n            kernel_size=3,\n            upsample_kernel_size=2,\n            norm_name=norm_name,\n            res_block=res_block,\n        )\n        self.decoder3 = UnetrUpBlock(\n            spatial_dims=spatial_dims,\n            in_channels=feature_size * 4,\n            out_channels=feature_size * 2,\n            kernel_size=3,\n            upsample_kernel_size=2,\n            norm_name=norm_name,\n            res_block=res_block,\n        )\n        self.decoder2 = UnetrUpBlock(\n            spatial_dims=spatial_dims,\n            in_channels=feature_size * 2,\n            out_channels=feature_size,\n            kernel_size=3,\n            upsample_kernel_size=2,\n            norm_name=norm_name,\n            res_block=res_block,\n        )\n        \n        self.conv = nn.Sequential(\n            nn.Conv2d(feature_size, 78, kernel_size=3, stride=2, padding=1),\n            nn.ReLU(),\n            nn.MaxPool2d(kernel_size=2, stride=2),\n            nn.Conv2d(78, 50, kernel_size=3, stride=2, padding=1),\n            nn.ReLU(),\n            nn.MaxPool2d(kernel_size=2, stride=2),\n        )\n        \n        # Classifier layer with convolution\n        self.classifier = nn.Sequential(\n            nn.Flatten(),\n            nn.Linear(2450, 1024),  # (DYNAMIC)Adjust the input size based on the output size of the convolutional layer\n            nn.ReLU(),\n            nn.Dropout(0.5),\n            nn.Linear(1024, num_classes)\n        )\n\n    \n    def forward(self, x_in):\n        \n        hidden_states_out = self.swin(x_in) # returns 4 lists\n#         print(\"Length of hidden states from Swin:\", len(hidden_states_out))\n#         for i in hidden_states_out:\n#             print(i.shape)\n#         print()\n        \n        ##we will permute each of the intermediate outputs from swin stages so that the channel comes first\n\n        hidden_states_out = [t.permute(0, 3, 1, 2) for t in hidden_states_out]\n        \n        \n        enc1 = self.encoder1(x_in)\n#         print(\"output from encoder1:\", enc1.shape)\n        \n        x2 = hidden_states_out[0]\n        enc2 = self.encoder2(x2)\n#         print(\"output from encoder2:\", enc2.shape)\n        \n        x3 = hidden_states_out[1]\n        enc3 = self.encoder3(x3)\n#         print(\"output from encoder3:\", enc3.shape)\n        \n        \n        x4 = hidden_states_out[2]\n        enc4 = self.encoder4(x4)\n#         print(\"output from encoder4:\", enc4.shape)\n        \n#         print(\"All encoders OK\\n\")\n        \n        dec4 = hidden_states_out[3]\n#         print(\"Input to decoder5:\", dec4.shape, enc4.shape)\n        dec3 = self.decoder5(dec4, enc4)\n#         print(\"output from decoder5:\", dec3.shape)\n        \n#         print(\"Input to decoder4:\", dec3.shape, enc3.shape)\n        dec2 = self.decoder4(dec3, enc3)\n#         print(\"output from decoder4:\", dec2.shape)\n        \n#         print(\"Input to decoder3:\", dec2.shape, enc2.shape)\n        dec1 = self.decoder3(dec2, enc2)\n#         print(\"output from decoder3:\", dec1.shape)\n        \n#         print(\"Input to decoder2:\", dec1.shape, enc1.shape)\n        out = self.decoder2(dec1, enc1) \n#         print(\"output from decoder2:\", out.shape)\n        \n\n        \n        conv_out = self.conv(out)\n#         print(f\"conv_out_shape:{conv_out.shape}\")\n\n        return self.classifier(conv_out)\n\n\n\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\nmodel = Swin_Unetr(num_classes, fine_tune=False)\nmodel.to(device)\n\n# print(model)\nprint()\n\nx = torch.randn(1, 1, IMAGE_SIZE[0], IMAGE_SIZE[1]).to(device)\n\n\noutput = model(x)\nprint(\"Model output's shape:\", output.shape)\nprint(output) # logits \ndisplay_params_flops(model)","metadata":{"execution":{"iopub.status.busy":"2024-04-10T10:20:45.835967Z","iopub.execute_input":"2024-04-10T10:20:45.836367Z","iopub.status.idle":"2024-04-10T10:20:58.537224Z","shell.execute_reply.started":"2024-04-10T10:20:45.836337Z","shell.execute_reply":"2024-04-10T10:20:58.5362Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model = Swin_Unetr(num_classes, fine_tune=False) # change fine_tune as required\nmodel.to(device)\n\n\nloss_function = torch.nn.BCEWithLogitsLoss()\noptimizer = torch.optim.Adam(model.parameters(), 3e-5) # adjust learning rate \n# reduce lr during fine tuning","metadata":{"papermill":{"duration":1.973611,"end_time":"2024-03-26T18:39:49.325262","exception":false,"start_time":"2024-03-26T18:39:47.351651","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2024-04-10T10:21:00.386083Z","iopub.execute_input":"2024-04-10T10:21:00.386886Z","iopub.status.idle":"2024-04-10T10:21:06.030683Z","shell.execute_reply.started":"2024-04-10T10:21:00.386854Z","shell.execute_reply":"2024-04-10T10:21:06.029824Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import torch\nimport torch.nn as nn\n\n# Assuming your model outputs logits\nlogits = torch.randn(3, num_classes)  # Example logits\ntargets = torch.randint(0, 2, (3, num_classes))  # Example targets (binary, one-hot encoded)\nprint(logits)\nprint(targets)\n\n# Define the loss function\ncriterion = nn.BCEWithLogitsLoss()\n\n# Calculate the loss\nloss = criterion(logits, targets.float())\nprint(\"loss:\", loss)","metadata":{"papermill":{"duration":0.068876,"end_time":"2024-03-26T18:39:49.430899","exception":false,"start_time":"2024-03-26T18:39:49.362023","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2024-04-10T10:21:07.410757Z","iopub.execute_input":"2024-04-10T10:21:07.411393Z","iopub.status.idle":"2024-04-10T10:21:07.499377Z","shell.execute_reply.started":"2024-04-10T10:21:07.411356Z","shell.execute_reply":"2024-04-10T10:21:07.498313Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def train(model, loss_function=loss_function, optimizer=optimizer, num_epochs=20,\n          ranOnce=False, epochs_ran=0, model_path='model.pth', history_path='history.csv',\n         save_interval=1):\n    '''\n    returns the 'history' dataframe.\n    \n    This function trains the model for a fixed number of epochs,\n    saving the checkpoints regularly.\n    It monitors time taken per epoch and total time elapsed.\n    It tracks loss, roc_auc, f1, accuracy, precision, recall for both train & validation data.\n    It also saves the best model on loss, roc_auc, f1 and accuracy.\n    '''\n    \n    \n    initial_epoch = 0\n    \n    valid_loss_min = np.Inf\n    valid_max_accuracy = 0\n    valid_max_auc = 0\n    valid_max_precision = 0\n    valid_max_recall = 0\n    valid_max_f1 = 0\n    \n    \n    \n    if ranOnce:\n        if epochs_ran <= 0:\n            print(\"Mention the no. of epochs run by the model already for which you have the weights\")\n            return \n\n        history = pd.read_csv(history_path)\n        history=history.head(epochs_ran)\n        model.load_state_dict(torch.load(model_path))\n        '''\n        It has the following columns:-\n        epoch_number, train_loss, val_loss, train_auc, val_auc, train_accuracy, val_accuracy,\n        train_f1, val_f1, train_precision, val_precision, train_recall, val_recall,\n        time_current_epoch, total_time_elapsed\n        '''\n        initial_epoch = len(history)\n        \n        valid_loss_min = history['val_loss'].min()\n        valid_max_accuracy = history['val_accuracy'].max()\n        valid_max_auc = history['val_auc'].max()\n        valid_max_precision = history['val_precision'].max()\n        valid_max_recall = history['val_recall'].max()\n        valid_max_f1 = history['val_f1'].max()\n        \n        \n        print(f\"Model was already trained fo {initial_epoch} epochs,\\\n    with minimum loss: {valid_loss_min}, max accuracy: {valid_max_accuracy},\\\n    max auc: {valid_max_auc}, max precision: {valid_max_precision}, \\\n    max recall: {valid_max_recall}, max f1: {valid_max_f1}\")\n        \n    else:\n        print(\"Starting afresh!\")\n        history = pd.DataFrame()\n#         history = pd.DataFrame(columns=['epoch_number', 'train_loss', 'train_accuracy', 'train_f1',\\\n#             'train_precision', 'train_recall', 'train_auc', 'train_auc_scores',\\\n#             'val_loss', 'val_accuracy', 'val_f1', 'val_precision', 'val_recall', 'val_auc','val_auc_scores',\\\n#             'time_current_epoch'])\n        \n    # Main loop\n    for epoch in range(initial_epoch+1, initial_epoch + num_epochs+1):\n        \n        history_list = [] # stores the history for the current epoch in a list \n        \n        train_labels_all = []\n        train_predictions_all = []\n        train_scores_all = []\n        \n        train_loss = 0.0\n        correct_train = 0\n        total_train = 0\n        \n         # Set to training\n        model.train()\n        start = timer()\n        \n        # Training loop\n        for batch_data in tqdm(train_loader):\n            inputs, labels = batch_data[0].to(device), batch_data[1].float().to(device)  \n            \n            # Clear gradients\n            optimizer.zero_grad()\n\n            outputs = model(inputs)\n#             print(\"train_batch_outputs:\", outputs)\n#             print(\"train_batch_labels:\", labels)\n            \n            outputs = outputs.float()\n            labels = labels.float()\n\n            # Loss and backpropagation of gradients\n            loss = loss_function(outputs, labels)\n\n            loss.backward()\n            optimizer.step()  # Update the parameters\n            \n            train_loss += loss.item()\n            \n            # Calculate training accuracy\n            scores = torch.sigmoid(outputs)\n            predictions = torch.sigmoid(outputs) > 0.5\n            total_train += labels.size(0) * labels.size(1)\n            \n            correct_train += (predictions == labels).sum().item()\n            \n#             print(\"train_batch_predictions:\", predictions)\n#             print(\"train_batch_scores:\", scores)\n            \n            train_scores_all.extend(scores.detach().cpu().numpy())\n            train_labels_all.extend(labels.cpu().numpy())\n            train_predictions_all.extend(predictions.cpu().numpy())\n            \n#             train_labels_all.extend(labels)\n#             train_predictions_all.extend(predictions)\n            \n    \n        train_loss /= len(train_loader)\n        accuracy_train = correct_train / total_train\n        \n        print(f\"Current epoch {epoch}/{initial_epoch + num_epochs}\")\n        print(f\"Training Loss: {train_loss:.4f}, Accuracy: {accuracy_train:.4f}\")\n        print(\"correct:\", correct_train, \" out of \", total_train)\n\n        train_predictions_all = np.array(train_predictions_all).astype(float)\n        train_labels_all = np.array(train_labels_all).astype(float)\n        \n#         print(classification_report(train_labels_all, train_predictions_all, target_names=diseases))\n        \n        cm = multilabel_confusion_matrix(train_predictions_all, train_labels_all)\n        # we will use macro-averaging strategy.\n        accuracy_arr = []\n        precision_arr = []\n        recall_arr = []\n        f1_arr = []\n        \n        for i in range(num_classes):\n#             print(cm[i])\n#             print(cm[i].sum())\n            \n#             cfm_plot = sn.heatmap(cm[i], annot=False)\n             # TP + TN / TP + TN  + FP + FN\n            accuracy = (cm[i][0][0]+ cm[i][1][1])/cm[i].sum()\n\n            # TP / TP + FP\n            precision = cm[i][1][1]/(cm[i][1][0]+cm[i][1][1])\n\n            # TP / TP + FN\n            recall = cm[i][1][1]/(cm[i][0][1]+cm[i][1][1]) # sensitivity\n            f1 = (2*precision*recall)/(precision+recall)\n#             print(diseases[i],\": \",round(accuracy*100,2),\"%\")\n#             print(\"Precision: \",round(precision,2))\n#             print(\"Recall:\", round(recall,2))\n#             print(\"F1-Score:\", round(f1,2))\n#             print('==========================================================')\n\n\n            accuracy_arr.append(accuracy)\n            precision_arr.append(precision)\n            recall_arr.append(recall)\n            f1_arr.append(f1)\n        \n        \n        accuracy_arr = np.nan_to_num(accuracy_arr, nan=0)\n        precision_arr = np.nan_to_num(precision_arr, nan=0)\n        recall_arr = np.nan_to_num(recall_arr, nan=0)\n        f1_arr = np.nan_to_num(f1_arr, nan=0)\n        \n        accuracy_macro_train = round(sum(accuracy_arr) / len(accuracy_arr), 4)\n        precision_macro_train = round(sum(precision_arr) / len(precision_arr), 4)\n        recall_macro_train = round(sum(recall_arr) / len(recall_arr), 4)\n        f1_macro_train = round(sum(f1_arr) / len(f1_arr), 4)\n        roc_auc_macro_train = round(roc_auc_score(train_labels_all, train_scores_all, average='macro'), 4)\n\n        print(\"MACRO-averged metrics\", end=':- ')\n        print(f\"accuracy: {accuracy_macro_train}, precision: {precision_macro_train}\", end=', ')\n        print(f\"recall: {recall_macro_train}, f1: {f1_macro_train}, ROC_AUC: {roc_auc_macro_train}\")\n\n        \n        #VALIDATION START:->\n        val_labels_all = []\n        val_predictions_all = []\n        val_scores_all = []\n        \n        val_loss = 0.0\n        correct_val = 0\n        total_val = 0\n        \n        # Don't need to keep track of gradients\n        with torch.no_grad():\n            # Set to evaluation mode\n            model.eval()\n        \n            #Validation loop\n            for batch_data in tqdm(val_loader):\n                inputs, labels = batch_data[0].to(device), batch_data[1].float().to(device)  \n\n                outputs = model(inputs)\n                outputs = outputs.float()\n                labels = labels.float()\n                \n                loss = loss_function(outputs, labels)                \n                val_loss += loss.item()\n\n                scores = torch.sigmoid(outputs)\n                predictions = torch.sigmoid(outputs) > 0.5\n                total_val += labels.size(0) * labels.size(1)\n                \n                correct_val += (predictions == labels).sum().item()\n                \n                val_scores_all.extend(scores.detach().cpu().numpy())\n                val_labels_all.extend(labels.cpu().numpy())\n                val_predictions_all.extend(predictions.cpu().numpy())\n                \n\n            val_loss /= len(val_loader)\n            accuracy_val = correct_val / total_val\n\n            print(f\"Validation Loss: {val_loss:.4f}, Accuracy: {accuracy_val:.4f}\")\n            print(\"correct:\", correct_val, \" out of \", total_val)\n\n            val_predictions_all = np.array(val_predictions_all).astype(float)\n            val_labels_all = np.array(val_labels_all).astype(float)\n\n    #         print(classification_report(val_labels_all, val_predictions_all, target_names=diseases))\n\n            cm = multilabel_confusion_matrix(val_predictions_all, val_labels_all)\n            # we will use macro-averaging strategy.\n            accuracy_arr = []\n            precision_arr = []\n            recall_arr = []\n            f1_arr = []\n\n            for i in range(num_classes):\n    #             print(cm[i])\n    #             print(cm[i].sum())\n\n    #             cfm_plot = sn.heatmap(cm[i], annot=False)\n#                 TP + TN / TP + TN  + FP + FN\n                accuracy = (cm[i][0][0]+ cm[i][1][1])/cm[i].sum()\n\n                # TP / TP + FP\n                precision = cm[i][1][1]/(cm[i][1][0]+cm[i][1][1])\n\n                # TP / TP + FN\n                recall = cm[i][1][1]/(cm[i][0][1]+cm[i][1][1]) # sensitivity\n                f1 = (2*precision*recall)/(precision+recall)\n    #             print(diseases[i],\": \",round(accuracy*100,2),\"%\")\n    #             print(\"Precision: \",round(precision,2))\n    #             print(\"Recall:\", round(recall,2))\n    #             print(\"F1-Score:\", round(f1,2))\n    #             print('==========================================================')\n\n        \n                accuracy_arr.append(accuracy)\n                precision_arr.append(precision)\n                recall_arr.append(recall)\n                f1_arr.append(f1)\n            \n            accuracy_arr = np.nan_to_num(accuracy_arr, nan=0)\n            precision_arr = np.nan_to_num(precision_arr, nan=0)\n            recall_arr = np.nan_to_num(recall_arr, nan=0)\n            f1_arr = np.nan_to_num(f1_arr, nan=0)\n            \n            accuracy_macro_val = round(sum(accuracy_arr) / len(accuracy_arr), 4)\n            precision_macro_val = round(sum(precision_arr) / len(precision_arr), 4)\n            recall_macro_val = round(sum(recall_arr) / len(recall_arr), 4)\n            f1_macro_val = round(sum(f1_arr) / len(f1_arr), 4)\n            roc_auc_macro_val = round(roc_auc_score(val_labels_all, val_scores_all, average='macro'), 4)\n            \n            print(\"MACRO-averged metrics\", end=':- ')\n            print(f\"accuracy: {accuracy_macro_val}, precision: {precision_macro_val}\", end=', ')\n            print(f\"recall: {recall_macro_val}, f1: {f1_macro_val}, ROC_AUC: {roc_auc_macro_val}\")\n\n            time_this_epoch = timer()-start\n            print(f\"Time_for_this_epoch: {(time_this_epoch):.4f} seconds\")\n            print(\"-\"*120)\n\n        \n#         Add values to the history DataFrame\n        history_list.append({\n            'epoch_number': epoch,\n            'train_loss': train_loss,\n            'train_accuracy': accuracy_macro_train,\n            'train_f1': f1_macro_train,\n            'train_precision': precision_macro_train,\n            'train_recall': recall_macro_train,\n            'train_auc': roc_auc_macro_train,\n            \n            'val_loss': val_loss,\n            'val_accuracy': accuracy_macro_val,\n            'val_f1': f1_macro_val,\n            'val_precision': precision_macro_val,\n            'val_recall': recall_macro_val,\n            'val_auc': roc_auc_macro_val,\n            \n            # Add other metrics as needed\n            'time_current_epoch': time_this_epoch\n        })\n        \n        # Convert the list of dictionaries to a DataFrame\n        epoch_history = pd.DataFrame(history_list)\n\n        # Concatenate the new DataFrame with the existing history DataFrame\n        history = pd.concat([history, epoch_history], ignore_index=True)\n        \n        history.to_csv('history.csv', index=False)\n#         print(\"history_list:\", history_list)\n        print('history should be saved')\n        \n        \n#         ### HANDLING THE MODEL SAVING MECHANISM\n# #       # Save the model with the best accuracy\n        if accuracy_macro_val > valid_max_accuracy:\n            valid_max_accuracy = accuracy_macro_val\n            best_model_accuracy = model.state_dict()\n            torch.save(best_model_accuracy, 'best_model_accuracy.pth')\n\n\n#         # Save the model with the best AUC\n        if roc_auc_macro_val > valid_max_auc:\n            valid_max_auc = roc_auc_macro_val\n            best_model_auc = model.state_dict()\n            torch.save(best_model_auc, 'best_model_auc.pth')\n\n\n#         # Save the model with the best validation loss\n        if val_loss < valid_loss_min:\n            valid_loss_min = val_loss\n            best_model_val_loss = model.state_dict()\n            torch.save(best_model_val_loss, 'best_model_val_loss.pth')\n            \n        # best precision\n        if precision_macro_val > valid_max_precision:\n            valid_max_precision = precision_macro_val\n            best_model_precision = model.state_dict()\n            torch.save(best_model_precision, 'best_model_precision.pth')\n        \n        # best recall \n        if recall_macro_val > valid_max_recall :\n            valid_max_recall  = recall_macro_val\n            best_model_recall = model.state_dict()\n            torch.save(best_model_recall, 'best_model_recall.pth')\n        \n        # best f1\n        if f1_macro_val > valid_max_f1 :\n            valid_max_f1  = f1_macro_val\n            best_model_f1 = model.state_dict()\n            torch.save(best_model_f1, 'best_model_f1.pth')\n            \n\n#         # Saving model every 'save_interval' number of epochs\n        if epoch % save_interval == 0:\n            print(f\"Saving model at epoch number: {epoch}\")\n            torch.save(model.state_dict(), f\"model_{epoch}.pth\")\n        \n\n        \n    return history","metadata":{"papermill":{"duration":0.083086,"end_time":"2024-03-26T18:39:49.549144","exception":false,"start_time":"2024-03-26T18:39:49.466058","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2024-04-10T10:21:09.510636Z","iopub.execute_input":"2024-04-10T10:21:09.511021Z","iopub.status.idle":"2024-04-10T10:21:09.559793Z","shell.execute_reply.started":"2024-04-10T10:21:09.51099Z","shell.execute_reply":"2024-04-10T10:21:09.558801Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#  history = train(model, loss_function=loss_function, optimizer=optimizer, num_epochs=10,\n#           ranOnce=False,\n#          save_interval=10)","metadata":{"papermill":{"duration":17107.713737,"end_time":"2024-03-26T23:24:57.298152","exception":false,"start_time":"2024-03-26T18:39:49.584415","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2024-04-09T15:32:18.198172Z","iopub.execute_input":"2024-04-09T15:32:18.199152Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Fine Tuning ( replace the model name )","metadata":{}},{"cell_type":"code","source":"model = Swin_Unetr(num_classes, fine_tune=True) # change fine_tune as required\nmodel.to(device)\n\n\nloss_function = torch.nn.BCEWithLogitsLoss()\noptimizer = torch.optim.Adam(model.parameters(), 7e-6) # adjust learning rate \n# reduce lr during fine tuning","metadata":{"execution":{"iopub.status.busy":"2024-04-10T10:21:20.74371Z","iopub.execute_input":"2024-04-10T10:21:20.744457Z","iopub.status.idle":"2024-04-10T10:21:26.051562Z","shell.execute_reply.started":"2024-04-10T10:21:20.744422Z","shell.execute_reply":"2024-04-10T10:21:26.050639Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# give the correct model & history paths & \"epochs_ran\" must be matching with model_{no.}\n\nhistory = train(model, loss_function=loss_function, optimizer=optimizer, num_epochs=30,\n          ranOnce=True, model_path='/kaggle/input/weights-april-9-swin-unetr-vinbigdata-ml/model_70.pth', history_path='/kaggle/input/weights-april-9-swin-unetr-vinbigdata-ml/history.csv',\n         epochs_ran=70, save_interval=10)","metadata":{"execution":{"iopub.status.busy":"2024-04-10T10:25:25.951563Z","iopub.execute_input":"2024-04-10T10:25:25.952314Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Testing Phase","metadata":{"papermill":{"duration":4.183496,"end_time":"2024-03-26T23:25:05.680441","exception":false,"start_time":"2024-03-26T23:25:01.496945","status":"completed"},"tags":[]}},{"cell_type":"code","source":"# set it to '/kaggle/working/history.csv' while running by SAVE and COMMIT\nhistory_path = '/kaggle/input/weights-april-9-swin-unetr-vinbigdata-ml/history.csv'\n# history_path = '/kaggle/input/20-03-2024-vinbigdata-multilabel-weights/history.csv'\nhistory = pd.read_csv(history_path)","metadata":{"papermill":{"duration":4.336153,"end_time":"2024-03-26T23:25:14.282245","exception":false,"start_time":"2024-03-26T23:25:09.946092","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2024-04-10T10:21:52.947346Z","iopub.execute_input":"2024-04-10T10:21:52.948427Z","iopub.status.idle":"2024-04-10T10:21:52.960526Z","shell.execute_reply.started":"2024-04-10T10:21:52.948392Z","shell.execute_reply":"2024-04-10T10:21:52.959501Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import pandas as pd\nimport matplotlib.pyplot as plt\n\n\n# Create subplots\nfig, axes = plt.subplots(3, 2, figsize=(15, 15))\n\n# Plot accuracy\naxes[0, 0].plot(history['epoch_number'], history['val_accuracy'], label='Validation Accuracy', color='blue')\naxes[0, 0].plot(history['epoch_number'], history['train_accuracy'], label='Training Accuracy', color='orange')\naxes[0, 0].set_xlabel('Epoch')\naxes[0, 0].set_ylabel('Accuracy')\naxes[0, 0].set_title('Validation Accuracy vs Training Accuracy')\naxes[0, 0].legend()\n\n# Plot AUC\naxes[0, 1].plot(history['epoch_number'], history['val_auc'], label='Validation AUC', color='green')\naxes[0, 1].plot(history['epoch_number'], history['train_auc'], label='Training AUC', color='red')\naxes[0, 1].set_xlabel('Epoch')\naxes[0, 1].set_ylabel('AUC')\naxes[0, 1].set_title('Validation AUC vs Training AUC')\naxes[0, 1].legend()\n\n# Plot loss\naxes[1, 0].plot(history['epoch_number'], history['val_loss'], label='Validation Loss', color='purple')\naxes[1, 0].plot(history['epoch_number'], history['train_loss'], label='Training Loss', color='brown')\naxes[1, 0].set_xlabel('Epoch')\naxes[1, 0].set_ylabel('Loss')\naxes[1, 0].set_title('Validation Loss vs Training Loss')\naxes[1, 0].legend()\n\n# Plot precision\naxes[1, 1].plot(history['epoch_number'], history['val_precision'], label='Validation Precision', color='cyan')\naxes[1, 1].plot(history['epoch_number'], history['train_precision'], label='Training Precision', color='magenta')\naxes[1, 1].set_xlabel('Epoch')\naxes[1, 1].set_ylabel('Precision')\naxes[1, 1].set_title('Validation Precision vs Training Precision')\naxes[1, 1].legend()\n\n# Plot recall\naxes[2, 0].plot(history['epoch_number'], history['val_recall'], label='Validation Recall', color='yellow')\naxes[2, 0].plot(history['epoch_number'], history['train_recall'], label='Training Recall', color='green')\naxes[2, 0].set_xlabel('Epoch')\naxes[2, 0].set_ylabel('Recall')\naxes[2, 0].set_title('Validation Recall vs Training Recall')\naxes[2, 0].legend()\n\n# Plot F1 score\naxes[2, 1].plot(history['epoch_number'], history['val_f1'], label='Validation F1 Score', color='orange')\naxes[2, 1].plot(history['epoch_number'], history['train_f1'], label='Training F1 Score', color='blue')\naxes[2, 1].set_xlabel('Epoch')\naxes[2, 1].set_ylabel('F1 Score')\naxes[2, 1].set_title('Validation F1 Score vs Training F1 Score')\naxes[2, 1].legend()\n\n# Adjust layout\nplt.tight_layout()\n\n# Show the plot\nplt.show()","metadata":{"papermill":{"duration":5.948189,"end_time":"2024-03-26T23:25:24.548768","exception":false,"start_time":"2024-03-26T23:25:18.600579","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2024-04-10T10:21:58.718053Z","iopub.execute_input":"2024-04-10T10:21:58.718933Z","iopub.status.idle":"2024-04-10T10:22:00.272947Z","shell.execute_reply.started":"2024-04-10T10:21:58.718901Z","shell.execute_reply":"2024-04-10T10:22:00.27195Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model_path = '/kaggle/input/weights-april-9-swin-unetr-vinbigdata-ml/best_model_precision.pth'\n# model_path = '/kaggle/input/20-03-2024-vinbigdata-multilabel-weights/best_model_auc.pth'\nmodel.load_state_dict(torch.load(model_path))","metadata":{"papermill":{"duration":4.665444,"end_time":"2024-03-26T23:25:33.446574","exception":false,"start_time":"2024-03-26T23:25:28.78113","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2024-04-10T10:22:13.157932Z","iopub.execute_input":"2024-04-10T10:22:13.158323Z","iopub.status.idle":"2024-04-10T10:22:23.40607Z","shell.execute_reply.started":"2024-04-10T10:22:13.158292Z","shell.execute_reply":"2024-04-10T10:22:23.405034Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"test_scores_all = []\ntest_labels_all = []\ntest_predictions_all = []\n\ncorrect_test = 0\ntotal_test = 0\n\n\n# Don't need to keep track of gradients\nwith torch.no_grad():\n    # Set to evaluation mode\n    model.eval()\n\n    start = timer()\n    #Test loop\n    for batch_data in tqdm(test_loader):\n        inputs, labels = batch_data[0].to(device), batch_data[1].float().to(device)  \n\n        outputs = model(inputs)\n        outputs = outputs.float()\n        labels = labels.float()\n\n\n        scores = torch.sigmoid(outputs)\n        predictions = torch.sigmoid(outputs) > 0.5\n        total_test += labels.size(0) * labels.size(1)\n\n        correct_test += (predictions == labels).sum().item()\n\n        test_scores_all.extend(scores.detach().cpu().numpy())\n        test_labels_all.extend(labels.cpu().numpy())\n        test_predictions_all.extend(predictions.cpu().numpy())\n\n    accuracy_test = correct_test / total_test\n\n    print(f\"Accuracy: {accuracy_test:.4f}\")\n    print(\"correct:\", correct_test, \" out of \", total_test)\n\n    test_predictions_all = np.array(test_predictions_all).astype(float)\n    test_labels_all = np.array(test_labels_all).astype(float)\n\n    print(classification_report(test_labels_all, test_predictions_all, target_names=diseases)) # adjust the target_names\n\n    cm = multilabel_confusion_matrix(test_predictions_all, test_labels_all)\n    # we will use macro-averaging strategy.\n    accuracy_arr = []\n    precision_arr = []\n    recall_arr = []\n    f1_arr = []\n\n    for i in range(num_classes):\n        print(cm[i])\n        print(cm[i].sum())\n\n#         cfm_plot = sn.heatmap(cm[i], annot=False)\n         # TP + TN / TP + TN  + FP + FN\n        accuracy = (cm[i][0][0]+ cm[i][1][1])/cm[i].sum()\n\n        # TP / TP + FP\n        precision = cm[i][1][1]/(cm[i][1][0]+cm[i][1][1])\n\n        # TP / TP + FN\n        recall = cm[i][1][1]/(cm[i][0][1]+cm[i][1][1]) # sensitivity\n        f1 = (2*precision*recall)/(precision+recall)\n        print(disease_labels[i],\": \",round(accuracy*100,2),\"%\") ### disease or disease_labels\n        print(\"Precision: \",round(precision,2))\n        print(\"Recall:\", round(recall,2))\n        print(\"F1-Score:\", round(f1,2))\n        print('==========================================================')\n\n\n        accuracy_arr.append(accuracy)\n        precision_arr.append(precision)\n        recall_arr.append(recall)\n        f1_arr.append(f1)\n    \n    accuracy_arr = np.nan_to_num(accuracy_arr, nan=0)\n    precision_arr = np.nan_to_num(precision_arr, nan=0)\n    recall_arr = np.nan_to_num(recall_arr, nan=0)\n    f1_arr = np.nan_to_num(f1_arr, nan=0)\n            \n    accuracy_arr = np.nan_to_num(accuracy_arr, nan=0)\n    precision_arr = np.nan_to_num(precision_arr, nan=0)\n    recall_arr = np.nan_to_num(recall_arr, nan=0)\n    f1_arr = np.nan_to_num(f1_arr, nan=0)\n    \n    accuracy_macro_test = round(sum(accuracy_arr) / len(accuracy_arr), 4)\n    precision_macro_test = round(sum(precision_arr) / len(precision_arr), 4)\n    recall_macro_test = round(sum(recall_arr) / len(recall_arr), 4)\n    f1_macro_test = round(sum(f1_arr) / len(f1_arr), 4)\n    roc_auc_macro_test = round(roc_auc_score(test_labels_all, test_scores_all, average='macro'), 4)\n\n    print(\"MACRO-averged metrics\", end=':- ')\n    print(f\"accuracy: {accuracy_macro_test}, precision: {precision_macro_test}\", end=', ')\n    print(f\"recall: {recall_macro_test}, f1: {f1_macro_test}, ROC_AUC: {roc_auc_macro_test}\")\n\n    infer_time = timer()-start\n    print(f\"INFERENCE TIME: {(infer_time):.4f} seconds\")\n    print(\"-\"*120)","metadata":{"papermill":{"duration":32.202575,"end_time":"2024-03-26T23:26:09.900952","exception":false,"start_time":"2024-03-26T23:25:37.698377","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2024-04-10T10:22:32.268005Z","iopub.execute_input":"2024-04-10T10:22:32.268398Z","iopub.status.idle":"2024-04-10T10:22:56.608735Z","shell.execute_reply.started":"2024-04-10T10:22:32.268368Z","shell.execute_reply":"2024-04-10T10:22:56.607625Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{"papermill":{"duration":4.224946,"end_time":"2024-03-26T23:26:18.511279","exception":false,"start_time":"2024-03-26T23:26:14.286333","status":"completed"},"tags":[]},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{"papermill":{"duration":4.29589,"end_time":"2024-03-26T23:26:26.991716","exception":false,"start_time":"2024-03-26T23:26:22.695826","status":"completed"},"tags":[]},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{"papermill":{"duration":4.188715,"end_time":"2024-03-26T23:26:35.381248","exception":false,"start_time":"2024-03-26T23:26:31.192533","status":"completed"},"tags":[]},"execution_count":null,"outputs":[]}]}