{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.12.12","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[{"sourceType":"competition","sourceId":10338,"databundleVersionId":862042}],"dockerImageVersionId":31328,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"# This Python 3 environment comes with many helpful analytics libraries installed\n# It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python\n# For example, here's several helpful packages to load\n\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n\n# Input data files are available in the read-only \"../input/\" directory\n# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory\n\n# import os\n# for dirname, _, filenames in os.walk('/kaggle/input'):\n#     for filename in filenames:\n#         print(os.path.join(dirname, filename))\n\n# You can write up to 20GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using \"Save & Run All\" \n# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2026-05-12T02:02:55.715427Z","iopub.execute_input":"2026-05-12T02:02:55.715799Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!pip install -q open_clip_torch","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-12T02:24:24.329447Z","iopub.execute_input":"2026-05-12T02:24:24.330824Z","iopub.status.idle":"2026-05-12T02:24:28.569063Z","shell.execute_reply.started":"2026-05-12T02:24:24.330773Z","shell.execute_reply":"2026-05-12T02:24:28.567803Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport pandas as pd\nfrom sklearn.model_selection import train_test_split\nimport torch\nimport pydicom\nfrom PIL import Image\nfrom torch.utils.data import Dataset, DataLoader\nfrom torchvision import transforms\nfrom transformers import AutoModel, AutoTokenizer\nimport torch.nn as nn\nfrom tqdm import tqdm\nimport open_clip","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-12T02:24:32.616286Z","iopub.execute_input":"2026-05-12T02:24:32.617498Z","iopub.status.idle":"2026-05-12T02:24:32.623995Z","shell.execute_reply.started":"2026-05-12T02:24:32.617437Z","shell.execute_reply":"2026-05-12T02:24:32.622483Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"DATASET_PATH = \"/kaggle/input/competitions/rsna-pneumonia-detection-challenge\"\nIMAGE_DIR = os.path.join(\n    DATASET_PATH,\n    \"stage_2_train_images\"\n)\n\nCSV_PATH = os.path.join(\n    DATASET_PATH,\n    \"stage_2_train_labels.csv\"\n)\n\nprint(os.listdir(DATASET_PATH)[:10])\nprint(\"\\nNumber of images:\")\nprint(len(os.listdir(IMAGE_DIR)))\ndf = pd.read_csv(CSV_PATH)\nprint(df.head())\ndf = df.groupby(\"patientId\")[\"Target\"].max().reset_index()\nprint(\"\\nGrouped DataFrame:\")\nprint(df.head())\nprint(\"\\nLabel Distribution:\")\nprint(df[\"Target\"].value_counts())\n\n\ndf_small, _ = train_test_split(\n    df,\n    train_size=0.05,\n    stratify=df[\"Target\"],\n    random_state=42\n)\n\nprint(\"\\nFULL DATA :\", len(df))\nprint(\"10% DATA  :\", len(df_small))\n\n\ndf_small.to_csv(\n    \"/kaggle/working/rsna_10_percent.csv\",\n    index=False\n)\n\n# print(\"\\nSaved:\")\n# print(\"/kaggle/working/rsna_10_percent.csv\")\n\n\nsample_patient_id = df_small.iloc[0][\"patientId\"]\nsample_path = os.path.join(\n    IMAGE_DIR,\n    f\"{sample_patient_id}.dcm\"\n)\n\n# print(\"\\nSample image path:\")\n# print(sample_path)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-12T02:24:35.112365Z","iopub.execute_input":"2026-05-12T02:24:35.112796Z","iopub.status.idle":"2026-05-12T02:24:35.233463Z","shell.execute_reply.started":"2026-05-12T02:24:35.112758Z","shell.execute_reply":"2026-05-12T02:24:35.232116Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"TRAIN_IMAGE_DIR = \"/kaggle/input/competitions/rsna-pneumonia-detection-challenge/stage_2_train_images\"\nCSV_PATH = \"/kaggle/input/competitions/rsna-pneumonia-detection-challenge/stage_2_train_labels.csv\"\nBATCH_SIZE = 16\nEPOCHS = 2\nLR = 1e-4\n\nDEVICE = \"cuda\" if torch.cuda.is_available() else \"cpu\"\n\n\n# df = pd.read_csv(CSV_PATH)\n# df = df.groupby(\"patientId\")[\"Target\"].max().reset_index()\n# print(df[\"Target\"].value_counts())\n\n\nDEVICE = \"cuda\" if torch.cuda.is_available() else \"cpu\"\nMODEL_NAME = \"hf-hub:microsoft/BiomedCLIP-PubMedBERT_256-vit_base_patch16_224\"\nmodel, preprocess_train, preprocess_val = open_clip.create_model_and_transforms(\n    MODEL_NAME\n)\n\ntokenizer = open_clip.get_tokenizer(MODEL_NAME)\nmodel = model.to(DEVICE)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-12T02:24:53.480595Z","iopub.execute_input":"2026-05-12T02:24:53.481941Z","iopub.status.idle":"2026-05-12T02:24:58.002694Z","shell.execute_reply.started":"2026-05-12T02:24:53.481894Z","shell.execute_reply":"2026-05-12T02:24:58.001637Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class RSNADataset(Dataset):\n\n    def __init__(self, dataframe, image_dir, transform=None):\n\n        self.df = dataframe\n        self.image_dir = image_dir\n        self.transform = transform\n\n    def __len__(self):\n        return len(self.df)\n\n    def __getitem__(self, idx):\n\n        row = self.df.iloc[idx]\n\n        patient_id = row[\"patientId\"]\n        label = row[\"Target\"]\n\n        dicom_path = os.path.join(\n            self.image_dir,\n            f\"{patient_id}.dcm\"\n        )\n\n        dicom = pydicom.dcmread(dicom_path)\n\n        image = dicom.pixel_array\n\n        # normalize to 0-255\n        image = image - image.min()\n        image = image / (image.max() + 1e-8)\n        image = (image * 255).astype(\"uint8\")\n\n        # grayscale -> RGB\n        image = Image.fromarray(image).convert(\"RGB\")\n\n        if self.transform:\n            image = self.transform(image)\n\n        return image, torch.tensor(label, dtype=torch.long)\n\n\ntransform = transforms.Compose([\n    transforms.Resize((224, 224)),\n    transforms.ToTensor(),\n    transforms.Normalize(\n        mean=[0.485, 0.456, 0.406],\n        std=[0.229, 0.224, 0.225]\n    )\n])\n\ndataset = RSNADataset(\n    dataframe=df_small,\n    image_dir=TRAIN_IMAGE_DIR,\n    transform=transform\n)\n\nloader = DataLoader(\n    dataset,\n    batch_size=BATCH_SIZE,\n    shuffle=True,\n    num_workers=2\n)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-12T02:25:04.583412Z","iopub.execute_input":"2026-05-12T02:25:04.584618Z","iopub.status.idle":"2026-05-12T02:25:04.595956Z","shell.execute_reply.started":"2026-05-12T02:25:04.584567Z","shell.execute_reply":"2026-05-12T02:25:04.594569Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"image_encoder = model.visual\nclass PneumoniaModel(nn.Module):\n\n    def __init__(self, encoder):\n\n        super().__init__()\n        self.encoder = encoder\n        with torch.no_grad():\n            dummy = torch.randn(1, 3, 224, 224).to(DEVICE)\n            features = self.encoder(dummy)\n            feature_dim = features.shape[1]\n\n        print(\"Feature dim:\", feature_dim)\n\n        self.classifier = nn.Sequential(\n            nn.Linear(feature_dim, 256),\n            nn.ReLU(),\n            nn.Dropout(0.2),\n            nn.Linear(256, 2)\n        )\n\n    def forward(self, x):\n\n        features = self.encoder(x)\n        logits = self.classifier(features)\n\n        return logits\n\n\nnet = PneumoniaModel(image_encoder).to(DEVICE)\nprint(\"Classifier model ready.\")\n\ncriterion = nn.CrossEntropyLoss()\n\noptimizer = torch.optim.AdamW(\n    net.parameters(),\n    lr=LR\n)\n\nprint(\"Optimizer ready.\")\n\nfor epoch in range(EPOCHS):\n\n    net.train()\n\n    total_loss = 0\n    correct = 0\n    total = 0\n\n    pbar = tqdm(loader)\n\n    for images, labels in pbar:\n\n        images = images.to(DEVICE)\n        labels = labels.to(DEVICE)\n        optimizer.zero_grad()\n        logits = net(images)\n        loss = criterion(logits, labels)\n        loss.backward()\n        optimizer.step()\n        total_loss += loss.item()\n        preds = logits.argmax(dim=1)\n        correct += (preds == labels).sum().item()\n        total += labels.size(0)\n        acc = correct / total\n        pbar.set_description(\n            f\"Epoch {epoch+1} | \"\n            f\"Loss {loss.item():.4f} | \"\n            f\"Acc {acc:.4f}\"\n        )\n\n    epoch_loss = total_loss / len(loader)\n    epoch_acc = correct / total\n\n    print(\n        f\"\\nEpoch {epoch+1} Finished | \"\n        f\"Loss: {epoch_loss:.4f} | \"\n        f\"Acc: {epoch_acc:.4f}\"\n    )\n\ntorch.save(\n    net.state_dict(),\n    \"biomedclip_rsna.pth\"\n)\n\nprint(\"\\nModel saved: biomedclip_rsna.pth\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-12T02:25:08.028827Z","iopub.execute_input":"2026-05-12T02:25:08.029181Z","iopub.status.idle":"2026-05-12T03:01:49.632236Z","shell.execute_reply.started":"2026-05-12T02:25:08.029146Z","shell.execute_reply":"2026-05-12T03:01:49.631052Z"}},"outputs":[],"execution_count":null}]}