{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[{"sourceId":71549,"databundleVersionId":8561470,"sourceType":"competition"}],"dockerImageVersionId":30732,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import os\nfrom pathlib import Path\n\nimport pandas as pd\nimport numpy as np\nimport pydicom\nimport torch\nimport torch.nn as nn\nimport torch.optim as optim\nfrom torch.utils.data import Dataset, DataLoader\nfrom torchvision import transforms, models","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2024-06-08T09:26:51.720422Z","iopub.execute_input":"2024-06-08T09:26:51.720840Z","iopub.status.idle":"2024-06-08T09:27:00.688561Z","shell.execute_reply.started":"2024-06-08T09:26:51.720804Z","shell.execute_reply":"2024-06-08T09:27:00.686748Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class SpineDataset(Dataset):\n    def __init__(self, data_directory, train_df, train_label_coord, transform_=None):\n        self.data_directory = data_directory\n        self.train_df = train_df\n        self.train_label_coord = train_label_coord\n        self.transform = transform\n\n        self.image_paths = []\n        self.labels = []\n\n        for _, row in train_df.iterrows():\n            s_id = row['study_id']\n            label_cols = [col for col in row.index if col != 'study_id']  # Get all label columns except 'study_id'\n            for col in label_cols:\n                severity = row[col]\n                if pd.notnull(severity):  # Check if severity label is not null\n                    series_ids = train_label_coord[(train_label_coord['study_id'] == s_id) & (train_label_coord['condition'] == col)]['series_id'].tolist()\n                    for series_id in series_ids:\n                        image_path = os.path.join(data_directory, str(s_id), str(series_id), '1.dcm')\n                        self.image_paths.append(image_path)\n                        self.labels.append(severity)\n\n    def __len__(self):\n        return len(self.image_paths)\n\n    def __getitem__(self, idx):\n        img_path = self.image_paths[idx]\n        dicom_img = pydicom.dcmread(img_path).pixel_array\n        img = np.stack([dicom_img] * 3, axis=-1)  # Convert to 3-channel\n\n        if self.transform:\n            img = self.transform(img)\n\n        label = self.labels[idx]\n        return img, label\n        \n    \nclass SpineModel(nn.Module):\n  def __init__(self, num_region, num_class):\n    super(SpineModel, self).__init__()\n    self.base_model = models.resnet18(pretrained=True)\n    self.base_model.fc = nn.Linear(self.base_model.fc.in_features, num_region * num_class)\n    self.num_regions = num_region\n    self.num_classes = num_class\n\n  def forward(self, x):\n    x = self.base_model(x)\n    x = x.view(-1, self.num_regions, self.num_classes)\n    return x","metadata":{"execution":{"iopub.status.busy":"2024-06-08T09:27:44.321063Z","iopub.execute_input":"2024-06-08T09:27:44.322098Z","iopub.status.idle":"2024-06-08T09:27:44.339098Z","shell.execute_reply.started":"2024-06-08T09:27:44.322052Z","shell.execute_reply":"2024-06-08T09:27:44.337715Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"data = Path('/kaggle/input/rsna-2024-lumbar-spine-degenerative-classification')\ntrain_df = pd.read_csv(os.path.join(data, 'train.csv'))\ntrain_label_coords = pd.read_csv(os.path.join(data, 'train_label_coordinates.csv'))\ntrain_img_dir = os.path.join(data, 'train')\n\ntransform = transforms.Compose([\n  transforms.ToPILImage(),\n  transforms.Resize((224, 224)),\n  transforms.ToTensor()\n])\n\n\ndataset = SpineDataset(train_img_dir, train_df, train_label_coords, transform_=transform)\ndataloader = DataLoader(dataset, batch_size=32, shuffle=True)","metadata":{"execution":{"iopub.status.busy":"2024-06-08T09:27:58.764062Z","iopub.execute_input":"2024-06-08T09:27:58.764649Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"num_regions = 25\nnum_classes = 3  # Normal/Mild, Moderate, Severe\nmodel = SpineModel(num_regions, num_classes).cuda()\n\n\ncriterion = nn.CrossEntropyLoss()\noptimizer = optim.Adam(model.parameters(), lr=0.001) ","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Training loop\nnum_epochs = 1\n\nfor epoch in range(num_epochs):\n  model.train()\n  running_loss = 0.0\n  for images, labels in dataloader:\n    images, labels = images.cuda(), labels.cuda()\n\n    optimizer.zero_grad()\n    outputs = model(images)\n    \n    # Reshape labels to match output shape\n    labels = labels.view(-1, num_regions, num_classes).long()\n    \n    loss = criterion(outputs.view(-1, num_classes), labels.view(-1, num_classes).argmax(dim=2))\n    loss.backward()\n    optimizer.step()\n    running_loss += loss.item() * images.size(0)\n\n  epoch_loss = running_loss / len(dataloader.dataset)\n  print(f'Epoch {epoch+1}/{num_epochs}, Loss: {epoch_loss:.4f}')","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model.eval()\ntest_image_paths = [i for i in os.walk('/data/test')]\ntest_dataset = SpineDataset(test_image_paths, None, transform_=transform)\ntest_dataloader = DataLoader(test_dataset, batch_size=32, shuffle=False)\n\npredictions = []\n\nwith torch.no_grad():\n  for images in test_dataloader:\n    images = images.cuda()\n    outputs = model(images)\n    _, preds = torch.max(outputs, dim=2)\n    predictions.extend(preds.cpu().numpy())\n\nsample_submission = pd.read_csv('sample_submission.csv')\n\n\nfor i, row in sample_submission.iterrows():\n  study_id, condition, level = row['row_id'].split('_')\n  pred = predictions[i]\n  sample_submission.at[i, 'normal_mild'] = pred[0]\n  sample_submission.at[i, 'moderate'] = pred[1]\n  sample_submission.at[i, 'severe'] = pred[2]\n\nsample_submission.to_csv('submission.csv', index=False)","metadata":{},"execution_count":null,"outputs":[]}]}