{"metadata":{"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.11.11"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceId":71549,"databundleVersionId":8561470,"isSourceIdPinned":false,"sourceType":"competition"},{"sourceId":11279674,"sourceType":"datasetVersion","datasetId":7051988},{"sourceId":11486673,"sourceType":"datasetVersion","datasetId":7199721},{"sourceId":231957574,"sourceType":"kernelVersion"},{"sourceId":992,"sourceType":"modelInstanceVersion","isSourceIdPinned":false,"modelInstanceId":846,"modelId":101}],"dockerImageVersionId":31011,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true},"papermill":{"default_parameters":{},"duration":3192.172565,"end_time":"2025-04-28T21:08:08.669936","environment_variables":{},"exception":null,"input_path":"__notebook__.ipynb","output_path":"__notebook__.ipynb","parameters":{},"start_time":"2025-04-28T20:14:56.497371","version":"2.6.0"}},"nbformat_minor":4,"nbformat":4,"cells":[{"id":"2cea6bfc","cell_type":"markdown","source":"https://www.kaggle.com/code/borabingol/fork-of-fixed-train-with-severe-focused-augmentati/notebook with confusion matrix. \nUsing old .pt method. ","metadata":{"papermill":{"duration":0.005493,"end_time":"2025-04-28T20:15:00.563061","exception":false,"start_time":"2025-04-28T20:15:00.557568","status":"completed"},"tags":[]}},{"id":"620f51b2","cell_type":"code","source":"import seaborn as sns\nimport cv2 \n\nimport matplotlib.pyplot as plt\nimport os\nimport time\nimport numpy as np\nimport glob\nimport json\nimport collections\nimport torch\nimport torch.nn as nn\n\nimport pydicom as dicom\nimport matplotlib.patches as patches\n\nfrom matplotlib import animation, rc\nimport pandas as pd\n\nimport pydicom as dicom # dicom\nimport pydicom\nfrom pydicom.pixel_data_handlers.util import apply_voi_lut","metadata":{"execution":{"iopub.status.busy":"2025-04-29T10:18:13.559247Z","iopub.execute_input":"2025-04-29T10:18:13.559463Z","iopub.status.idle":"2025-04-29T10:18:21.160616Z","shell.execute_reply.started":"2025-04-29T10:18:13.559435Z","shell.execute_reply":"2025-04-29T10:18:21.159826Z"},"papermill":{"duration":7.482372,"end_time":"2025-04-28T20:15:08.050177","exception":false,"start_time":"2025-04-28T20:15:00.567805","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"id":"33eb4a1d","cell_type":"markdown","source":"/kaggle/input/current-cropped-images/cropped_pngs/100206310_left_neural_foraminal_narrowing_l1_l2.png\n/kaggle/input/cropped-image/cropped_pngs/100206310_left_neural_foraminal_narrowing_l1_l2.png","metadata":{"papermill":{"duration":0.004328,"end_time":"2025-04-28T20:15:08.059547","exception":false,"start_time":"2025-04-28T20:15:08.055219","status":"completed"},"tags":[]}},{"id":"f9a58555","cell_type":"code","source":"csv_path = '/kaggle/input/64px-lumbar-cropped-images/cropped_labels.csv'\nimage_dir = '/kaggle/input/64px-lumbar-cropped-images/cropped_pngs'\n\ndataframe = pd.read_csv(csv_path)\ndataframe['image_path'] = dataframe['filename'].apply(lambda x: os.path.join(image_dir, x))","metadata":{"execution":{"iopub.status.busy":"2025-04-29T10:20:24.440036Z","iopub.execute_input":"2025-04-29T10:20:24.440633Z","iopub.status.idle":"2025-04-29T10:20:24.774977Z","shell.execute_reply.started":"2025-04-29T10:20:24.440605Z","shell.execute_reply":"2025-04-29T10:20:24.774098Z"},"papermill":{"duration":0.323631,"end_time":"2025-04-28T20:15:08.387837","exception":false,"start_time":"2025-04-28T20:15:08.064206","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"id":"6c0c9ae1","cell_type":"code","source":"# read data\ntrain_path = '/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification/'\n\ntrain  = pd.read_csv(train_path + 'train.csv')\nlabel = pd.read_csv(train_path + 'train_label_coordinates.csv')\ntrain_desc  = pd.read_csv(train_path + 'train_series_descriptions.csv')\ntest_desc   = pd.read_csv(train_path + 'test_series_descriptions.csv')\nsub         = pd.read_csv(train_path + 'sample_submission.csv')\nlen(test_desc) #number of test_description.csv rows","metadata":{"execution":{"iopub.status.busy":"2025-04-29T10:20:46.422395Z","iopub.execute_input":"2025-04-29T10:20:46.422688Z","iopub.status.idle":"2025-04-29T10:20:46.623464Z","shell.execute_reply.started":"2025-04-29T10:20:46.422665Z","shell.execute_reply":"2025-04-29T10:20:46.622838Z"},"papermill":{"duration":0.188604,"end_time":"2025-04-28T20:15:08.581221","exception":false,"start_time":"2025-04-28T20:15:08.392617","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"id":"6259712d","cell_type":"code","source":"dataframe.head()","metadata":{"execution":{"iopub.status.busy":"2025-04-29T10:20:49.982222Z","iopub.execute_input":"2025-04-29T10:20:49.982832Z","iopub.status.idle":"2025-04-29T10:20:50.004438Z","shell.execute_reply.started":"2025-04-29T10:20:49.982807Z","shell.execute_reply":"2025-04-29T10:20:50.003849Z"},"papermill":{"duration":0.0256,"end_time":"2025-04-28T20:15:08.611796","exception":false,"start_time":"2025-04-28T20:15:08.586196","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"id":"1a6901dc","cell_type":"code","source":"dataframe[dataframe[\"severity\"] == \"normal_mild\"].value_counts().sum()","metadata":{"execution":{"iopub.status.busy":"2025-04-29T10:20:52.910854Z","iopub.execute_input":"2025-04-29T10:20:52.911592Z","iopub.status.idle":"2025-04-29T10:20:53.075159Z","shell.execute_reply.started":"2025-04-29T10:20:52.911562Z","shell.execute_reply":"2025-04-29T10:20:53.074496Z"},"papermill":{"duration":0.162962,"end_time":"2025-04-28T20:15:08.780118","exception":false,"start_time":"2025-04-28T20:15:08.617156","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"id":"18ad13a9","cell_type":"code","source":"dataframe[dataframe[\"severity\"] == \"moderate\"].value_counts().sum()","metadata":{"execution":{"iopub.status.busy":"2025-04-29T10:20:54.928636Z","iopub.execute_input":"2025-04-29T10:20:54.928937Z","iopub.status.idle":"2025-04-29T10:20:54.971362Z","shell.execute_reply.started":"2025-04-29T10:20:54.928915Z","shell.execute_reply":"2025-04-29T10:20:54.970565Z"},"papermill":{"duration":0.042784,"end_time":"2025-04-28T20:15:08.828156","exception":false,"start_time":"2025-04-28T20:15:08.785372","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"id":"bb82f59f","cell_type":"code","source":"dataframe[dataframe[\"severity\"] == \"severe\"].value_counts().sum()","metadata":{"execution":{"iopub.status.busy":"2025-04-29T10:20:57.283909Z","iopub.execute_input":"2025-04-29T10:20:57.284195Z","iopub.status.idle":"2025-04-29T10:20:57.315128Z","shell.execute_reply.started":"2025-04-29T10:20:57.284172Z","shell.execute_reply":"2025-04-29T10:20:57.314415Z"},"papermill":{"duration":0.026791,"end_time":"2025-04-28T20:15:08.859980","exception":false,"start_time":"2025-04-28T20:15:08.833189","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"id":"c7d5c176","cell_type":"code","source":"#changed the name of dataframe to prevent conflict, you have to pay attention of using post underscores\ntrain_df = dataframe","metadata":{"execution":{"iopub.status.busy":"2025-04-29T10:20:59.770232Z","iopub.execute_input":"2025-04-29T10:20:59.770530Z","iopub.status.idle":"2025-04-29T10:20:59.774246Z","shell.execute_reply.started":"2025-04-29T10:20:59.770508Z","shell.execute_reply":"2025-04-29T10:20:59.773614Z"},"papermill":{"duration":0.009205,"end_time":"2025-04-28T20:15:08.874414","exception":false,"start_time":"2025-04-28T20:15:08.865209","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"id":"23c9558f","cell_type":"code","source":"# Define the base path for test images\nbase_path = '/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification/test_images/'\n\n# Function to get image paths for a series\ndef get_image_paths(row):\n    series_path = os.path.join(base_path, str(row['study_id']), str(row['series_id']))\n    if os.path.exists(series_path):\n        return [os.path.join(series_path, f) for f in os.listdir(series_path) if os.path.isfile(os.path.join(series_path, f))]\n    return []\n\n# Mapping of series_description to conditions\ncondition_mapping = {\n    'Sagittal T1': {'left': 'left_neural_foraminal_narrowing', 'right': 'right_neural_foraminal_narrowing'},\n    'Axial T2': {'left': 'left_subarticular_stenosis', 'right': 'right_subarticular_stenosis'},\n    'Sagittal T2/STIR': 'spinal_canal_stenosis'\n}\n\n# Create a list to store the expanded rows\nexpanded_rows = []\n\n# Expand the dataframe by adding new rows for each file path\nfor index, row in test_desc.iterrows():\n    image_paths = get_image_paths(row)\n    conditions = condition_mapping.get(row['series_description'], {})\n    if isinstance(conditions, str):  # Single condition\n        conditions = {'left': conditions, 'right': conditions}\n    for side, condition in conditions.items():\n        for image_path in image_paths:\n            expanded_rows.append({\n                'study_id': row['study_id'],\n                'series_id': row['series_id'],\n                'series_description': row['series_description'],\n                'image_path': image_path,\n                'condition': condition,\n                'row_id': f\"{row['study_id']}_{condition}\"\n            })\n\n# Create a new dataframe from the expanded rows\nexpanded_test_desc = pd.DataFrame(expanded_rows)\n\n# Display the resulting dataframe\nexpanded_test_desc.head(5)","metadata":{"execution":{"iopub.status.busy":"2025-04-29T10:21:02.435141Z","iopub.execute_input":"2025-04-29T10:21:02.435669Z","iopub.status.idle":"2025-04-29T10:21:02.716865Z","shell.execute_reply.started":"2025-04-29T10:21:02.435647Z","shell.execute_reply":"2025-04-29T10:21:02.716048Z"},"papermill":{"duration":0.187569,"end_time":"2025-04-28T20:15:09.066800","exception":false,"start_time":"2025-04-28T20:15:08.879231","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"id":"bdb43db2","cell_type":"code","source":"test_data = expanded_test_desc\ntrain_data = train_df","metadata":{"execution":{"iopub.status.busy":"2025-04-29T10:21:06.327928Z","iopub.execute_input":"2025-04-29T10:21:06.328729Z","iopub.status.idle":"2025-04-29T10:21:06.332028Z","shell.execute_reply.started":"2025-04-29T10:21:06.328703Z","shell.execute_reply":"2025-04-29T10:21:06.331301Z"},"papermill":{"duration":0.009815,"end_time":"2025-04-28T20:15:09.081927","exception":false,"start_time":"2025-04-28T20:15:09.072112","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"id":"07f89297","cell_type":"code","source":"train_data.head(5)","metadata":{"execution":{"iopub.status.busy":"2025-04-29T10:21:07.880342Z","iopub.execute_input":"2025-04-29T10:21:07.880857Z","iopub.status.idle":"2025-04-29T10:21:07.891862Z","shell.execute_reply.started":"2025-04-29T10:21:07.880834Z","shell.execute_reply":"2025-04-29T10:21:07.891084Z"},"papermill":{"duration":0.016616,"end_time":"2025-04-28T20:15:09.103679","exception":false,"start_time":"2025-04-28T20:15:09.087063","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"id":"b67de88f","cell_type":"code","source":"train_data['series_description'].value_counts()","metadata":{"execution":{"iopub.status.busy":"2025-04-29T10:21:10.705255Z","iopub.execute_input":"2025-04-29T10:21:10.705852Z","iopub.status.idle":"2025-04-29T10:21:10.715810Z","shell.execute_reply.started":"2025-04-29T10:21:10.705817Z","shell.execute_reply":"2025-04-29T10:21:10.715184Z"},"papermill":{"duration":0.018011,"end_time":"2025-04-28T20:15:09.127024","exception":false,"start_time":"2025-04-28T20:15:09.109013","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"id":"9833cb1f","cell_type":"code","source":"\"\"\"\ndef load_dicom(path):\n    dicom = pydicom.dcmread(path)\n    data = dicom.pixel_array\n    data = data - np.min(data)\n    if np.max(data) != 0:\n        data = data / np.max(data)\n    data = (data * 255).astype(np.uint8)\n    return data\n\"\"\"\n#Yeni pipiline'dan dolayı artık load_dicom yerine load_png kullanıyoruz.\ndef load_png(path):\n    # PNG görüntüsünü oku (gri tonlamalı olarak)\n    image = cv2.imread(path, cv2.IMREAD_GRAYSCALE)\n    \n    # Görüntüyü normalize et\n    image = image - np.min(image)\n    if np.max(image) != 0:\n        image = image / np.max(image)\n    \n    # Görüntüyü 0-255 aralığına dönüştür\n    image = (image * 255).astype(np.uint8)\n    \n    return image","metadata":{"execution":{"iopub.status.busy":"2025-04-29T10:21:18.608143Z","iopub.execute_input":"2025-04-29T10:21:18.608656Z","iopub.status.idle":"2025-04-29T10:21:18.613186Z","shell.execute_reply.started":"2025-04-29T10:21:18.608631Z","shell.execute_reply":"2025-04-29T10:21:18.612461Z"},"papermill":{"duration":0.010599,"end_time":"2025-04-28T20:15:09.143370","exception":false,"start_time":"2025-04-28T20:15:09.132771","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"id":"290659e3","cell_type":"code","source":"import random\nimport matplotlib.pyplot as plt\n\n# Yeni sıfırlanmış indekslerle rastgele seçim yapalım\ntrain_data_reset = train_data.reset_index(drop=True)\n\n# Rastgele iki indeks seçelim\nselected_indices = random.sample(range(len(train_data_reset)), 2)\n\nimages = []\nrow_ids = []\n\n# Seçilen indekslerle görselleri yükleyelim\nfor i in selected_indices:\n    image = load_png(train_data_reset['image_path'][i])  # Yeni sıfırlanmış indeksi kullan\n    images.append(image)\n    row_ids.append(train_data_reset['row_id'][i])  # Yeni sıfırlanmış indeksi kullan\n\n# Görselleri çizdirelim\nfig, ax = plt.subplots(1, 2, figsize=(8, 4))\nfor i in range(2):\n    ax[i].imshow(images[i], cmap='gray')\n    ax[i].set_title(f'Row ID: {row_ids[i]}', fontsize=8)\n    ax[i].axis('off')\nplt.tight_layout()\nplt.show()\n","metadata":{"execution":{"iopub.status.busy":"2025-04-29T10:21:21.727016Z","iopub.execute_input":"2025-04-29T10:21:21.727291Z","iopub.status.idle":"2025-04-29T10:21:22.036035Z","shell.execute_reply.started":"2025-04-29T10:21:21.727269Z","shell.execute_reply":"2025-04-29T10:21:22.035172Z"},"papermill":{"duration":0.290138,"end_time":"2025-04-28T20:15:09.438720","exception":false,"start_time":"2025-04-28T20:15:09.148582","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"id":"cb677b3c","cell_type":"code","source":"train_data ","metadata":{"execution":{"iopub.status.busy":"2025-04-29T10:21:26.865672Z","iopub.execute_input":"2025-04-29T10:21:26.865984Z","iopub.status.idle":"2025-04-29T10:21:26.879478Z","shell.execute_reply.started":"2025-04-29T10:21:26.865963Z","shell.execute_reply":"2025-04-29T10:21:26.878862Z"},"papermill":{"duration":0.019051,"end_time":"2025-04-28T20:15:09.464058","exception":false,"start_time":"2025-04-28T20:15:09.445007","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"id":"a0ed6d51","cell_type":"code","source":"train_data = train_data.dropna()","metadata":{"execution":{"iopub.status.busy":"2025-04-29T10:21:56.284126Z","iopub.execute_input":"2025-04-29T10:21:56.284758Z","iopub.status.idle":"2025-04-29T10:21:56.311834Z","shell.execute_reply.started":"2025-04-29T10:21:56.284736Z","shell.execute_reply":"2025-04-29T10:21:56.311179Z"},"papermill":{"duration":0.043005,"end_time":"2025-04-28T20:15:09.513131","exception":false,"start_time":"2025-04-28T20:15:09.470126","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"id":"97ac5e6e","cell_type":"code","source":"import pandas as pd\nfrom sklearn.model_selection import train_test_split\nfrom torch.utils.data import Dataset, DataLoader\nimport torchvision.transforms as transforms\nimport torch\nimport torch.optim.lr_scheduler as lr_scheduler\nfrom tqdm import tqdm\nimport numpy as np\nimport cv2\nfrom scipy.ndimage import map_coordinates, gaussian_filter\n\n# Augmentasyonları ve veriyi yükleme işini burada yapacağız\nclass CustomDataset(Dataset):\n    def __init__(self, dataframe, transform=None, apply_augmentation_to_all=True):\n        self.dataframe = dataframe\n        self.transform = transform\n        self.apply_augmentation_to_all = apply_augmentation_to_all  # Tüm sınıflara augmentasyon uygula\n\n    def __len__(self):\n        return len(self.dataframe)  # Her iki versiyonu kullanarak veri kümesini iki katına çıkarıyoruz\n\n    def __getitem__(self, index):\n        image_path = self.dataframe['image_path'][index]\n        image = load_png(image_path)\n        label = self.dataframe['severity'][index]\n    \n        # 1 kanal --> 3 kanal yap\n        image = np.stack([image] * 3, axis=-1)\n    \n        # %50 ihtimalle augmentasyon uygula\n        if self.apply_augmentation_to_all and np.random.rand() < 0.75:\n            image = self.apply_augmentation(image[:, :, 0])  # 1 kanal ver\n            image = np.stack([image] * 3, axis=-1)  # tekrar 3 kanal yap\n    \n        # Transform işlemleri uygula (örn. Resize, ToTensor vs.)\n        if self.transform:\n            image = self.transform(image)\n    \n        return image, label\n\n\n    # Normalize DICOM görüntüsü\n    def normalize_dicom(self, image, window_min=-1000, window_max=400):\n        image = np.clip(image, window_min, window_max)\n        return (image - window_min) / (window_max - window_min)\n\n    # Augmentasyon uygulama\n    def apply_augmentation(self, image):\n        augmentations = [\n            lambda img: self.random_brightness_contrast(img),\n            lambda img: self.motion_blur(img),\n            lambda img: self.median_blur(img),\n            lambda img: self.gaussian_blur(img),\n            lambda img: self.gauss_noise(img),\n            lambda img: self.optical_distortion(img),\n            lambda img: self.grid_distortion(img),\n            lambda img: self.elastic_transform(img),\n            lambda img: self.shift_scale_rotate(img),\n            lambda img: self.coarse_dropout(img),\n        ]\n        augmentation = np.random.choice(augmentations)\n        return augmentation(image)\n\n\n    # Random brightness and contrast adjustment\n    def random_brightness_contrast(self, image, brightness_limit=0.2, contrast_limit=0.2):\n        alpha = 1.0 + np.random.uniform(-contrast_limit, contrast_limit)\n        beta = 255.0 * np.random.uniform(-brightness_limit, brightness_limit)\n        image = alpha * image + beta\n        return np.clip(image, 0, 255)\n    \n    # Motion blur\n    def motion_blur(self, image, blur_limit=5):\n        size = np.random.randint(3, blur_limit+1) | 1\n        kernel_motion_blur = np.zeros((size, size))\n        kernel_motion_blur[int((size-1)/2), :] = np.ones(size)\n        kernel_motion_blur = kernel_motion_blur / size\n        image = cv2.filter2D(image, -1, kernel_motion_blur)\n        return np.clip(image, 0, 255)\n    \n    # Median blur\n    def median_blur(self, image, blur_limit=5):\n        size = np.random.randint(3, blur_limit+1) | 1\n        image = cv2.medianBlur(image.astype(np.uint8), size)\n        return image\n    \n    # Gaussian blur\n    def gaussian_blur(self, image, blur_limit=5):\n        size = np.random.randint(3, blur_limit+1) | 1\n        image = cv2.GaussianBlur(image.astype(np.uint8), (size, size), 0)\n        return image\n    \n    # Gaussian noise\n    def gauss_noise(self, image, var_limit=(5.0, 30.0)):\n        var = np.random.uniform(*var_limit)\n        noise = np.random.normal(0, var ** 0.5, image.shape)\n        return np.clip(image + noise, 0, 255)\n    \n    # Optical distortion (basic implementation)\n    def optical_distortion(self, image, distort_limit=0.5):\n        h, w = image.shape\n        dx = np.random.uniform(-distort_limit, distort_limit) * w\n        dy = np.random.uniform(-distort_limit, distort_limit) * h\n    \n        src = np.array([[0,0], [w-1,0], [w-1,h-1], [0,h-1]], dtype=np.float32)\n        dst = np.array([[dx,dy], [w-1+dx,dy], [w-1-dx,h-1-dy], [dx,h-1+dy]], dtype=np.float32)\n    \n        M = cv2.getPerspectiveTransform(src, dst)\n        image = cv2.warpPerspective(image, M, (w, h), borderMode=cv2.BORDER_REFLECT_101)\n        return image\n    \n    # Grid distortion\n    def grid_distortion(self, image, num_steps=5, distort_limit=0.3):\n        h, w = image.shape\n        x_step = w // num_steps\n        y_step = h // num_steps\n    \n        xx = np.arange(0, w, x_step)\n        yy = np.arange(0, h, y_step)\n        xx, yy = np.meshgrid(xx, yy)\n    \n        dx = (np.random.rand(*xx.shape) - 0.5) * 2 * distort_limit * x_step\n        dy = (np.random.rand(*yy.shape) - 0.5) * 2 * distort_limit * y_step\n    \n        map_x = (xx + dx).astype(np.float32)\n        map_y = (yy + dy).astype(np.float32)\n    \n        map_x = cv2.resize(map_x, (w, h))\n        map_y = cv2.resize(map_y, (w, h))\n    \n        distorted = cv2.remap(image.astype(np.float32), map_x, map_y, interpolation=cv2.INTER_LINEAR, borderMode=cv2.BORDER_REFLECT_101)\n        return np.clip(distorted, 0, 255)\n    \n    # Elastic transform\n    def elastic_transform(self, image, alpha=3, sigma=0.5):\n        random_state = np.random.RandomState(None)\n        shape = image.shape\n    \n        dx = gaussian_filter((random_state.rand(*shape) * 2 - 1), sigma) * alpha\n        dy = gaussian_filter((random_state.rand(*shape) * 2 - 1), sigma) * alpha\n    \n        x, y = np.meshgrid(np.arange(shape[1]), np.arange(shape[0]))\n        indices = (y+dy).reshape(-1,1), (x+dx).reshape(-1,1)\n    \n        distorted = map_coordinates(image, indices, order=1, mode='reflect').reshape(shape)\n        return np.clip(distorted, 0, 255)\n    \n    # Shift, Scale, Rotate\n    def shift_scale_rotate(self, image, shift_limit=0.1, scale_limit=0.1, rotate_limit=15):\n        h, w = image.shape\n        angle = np.random.uniform(-rotate_limit, rotate_limit)\n        scale = np.random.uniform(1-scale_limit, 1+scale_limit)\n        tx = np.random.uniform(-shift_limit, shift_limit) * w\n        ty = np.random.uniform(-shift_limit, shift_limit) * h\n    \n        M = cv2.getRotationMatrix2D((w/2, h/2), angle, scale)\n        M[:,2] += (tx, ty)\n    \n        image = cv2.warpAffine(image, M, (w, h), borderMode=cv2.BORDER_REFLECT_101)\n        return np.clip(image, 0, 255)\n    \n    # Coarse Dropout\n    def coarse_dropout(self, image, max_holes=8, max_height=32, max_width=32, min_holes=1, min_height=8, min_width=8):\n        h, w = image.shape\n        holes = np.random.randint(min_holes, max_holes+1)\n        for _ in range(holes):\n            y = np.random.randint(0, h)\n            x = np.random.randint(0, w)\n            dh = np.random.randint(min_height, max_height)\n            dw = np.random.randint(min_width, max_width)\n            y1 = np.clip(y, 0, h)\n            y2 = np.clip(y+dh, 0, h)\n            x1 = np.clip(x, 0, w)\n            x2 = np.clip(x+dw, 0, w)\n            image[y1:y2, x1:x2] = 0\n        return image\n\n\n# Function to create datasets and dataloaders for each series description\ndef create_datasets_and_loaders(df, series_description, transform, batch_size=8):\n    # Belirtilen seriye göre filtreleme\n    filtered_df = df[df['series_description'] == series_description]\n    \n    # Tüm verinin %100'ünü kullanıyoruz (isteğe bağlı olarak frac değiştirilebilir)\n    filtered_df = filtered_df.sample(frac=1.0, random_state=42)  \n    \n    # Eğitim ve doğrulama setlerine bölme\n    train_df, val_df = train_test_split(filtered_df, test_size=0.2, random_state=42)\n    train_df = train_df.reset_index(drop=True)\n    val_df = val_df.reset_index(drop=True)\n\n    # Dataset oluşturma (augmentasyon ve transform tüm veriye uygulanır)\n    train_dataset = CustomDataset(train_df, transform=transform, apply_augmentation_to_all=True)  # Eğitimde augmentasyon\n    val_dataset = CustomDataset(val_df, transform=transform, apply_augmentation_to_all=False)  # Validasyonda augmentasyon yok\n\n\n    # DataLoader oluşturma\n    trainloader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True)\n    valloader = DataLoader(val_dataset, batch_size=batch_size, shuffle=False)\n    \n    return trainloader, valloader, len(train_df), len(val_df)\n\n\n# Define the transforms\ntransform = transforms.Compose([\n    transforms.Lambda(lambda x: (x * 255).astype(np.uint8)),  # Convert back to uint8 for PIL\n    transforms.ToPILImage(),\n    transforms.Resize((224, 224)),\n    transforms.Grayscale(num_output_channels=3),\n    transforms.ToTensor(),\n])\n\n# Create dataloaders for each series description\ndataloaders = {}\nlengths = {}\n\n# DataLoader'ları oluşturma\ntrainloader_t1, valloader_t1, len_train_t1, len_val_t1 = create_datasets_and_loaders(\n    train_data, 'Sagittal T1', transform\n)\ntrainloader_t2, valloader_t2, len_train_t2, len_val_t2 = create_datasets_and_loaders(\n    train_data, 'Axial T2', transform\n)\ntrainloader_t2stir, valloader_t2stir, len_train_t2stir, len_val_t2stir = create_datasets_and_loaders(\n    train_data, 'Sagittal T2/STIR', transform\n)\n\ndataloaders['Sagittal T1'] = (trainloader_t1, valloader_t1)\ndataloaders['Axial T2'] = (trainloader_t2, valloader_t2)\ndataloaders['Sagittal T2/STIR'] = (trainloader_t2stir, valloader_t2stir)\n\nlengths['Sagittal T1'] = (len_train_t1, len_val_t1)\nlengths['Axial T2'] = (len_train_t2, len_val_t2)\nlengths['Sagittal T2/STIR'] = (len_train_t2stir, len_val_t2stir)\n\n\n# Dictionary mapping labels to indices\nlabel_map = {'Mild': 0, 'Moderate': 1, 'Severe': 2}","metadata":{"execution":{"iopub.status.busy":"2025-04-29T10:21:58.138331Z","iopub.execute_input":"2025-04-29T10:21:58.138913Z","iopub.status.idle":"2025-04-29T10:21:58.216154Z","shell.execute_reply.started":"2025-04-29T10:21:58.138888Z","shell.execute_reply":"2025-04-29T10:21:58.215295Z"},"papermill":{"duration":4.908214,"end_time":"2025-04-28T20:15:14.428175","exception":false,"start_time":"2025-04-28T20:15:09.519961","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"id":"ba37c089","cell_type":"code","source":"print(f\"Özgün Eğitim seti uzunluğu: {len(trainloader_t1.dataset) / 2}\")  # Yarıya bölüyoruz çünkü her örnek iki kere sayılıyor\nprint(f\"Özgün Doğrulama seti uzunluğu: {len(valloader_t1.dataset) / 2}\")  # Aynı şekilde\nprint(f\"Eğitim setinin uzunluğu: {len(trainloader_t1.dataset)}\")  # Eğitim setinin uzunluğunu yazdır\nprint(f\"Doğrulama setinin uzunluğu: {len(valloader_t1.dataset)}\")  # Doğrulama setinin uzunluğunu yazdır","metadata":{"execution":{"iopub.status.busy":"2025-04-29T10:22:03.779516Z","iopub.execute_input":"2025-04-29T10:22:03.780322Z","iopub.status.idle":"2025-04-29T10:22:03.785005Z","shell.execute_reply.started":"2025-04-29T10:22:03.780294Z","shell.execute_reply":"2025-04-29T10:22:03.784291Z"},"papermill":{"duration":0.012566,"end_time":"2025-04-28T20:15:14.447219","exception":false,"start_time":"2025-04-28T20:15:14.434653","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"id":"30a7ce74","cell_type":"code","source":"import matplotlib.pyplot as plt\n\n# Function to visualize a batch of images\ndef visualize_batch(dataloader):\n    images, labels = next(iter(dataloader))\n    fig, axes = plt.subplots(1, len(images), figsize=(20, 5))\n    for i, (img, lbl) in enumerate(zip(images, labels)):\n        ax = axes[i]\n        img = img.permute(1, 2, 0)  # Convert to HWC for visualization\n        ax.imshow(img)\n        ax.set_title(f\"Label: {lbl}\")\n        ax.axis('off')\n    plt.show()\n\n# Visualize samples from each dataloader\nprint(\"Visualizing Sagittal T1 samples\")\nvisualize_batch(trainloader_t1)\nprint(\"Visualizing Axial T2 samples\")\nvisualize_batch(trainloader_t2)\nprint(\"Visualizing Sagittal T2/STIR samples\")\nvisualize_batch(trainloader_t2stir)","metadata":{"execution":{"iopub.status.busy":"2025-04-29T10:22:06.445373Z","iopub.execute_input":"2025-04-29T10:22:06.445942Z","iopub.status.idle":"2025-04-29T10:22:08.241509Z","shell.execute_reply.started":"2025-04-29T10:22:06.445918Z","shell.execute_reply":"2025-04-29T10:22:08.240572Z"},"papermill":{"duration":1.828346,"end_time":"2025-04-28T20:15:16.281699","exception":false,"start_time":"2025-04-28T20:15:14.453353","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"id":"9cef03bd","cell_type":"code","source":"import matplotlib.pyplot as plt\n\nimage, label = next(iter(trainloader_t2))\nsample = image[1].permute(1, 2, 0)  #sample\n\n# Görüntü ve etiketleri çiz\nplt.figure(figsize=(8, 4))\nplt.imshow(sample)  # Eğer 3 kanallı ise cmap kullanmaya gerek yok\nplt.title(label[1])  # Etiketi yazdır\nplt.axis('off')  # Ekseni kapat\nplt.tight_layout()\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2025-04-29T10:22:12.033954Z","iopub.execute_input":"2025-04-29T10:22:12.034653Z","iopub.status.idle":"2025-04-29T10:22:12.230502Z","shell.execute_reply.started":"2025-04-29T10:22:12.034631Z","shell.execute_reply":"2025-04-29T10:22:12.229717Z"},"papermill":{"duration":0.188118,"end_time":"2025-04-28T20:15:16.486744","exception":false,"start_time":"2025-04-28T20:15:16.298626","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"id":"746a1d48","cell_type":"code","source":"import torch\nimport torch.nn as nn\nimport torchvision.models as models\nfrom torchvision import transforms\nfrom torch.utils.data import DataLoader\nfrom sklearn.model_selection import train_test_split\nimport pandas as pd\nfrom tqdm import tqdm\n\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")","metadata":{"execution":{"iopub.status.busy":"2025-04-29T10:22:16.128808Z","iopub.execute_input":"2025-04-29T10:22:16.129089Z","iopub.status.idle":"2025-04-29T10:22:16.176327Z","shell.execute_reply.started":"2025-04-29T10:22:16.129070Z","shell.execute_reply":"2025-04-29T10:22:16.175626Z"},"papermill":{"duration":0.079993,"end_time":"2025-04-28T20:15:16.584680","exception":false,"start_time":"2025-04-28T20:15:16.504687","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"id":"f16de6b5","cell_type":"code","source":"import torch\nimport torch.nn as nn\nimport torchvision.models as models\nfrom torchvision.models import ResNet50_Weights  # ResNet50 ağırlıkları için enum'u import et\n\nclass CustomResNet50(nn.Module):\n    def __init__(self, num_classes=3, pretrained_weights=None):\n        super(CustomResNet50, self).__init__()\n        \n        # pretrained=True yerine weights=ResNet50_Weights.IMAGENET1K_V1 kullanarak ağırlıkları yükleyin\n        self.model = models.resnet50(weights=ResNet50_Weights.IMAGENET1K_V1).to(device)\n        \n        # Eğer manuel ağırlık yolu verilmişse, bu ağırlıkları yükle\n        if pretrained_weights:\n            self.model.load_state_dict(torch.load(pretrained_weights))\n        \n        num_ftrs = self.model.fc.in_features  # Son katmanın özellik sayısını al\n        self.model.fc = nn.Linear(num_ftrs, num_classes)  # Son katmanı değiştirme\n\n    def forward(self, x):\n        return self.model(x)\n\n    def unfreeze_middle_layers(self):\n        \"\"\"Orta katmanları çöz.\"\"\"\n        for name, param in self.model.named_parameters():\n            if 'layer3' in name or 'layer4' in name:  \n                param.requires_grad = True\n            else:\n                param.requires_grad = False\n\n# Modeli başlat\nsagittal_t1_model = CustomResNet50(num_classes=3).to(device)\naxial_t2_model = CustomResNet50(num_classes=3).to(device)\nsagittal_t2stir_model = CustomResNet50(num_classes=3).to(device)\n\n# Orta katmanları çözmek için\nfor model in [sagittal_t1_model, axial_t2_model, sagittal_t2stir_model]:\n    model.unfreeze_middle_layers()  # Orta katmanları çöz\n\n# Eğitim parametreleri\nweights = torch.tensor([1.0, 2.0, 4.0])\ncriterion = nn.CrossEntropyLoss(weight=weights.to(device))\n\n# Optimizer ayarları\noptimizer_sagittal_t1 = torch.optim.Adam(sagittal_t1_model.parameters(), lr=0.001)\noptimizer_axial_t2 = torch.optim.Adam(axial_t2_model.parameters(), lr=0.001)\noptimizer_sagittal_t2stir = torch.optim.Adam(sagittal_t2stir_model.parameters(), lr=0.001)\n\n# Modelleri ve optimizörleri saklamak için dictionary\nmodels_dict = {\n    'Sagittal T1': sagittal_t1_model,\n    'Axial T2': axial_t2_model,\n    'Sagittal T2/STIR': sagittal_t2stir_model,\n}\n\noptimizers_dict = {\n    'Sagittal T1': optimizer_sagittal_t1,\n    'Axial T2': optimizer_axial_t2,\n    'Sagittal T2/STIR': optimizer_sagittal_t2stir,\n}\n\n# Eğitim yapılabilir parametrelerin sayısını yazdır\nfor model_name, model in models_dict.items():\n    trainable_params = sum(p.numel() for p in model.parameters() if p.requires_grad)\n    print(f\"Trainable parameters for {model_name}: {trainable_params}\")\n","metadata":{"execution":{"iopub.status.busy":"2025-04-29T10:22:19.184503Z","iopub.execute_input":"2025-04-29T10:22:19.185275Z","iopub.status.idle":"2025-04-29T10:22:21.729850Z","shell.execute_reply.started":"2025-04-29T10:22:19.185248Z","shell.execute_reply":"2025-04-29T10:22:21.729200Z"},"papermill":{"duration":2.240829,"end_time":"2025-04-28T20:15:18.842626","exception":false,"start_time":"2025-04-28T20:15:16.601797","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"id":"b7e12d09","cell_type":"code","source":"label_map = {'normal_mild': 0, 'moderate': 1, 'severe': 2}","metadata":{"execution":{"iopub.status.busy":"2025-04-29T10:22:43.132923Z","iopub.execute_input":"2025-04-29T10:22:43.133206Z","iopub.status.idle":"2025-04-29T10:22:43.136817Z","shell.execute_reply.started":"2025-04-29T10:22:43.133183Z","shell.execute_reply":"2025-04-29T10:22:43.136046Z"},"papermill":{"duration":0.022527,"end_time":"2025-04-28T20:15:18.882143","exception":false,"start_time":"2025-04-28T20:15:18.859616","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"id":"5713563a","cell_type":"code","source":"for images, labels in trainloader_t2:\n    labels = torch.tensor([label_map[label] for label in labels])\n    labels = labels.to(device)\n    print(labels)\n    break","metadata":{"execution":{"iopub.status.busy":"2025-04-29T10:22:45.681228Z","iopub.execute_input":"2025-04-29T10:22:45.681810Z","iopub.status.idle":"2025-04-29T10:22:45.760158Z","shell.execute_reply.started":"2025-04-29T10:22:45.681777Z","shell.execute_reply":"2025-04-29T10:22:45.759448Z"},"papermill":{"duration":0.096686,"end_time":"2025-04-28T20:15:18.995385","exception":false,"start_time":"2025-04-28T20:15:18.898699","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"id":"e93b15d4","cell_type":"code","source":"from sklearn.metrics import precision_score, recall_score, roc_auc_score, cohen_kappa_score, matthews_corrcoef, balanced_accuracy_score\n","metadata":{"execution":{"iopub.status.busy":"2025-04-29T10:22:48.655302Z","iopub.execute_input":"2025-04-29T10:22:48.655581Z","iopub.status.idle":"2025-04-29T10:22:48.659595Z","shell.execute_reply.started":"2025-04-29T10:22:48.655562Z","shell.execute_reply":"2025-04-29T10:22:48.658853Z"},"papermill":{"duration":0.021517,"end_time":"2025-04-28T20:15:19.034395","exception":false,"start_time":"2025-04-28T20:15:19.012878","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"id":"87775b9f","cell_type":"code","source":"import torch.optim.lr_scheduler as lr_scheduler\nfrom copy import deepcopy\nfrom sklearn.metrics import (\n    confusion_matrix,\n    f1_score,\n    precision_score,\n    recall_score,\n    cohen_kappa_score,\n    matthews_corrcoef,\n    balanced_accuracy_score,\n    roc_auc_score,\n    classification_report\n)\n\ndef train_model(model, trainloader, valloader, len_train, len_val, optimizer, num_epochs=10, patience=3):\n    # Learning rate scheduler\n    scheduler = lr_scheduler.StepLR(optimizer, step_size=2, gamma=0.1)\n\n    best_val_acc = 0.0\n    best_model_wts = deepcopy(model.state_dict())\n    counter = 0\n\n    for epoch in range(num_epochs):\n        model.train()\n        train_loss = 0\n        correct_train = 0\n\n        with tqdm(trainloader, unit=\"batch\") as tepoch:\n            for images, labels in tepoch:\n                images, labels = images.to(device), torch.tensor([label_map[label] for label in labels]).to(device)\n                optimizer.zero_grad()\n                outputs = model(images)\n                loss = criterion(outputs, labels)\n                loss.backward()\n                optimizer.step()\n                train_loss += loss.item()\n\n                probabilities = torch.softmax(outputs, dim=1)\n                _, predicted = torch.max(probabilities, 1)\n                correct_train += (predicted == labels).sum().item()\n\n                tepoch.set_postfix(epoch=epoch+1)\n\n        scheduler.step()\n\n        train_loss /= len(trainloader)\n        train_acc = 100 * correct_train / len_train\n\n        model.eval()\n        val_loss, correct_val = 0, 0\n        all_labels = []\n        all_predictions = []\n\n        with torch.no_grad():\n            with tqdm(valloader, unit=\"batch\") as vepoch:\n                for images, labels in vepoch:\n                    images, labels = images.to(device), torch.tensor([label_map[label] for label in labels]).to(device)\n                    outputs = model(images)\n                    loss = criterion(outputs, labels)\n                    val_loss += loss.item()\n\n                    probabilities = torch.softmax(outputs, dim=1)\n\n                    if probabilities.dim() == 1:\n                        _, predicted = torch.max(probabilities, 0)\n                    else:\n                        _, predicted = torch.max(probabilities, 1)\n                    correct_val += (predicted == labels).sum().item()\n\n                    all_labels.extend(labels.cpu().numpy())\n                    all_predictions.extend(predicted.cpu().numpy())\n\n                    vepoch.set_postfix(epoch=epoch+1)\n\n        val_loss /= len(valloader)\n        val_acc = 100 * correct_val / len_val\n\n        # Confusion Matrix ve F1 Skoru Hesaplama\n        conf_matrix = confusion_matrix(all_labels, all_predictions)\n        f1 = f1_score(all_labels, all_predictions, average=\"weighted\")\n        class_report = classification_report(all_labels, all_predictions)\n\n        # Precision ve Recall\n        precision = precision_score(all_labels, all_predictions, average=\"weighted\")\n        recall = recall_score(all_labels, all_predictions, average=\"weighted\")\n\n        # Cohen's Kappa\n        kappa = cohen_kappa_score(all_labels, all_predictions)\n\n        # Matthews Correlation Coefficient (MCC)\n        mcc = matthews_corrcoef(all_labels, all_predictions)\n\n        # Balanced Accuracy\n        balanced_acc = balanced_accuracy_score(all_labels, all_predictions)\n\n        # ROC-AUC (Eğer çok sınıflıysa one-vs-rest yaklaşımı kullanılır)\n        try:\n            roc_auc = roc_auc_score(all_labels, torch.nn.functional.one_hot(torch.tensor(all_predictions), num_classes=len(set(all_labels))).numpy(), multi_class=\"ovr\")\n        except ValueError:\n            roc_auc = \"N/A (ROC-AUC çok sınıflı problemde uygun olmayabilir)\"\n\n        # Çıktılar\n        print(f\"Precision: {precision:.4f}\")\n        print(f\"Recall: {recall:.4f}\")\n        print(f\"Cohen's Kappa: {kappa:.4f}\")\n        print(f\"MCC: {mcc:.4f}\")\n        print(f\"Balanced Accuracy: {balanced_acc:.4f}\")\n        print(f\"ROC-AUC Score: {roc_auc}\")\n        print(f\"Epoch {epoch+1}, Train Loss: {train_loss:.4f}, Train Acc: {train_acc:.2f}%, Val Loss: {val_loss:.4f}, Val Acc: {val_acc:.2f}%\")\n        print(f\"Confusion Matrix:\\n{conf_matrix}\")\n        print(f\"F1 Score: {f1:.4f}\")\n        print(f\"Classification Report:\\n{class_report}\")\n\n        # Save the best model and check for early stopping\n        if val_acc > best_val_acc:\n            best_val_acc = val_acc\n            best_model_wts = deepcopy(model.state_dict())\n            counter = 0\n            torch.save(best_model_wts, f'best_model_{epoch+1}.pth')\n        else:\n            counter += 1\n\n        # Early stopping\n        if counter >= patience:\n            print(f\"Early stopping triggered after {epoch+1} epochs\")\n            break\n\n    # Load best model weights\n    model.load_state_dict(best_model_wts)\n    return model, best_val_acc","metadata":{"execution":{"iopub.status.busy":"2025-04-29T10:22:51.157162Z","iopub.execute_input":"2025-04-29T10:22:51.157876Z","iopub.status.idle":"2025-04-29T10:22:51.172367Z","shell.execute_reply.started":"2025-04-29T10:22:51.157847Z","shell.execute_reply":"2025-04-29T10:22:51.171601Z"},"papermill":{"duration":0.031667,"end_time":"2025-04-28T20:15:19.082611","exception":false,"start_time":"2025-04-28T20:15:19.050944","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"id":"a71e9cb1","cell_type":"code","source":"#!rm -rf /kaggle/working/*","metadata":{"execution":{"iopub.execute_input":"2025-04-28T20:15:19.116399Z","iopub.status.busy":"2025-04-28T20:15:19.116184Z","iopub.status.idle":"2025-04-28T20:15:19.119356Z","shell.execute_reply":"2025-04-28T20:15:19.118699Z"},"papermill":{"duration":0.02139,"end_time":"2025-04-28T20:15:19.120584","exception":false,"start_time":"2025-04-28T20:15:19.099194","status":"completed"},"tags":[]},"outputs":[],"execution_count":null},{"id":"d1c80877","cell_type":"code","source":"# Eğitim işlemi\nfor desc, model in models_dict.items():  # Burada 'models_dict' kullanılmalı\n    if desc == 'Sagittal T1':\n        trainloader, valloader, len_train, len_val = trainloader_t1, valloader_t1, len_train_t1, len_val_t1\n    elif desc == 'Axial T2':\n        trainloader, valloader, len_train, len_val = trainloader_t2, valloader_t2, len_train_t2, len_val_t2\n    elif desc == 'Sagittal T2/STIR':\n        trainloader, valloader, len_train, len_val = trainloader_t2stir, valloader_t2stir, len_train_t2stir, len_val_t2stir\n    \n    print(f\"Training model for {desc}\")\n    train_model(model, trainloader, valloader, len_train, len_val, optimizers_dict[desc])  # Burada 'optimizers_dict' kullanılmalı","metadata":{"execution":{"iopub.status.busy":"2025-04-29T10:22:58.339724Z","iopub.execute_input":"2025-04-29T10:22:58.340423Z"},"papermill":{"duration":3158.431025,"end_time":"2025-04-28T21:07:57.568401","exception":false,"start_time":"2025-04-28T20:15:19.137376","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"id":"21a0fc6d","cell_type":"code","source":"train_data['level'].unique()","metadata":{"execution":{"iopub.execute_input":"2025-04-28T21:08:03.252073Z","iopub.status.busy":"2025-04-28T21:08:03.251425Z","iopub.status.idle":"2025-04-28T21:08:03.258047Z","shell.execute_reply":"2025-04-28T21:08:03.257502Z"},"papermill":{"duration":2.802641,"end_time":"2025-04-28T21:08:03.259113","exception":false,"start_time":"2025-04-28T21:08:00.456472","status":"completed"},"tags":[]},"outputs":[],"execution_count":null}]}