{"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"name":"python","version":"3.11.11","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceId":39272,"databundleVersionId":4629629,"sourceType":"competition"},{"sourceId":4866520,"sourceType":"datasetVersion","datasetId":2820722}],"dockerImageVersionId":31011,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# **A Beacon of Hope: Harnessing AI for Breast Cancer Detection**","metadata":{}},{"cell_type":"code","source":"import os\nfrom PIL import Image\nimport pydicom\n\n# import other libraries","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-28T06:38:57.297632Z","iopub.execute_input":"2025-04-28T06:38:57.297879Z","iopub.status.idle":"2025-04-28T06:38:57.869197Z","shell.execute_reply.started":"2025-04-28T06:38:57.297862Z","shell.execute_reply":"2025-04-28T06:38:57.868592Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def convert_dicom_to_jpg(dicom_path, jpg_path):\n    \"\"\"Converts a single DICOM file to a JPEG file with error handling.\"\"\"\n    try:\n        # Read the DICOM file\n        ds = pydicom.dcmread(dicom_path)\n        \n        # Extract pixel data\n        image = ds.pixel_array.astype(float)\n        \n        # Apply rescale slope and intercept if available (commonly for CT images)\n        if 'RescaleSlope' in ds and 'RescaleIntercept' in ds:\n            image = image * float(ds.RescaleSlope) + float(ds.RescaleIntercept)\n        \n        # Normalize the pixel values to the range 0-255\n        min_val = np.min(image)\n        max_val = np.max(image)\n        if max_val - min_val != 0:\n            image_normalized = (image - min_val) / (max_val - min_val) * 255.0\n        else:\n            image_normalized = np.zeros_like(image)\n        \n        image_normalized = image_normalized.astype(np.uint8)\n        \n        # Create a PIL image and save as JPEG\n        im = Image.fromarray(image_normalized)\n        im.save(jpg_path)\n        print(f\"Converted {dicom_path} to {jpg_path}\")\n        \n    except RuntimeError as e:\n        # This error likely indicates that decompression failed because of missing plugins.\n        print(f\"RuntimeError for file {dicom_path}: {e}. Skipping this file.\")\n    except Exception as e:\n        # Catch any other unexpected errors.\n        print(f\"An error occurred while converting {dicom_path}: {e}. Skipping this file.\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-28T06:39:24.443636Z","iopub.execute_input":"2025-04-28T06:39:24.444354Z","iopub.status.idle":"2025-04-28T06:39:24.450337Z","shell.execute_reply.started":"2025-04-28T06:39:24.444327Z","shell.execute_reply":"2025-04-28T06:39:24.449613Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def process_folder(input_folder, output_folder):\n    \"\"\"\n    Recursively processes the input_folder, converts all .dcm files,\n    and saves them into the output_folder, preserving the subfolder structure.\n    \"\"\"\n    for root, _, files in os.walk(input_folder):\n        for file in files:\n            if file.lower().endswith('.dcm'):\n                # Construct the full input file path\n                dicom_path = os.path.join(root, file)\n                \n                # Determine the relative path to recreate folder structure in output_folder\n                relative_path = os.path.relpath(root, input_folder)\n                output_dir = os.path.join(output_folder, relative_path)\n                os.makedirs(output_dir, exist_ok=True)\n                \n                # Create output file path by replacing .dcm with .jpg\n                jpg_filename = os.path.splitext(file)[0] + '.jpg'\n                jpg_path = os.path.join(output_dir, jpg_filename)\n                \n                # Convert and save the image, with error handling inside the conversion function\n                convert_dicom_to_jpg(dicom_path, jpg_path)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-28T06:39:42.379885Z","iopub.execute_input":"2025-04-28T06:39:42.380565Z","iopub.status.idle":"2025-04-28T06:39:42.385393Z","shell.execute_reply.started":"2025-04-28T06:39:42.380538Z","shell.execute_reply":"2025-04-28T06:39:42.384693Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Define the input and output directories\ninput_folder = '/kaggle/input/rsna-breast-cancer-detection/train_images'         # folder containing subfolders with DICOM files\noutput_folder = '/kaggle/working/jpeg_images'        # folder to store converted JPEG images\n\n# Process the folder\nprocess_folder(input_folder, output_folder)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-28T06:57:34.425481Z","iopub.execute_input":"2025-04-28T06:57:34.426114Z","execution_failed":"2025-04-28T09:30:34.281Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import pandas as pd\nimport os\n\n# 1. اقرأ ملف train.csv\ndf = pd.read_csv('/kaggle/input/rsna-breast-cancer-detection/train.csv')\n\n# 2. اضف عمود جديد لمسار الصورة\n# لاحظ إن كل صورة موجودة في فولدر اسمه برقم study_id\ndf['image_path'] = df.apply(lambda row: \n                            os.path.join('/kaggle/input/rsna-breast-cancer-detection/train_images',\n                                         str(row['patient_id']), \n                                         str(row['image_id']) + '.dcm'), axis=1)\n\n# 3. عرض أول 5 صفوف للتأكد\nprint(df.head())\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-28T09:30:58.815871Z","iopub.execute_input":"2025-04-28T09:30:58.816103Z","iopub.status.idle":"2025-04-28T09:30:59.300807Z","shell.execute_reply.started":"2025-04-28T09:30:58.816083Z","shell.execute_reply":"2025-04-28T09:30:59.300032Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from sklearn.model_selection import train_test_split\n\n# أولاً: قسم Train (80%) والباقي (20%)\ntrain_df, temp_df = train_test_split(df, test_size=0.2, random_state=42, stratify=df['cancer'])\n\n# ثانيًا: قسم الباقي (20%) إلى Validation (10%) وTest (10%)\nvalid_df, test_df = train_test_split(temp_df, test_size=0.5, random_state=42, stratify=temp_df['cancer'])\n\n# عرض أحجام الداتا عشان تتأكد\nprint(f\"Train size: {len(train_df)}\")\nprint(f\"Validation size: {len(valid_df)}\")\nprint(f\"Test size: {len(test_df)}\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-28T09:31:06.901108Z","iopub.execute_input":"2025-04-28T09:31:06.901617Z","iopub.status.idle":"2025-04-28T09:31:07.529846Z","shell.execute_reply.started":"2025-04-28T09:31:06.901594Z","shell.execute_reply":"2025-04-28T09:31:07.529219Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import torch\nfrom torch.utils.data import Dataset, DataLoader\nfrom torchvision import transforms\nfrom PIL import Image\nimport os\n\n# 1. تعريف التحويلات المطلوبة\ntransform = transforms.Compose([\n    transforms.Resize((512, 512)),         # تغيير حجم الصورة\n    transforms.ToTensor(),                 # تحويل الصورة إلى Tensor\n    transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])  # تطبيع الصورة\n])\n\n# 2. كلاس Dataset لتحميل الصور من DataFrame\nclass RSNADataset(Dataset):\n    def __init__(self, df, transform=None):\n        self.df = df\n        self.transform = transform\n\n    def __len__(self):\n        return len(self.df)\n\n    def __getitem__(self, idx):\n        # احصل على مسار الصورة والـ label من الـ DataFrame\n        img_path = self.df.iloc[idx]['image_path']\n        label = self.df.iloc[idx]['cancer']\n\n        # افتح الصورة باستخدام PIL\n        img = Image.open(img_path).convert('RGB')\n\n        # تطبيق التحويلات إذا كانت موجودة\n        if self.transform:\n            img = self.transform(img)\n\n        return img, label\n\n# 3. تحميل الـ Dataset باستخدام DataLoader مع batch_size المناسب\ntrain_dataset = RSNADataset(train_df, transform=transform)\nvalid_dataset = RSNADataset(valid_df, transform=transform)\ntest_dataset = RSNADataset(test_df, transform=transform)\n\ntrain_loader = DataLoader(train_dataset, batch_size=32, shuffle=True)\nvalid_loader = DataLoader(valid_dataset, batch_size=16, shuffle=False)\ntest_loader = DataLoader(test_dataset, batch_size=16, shuffle=False)\n\n# 4. عرض حجم الدفعة الأولى للتأكد\ntrain_images, train_labels = next(iter(train_loader))\nprint(f\"Train batch size: {train_images.size()}\")  # الحجم المتوقع: (32, 3, 512, 512)\n","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import matplotlib.pyplot as plt\nimport numpy as np\n\n# Function to show a batch of images\ndef show_sample_batch(loader, num_images=8):\n    # Load a batch of images and labels from the DataLoader\n    images, labels = next(iter(loader))\n    \n    # Set up the figure with the required number of subplots (num_images)\n    fig, axes = plt.subplots(2, 4, figsize=(12, 6))  # Adjust 2x4 grid for 8 images\n    axes = axes.ravel()\n    \n    # Show the images\n    for i in np.arange(num_images):\n        img = images[i].numpy().transpose((1, 2, 0))  # Convert from Tensor to HxWxC\n        img = np.clip(img, 0, 1)  # Ensure pixel values are between 0 and 1 for visualization\n        \n        axes[i].imshow(img)\n        axes[i].set_title(f\"Label: {labels[i].item()}\")\n        axes[i].axis('off')  # Remove axis for a cleaner look\n    \n    plt.show()\n\n# Call the function with the train_loader\nshow_sample_batch(train_loader, num_images=8)  # Display 8 images\n","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import torch\nimport torch.nn as nn\nimport torch.optim as optim\nfrom torchvision import models\nfrom efficientnet_pytorch import EfficientNet\n\n# 1. تحميل EfficientNetB5\nmodel = EfficientNet.from_pretrained('efficientnet-b5')\n\n# 2. تخصيص الطبقات النهائية لتناسب مهمتنا (2 فئات: سرطان / لا سرطان)\nmodel._fc = nn.Sequential(\n    nn.Dropout(p=0.3),  # إضافة طبقة Dropout لتقليل الإفراط في التعميم\n    nn.Linear(in_features=model._fc.in_features, out_features=1),  # 1 output node (0 or 1)\n    nn.Sigmoid()  # لتوليد القيمة بين 0 و 1 (احتمال السرطان)\n)\n\n# 3. اختيار دالة الخسارة (Binary Cross Entropy) + دالة التقييم (Accuracy)\ncriterion = nn.BCELoss()  # خسارة لثنائية التصنيف\noptimizer = optim.Adam(model.parameters(), lr=1e-4)\n\n# 4. تدريب النموذج باستخدام DataLoader و GPU إذا كان متاح\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\nmodel = model.to(device)\n\n# 5. دالة تدريب النموذج\ndef train_model(model, train_loader, criterion, optimizer, num_epochs=5):\n    for epoch in range(num_epochs):\n        model.train()  # تأكد أن الموديل في وضع التدريب\n        running_loss = 0.0\n        correct_preds = 0\n        total_preds = 0\n        \n        for inputs, labels in train_loader:\n            inputs, labels = inputs.to(device), labels.to(device)\n            \n            optimizer.zero_grad()  # مسح التدرجات السابقة\n            \n            # تحويل الـ inputs عبر النموذج\n            outputs = model(inputs)\n            \n            loss = criterion(outputs.squeeze(), labels.float())  # حساب الخسارة\n            loss.backward()  # حساب التدرجات\n            \n            optimizer.step()  # تحديث الأوزان\n            \n            # حساب الدقة\n            preds = (outputs.squeeze() > 0.5).float()  # التنبؤات (سرطان أو لا)\n            correct_preds += (preds == labels).sum().item()\n            total_preds += labels.size(0)\n            \n            running_loss += loss.item()\n\n        epoch_loss = running_loss / len(train_loader)\n        epoch_acc = correct_preds / total_preds\n\n        print(f\"Epoch {epoch+1}/{num_epochs}, Loss: {epoch_loss:.4f}, Accuracy: {epoch_acc:.4f}\")\n\n# 6. دالة التقييم\ndef evaluate_model(model, test_loader):\n    model.eval()  # وضع التقييم\n    correct_preds = 0\n    total_preds = 0\n    \n    with torch.no_grad():  # تعطيل حساب التدرجات للتقييم\n        for inputs, labels in test_loader:\n            inputs, labels = inputs.to(device), labels.to(device)\n            outputs = model(inputs)\n            \n            preds = (outputs.squeeze() > 0.5).float()\n            correct_preds += (preds == labels).sum().item()\n            total_preds += labels.size(0)\n\n    accuracy = correct_preds / total_preds\n    print(f\"Test Accuracy: {accuracy:.4f}\")\n\n# 7. تدريب النموذج\ntrain_model(model, train_loader, criterion, optimizer, num_epochs=5)\n\n# 8. تقييم النموذج\nevaluate_model(model, test_loader)\n","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from sklearn.metrics import precision_score, recall_score, f1_score\n\n# دالة لتدريب النموذج مع مراقبة الدقة و الحساسية\ndef train_and_evaluate(model, train_loader, valid_loader, criterion, optimizer, num_epochs=5):\n    best_acc = 0\n    for epoch in range(num_epochs):\n        model.train()  # وضع النموذج في وضع التدريب\n        running_loss = 0.0\n        all_preds = []\n        all_labels = []\n        \n        for inputs, labels in train_loader:\n            inputs, labels = inputs.to(device), labels.to(device)\n            \n            optimizer.zero_grad()  # مسح التدرجات السابقة\n            \n            # المرور بالصور عبر النموذج\n            outputs = model(inputs)\n            \n            # حساب الخسارة\n            loss = criterion(outputs.squeeze(), labels.float())\n            loss.backward()\n            optimizer.step()\n            \n            running_loss += loss.item()\n            all_preds.extend(outputs.squeeze().cpu().detach().numpy())\n            all_labels.extend(labels.cpu().detach().numpy())\n        \n        epoch_loss = running_loss / len(train_loader)\n        epoch_preds = (np.array(all_preds) > 0.5).astype(int)\n        epoch_acc = np.mean(epoch_preds == np.array(all_labels))\n        epoch_precision = precision_score(all_labels, epoch_preds)\n        epoch_recall = recall_score(all_labels, epoch_preds)\n        epoch_f1 = f1_score(all_labels, epoch_preds)\n        \n        print(f\"Epoch {epoch+1}/{num_epochs}, Loss: {epoch_loss:.4f}, Accuracy: {epoch_acc:.4f}, Precision: {epoch_precision:.4f}, Recall: {epoch_recall:.4f}, F1-Score: {epoch_f1:.4f}\")\n\n        # التقييم بعد كل epoch على البيانات التحقق\n        model.eval()  # وضع التقييم\n        valid_preds = []\n        valid_labels = []\n        with torch.no_grad():\n            for inputs, labels in valid_loader:\n                inputs, labels = inputs.to(device), labels.to(device)\n                outputs = model(inputs)\n                valid_preds.extend(outputs.squeeze().cpu().detach().numpy())\n                valid_labels.extend(labels.cpu().detach().numpy())\n        \n        valid_preds = (np.array(valid_preds) > 0.5).astype(int)\n        valid_acc = np.mean(valid_preds == np.array(valid_labels))\n        \n        print(f\"Validation Accuracy: {valid_acc:.4f}\")\n\n        # حفظ أفضل نموذج بناءً على الدقة\n        if valid_acc > best_acc:\n            best_acc = valid_acc\n            torch.save(model.state_dict(), 'best_model.pth')\n\n# التدريب مع مراقبة الأداء\ntrain_and_evaluate(model, train_loader, valid_loader, criterion, optimizer, num_epochs=5)\n","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}