mobadara commited on
Commit
1efb6ba
·
1 Parent(s): c87906b

Created using Colab

Browse files
notebooks/01_model_training_and_fine_tuning.ipynb CHANGED
@@ -5,7 +5,7 @@
5
  "colab": {
6
  "provenance": [],
7
  "gpuType": "T4",
8
- "authorship_tag": "ABX9TyM5Fah77XuALjsYEAXaCFqE",
9
  "include_colab_link": true
10
  },
11
  "kernelspec": {
@@ -44,13 +44,25 @@
44
  },
45
  {
46
  "cell_type": "code",
47
- "execution_count": 2,
 
 
 
 
 
 
 
 
 
 
 
 
48
  "metadata": {
49
  "colab": {
50
  "base_uri": "https://localhost:8080/"
51
  },
52
  "id": "IGGkAlCRNlro",
53
- "outputId": "919ad755-854c-49b1-a812-43d413128bb3"
54
  },
55
  "outputs": [
56
  {
@@ -69,6 +81,9 @@
69
  "from torchvision import datasets, transforms, models\n",
70
  "from torch.utils.data import DataLoader\n",
71
  "import os\n",
 
 
 
72
  "import numpy as np\n",
73
  "\n",
74
  "# Configure the device\n",
@@ -122,9 +137,9 @@
122
  "base_uri": "https://localhost:8080/"
123
  },
124
  "id": "ikceEotXOU_1",
125
- "outputId": "367466cb-bd4f-4de8-b8c6-1142abda4b49"
126
  },
127
- "execution_count": 3,
128
  "outputs": [
129
  {
130
  "output_type": "stream",
@@ -135,11 +150,145 @@
135
  }
136
  ]
137
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
138
  {
139
  "cell_type": "code",
140
  "source": [],
141
  "metadata": {
142
- "id": "I6obJOa1QYMy"
143
  },
144
  "execution_count": null,
145
  "outputs": []
 
5
  "colab": {
6
  "provenance": [],
7
  "gpuType": "T4",
8
+ "authorship_tag": "ABX9TyMscbUSeB2qw1Lue5xsmWqV",
9
  "include_colab_link": true
10
  },
11
  "kernelspec": {
 
44
  },
45
  {
46
  "cell_type": "code",
47
+ "source": [
48
+ "%%capture install_output\n",
49
+ "!pip install optuna"
50
+ ],
51
+ "metadata": {
52
+ "id": "OwBert5NpQwF"
53
+ },
54
+ "execution_count": 5,
55
+ "outputs": []
56
+ },
57
+ {
58
+ "cell_type": "code",
59
+ "execution_count": 6,
60
  "metadata": {
61
  "colab": {
62
  "base_uri": "https://localhost:8080/"
63
  },
64
  "id": "IGGkAlCRNlro",
65
+ "outputId": "778acab3-e0b5-4c40-a995-ded91103b0a4"
66
  },
67
  "outputs": [
68
  {
 
81
  "from torchvision import datasets, transforms, models\n",
82
  "from torch.utils.data import DataLoader\n",
83
  "import os\n",
84
+ "import optuna\n",
85
+ "import time\n",
86
+ "import copy\n",
87
  "import numpy as np\n",
88
  "\n",
89
  "# Configure the device\n",
 
137
  "base_uri": "https://localhost:8080/"
138
  },
139
  "id": "ikceEotXOU_1",
140
+ "outputId": "077395cc-6cf0-42f5-e0b3-65a1e322b5a8"
141
  },
142
+ "execution_count": 2,
143
  "outputs": [
144
  {
145
  "output_type": "stream",
 
150
  }
151
  ]
152
  },
153
+ {
154
+ "cell_type": "markdown",
155
+ "source": [
156
+ "## **Initializing Datasets and DataLoaders**\n",
157
+ "\n",
158
+ "With our transforms defined, we will use PyTorch's `ImageFolder` to automatically read our directory structure and assign the correct labels (0 for Normal, 1 for Pneumonia).\n",
159
+ "\n",
160
+ "We will then wrap these datasets in `DataLoaders`. DataLoaders handle the heavy lifting of batching the images, shuffling them, and pushing them to the GPU. We are setting the `batch_size` to 64 to fully utilize the Google Colab GPU memory."
161
+ ],
162
+ "metadata": {
163
+ "id": "odyLrQARmmn-"
164
+ }
165
+ },
166
+ {
167
+ "cell_type": "code",
168
+ "source": [
169
+ "# 1. Create the Dataset objects\n",
170
+ "train_dataset = datasets.ImageFolder(train_dir, transform=train_transforms)\n",
171
+ "val_dataset = datasets.ImageFolder(val_dir, transform=eval_transforms)\n",
172
+ "test_dataset = datasets.ImageFolder(test_dir, transform=eval_transforms)\n",
173
+ "\n",
174
+ "print(f\"Training images: {len(train_dataset)}\")\n",
175
+ "print(f\"Validation images: {len(val_dataset)}\")\n",
176
+ "print(f\"Testing images: {len(test_dataset)}\")\n",
177
+ "print(f\"Detected Classes: {train_dataset.classes}\")\n",
178
+ "\n",
179
+ "# 2. Create the DataLoaders\n",
180
+ "BATCH_SIZE = 64\n",
181
+ "\n",
182
+ "# We shuffle the training data so the model doesn't learn sequence patterns\n",
183
+ "train_loader = DataLoader(train_dataset, batch_size=BATCH_SIZE, shuffle=True, num_workers=2, pin_memory=True)\n",
184
+ "\n",
185
+ "# We do not need to shuffle validation/testing data\n",
186
+ "val_loader = DataLoader(val_dataset, batch_size=BATCH_SIZE, shuffle=False, num_workers=2, pin_memory=True)\n",
187
+ "test_loader = DataLoader(test_dataset, batch_size=BATCH_SIZE, shuffle=False, num_workers=2, pin_memory=True)\n",
188
+ "\n",
189
+ "print(\"DataLoaders are ready!\")"
190
+ ],
191
+ "metadata": {
192
+ "id": "I6obJOa1QYMy",
193
+ "outputId": "3a3b35dc-01e5-4980-bdfc-0958467a5f24",
194
+ "colab": {
195
+ "base_uri": "https://localhost:8080/"
196
+ }
197
+ },
198
+ "execution_count": 3,
199
+ "outputs": [
200
+ {
201
+ "output_type": "stream",
202
+ "name": "stdout",
203
+ "text": [
204
+ "Training images: 5216\n",
205
+ "Validation images: 16\n",
206
+ "Testing images: 624\n",
207
+ "Detected Classes: ['NORMAL', 'PNEUMONIA']\n",
208
+ "DataLoaders are ready!\n"
209
+ ]
210
+ }
211
+ ]
212
+ },
213
+ {
214
+ "cell_type": "markdown",
215
+ "source": [
216
+ "## **Model Architecture and Class Weighting**\n",
217
+ "\n",
218
+ "Instead of training a model from scratch, we use **Transfer Learning**. We will download DenseNet121 (which already knows how to detect edges, textures, and shapes from millions of standard images) and replace its final classification layer with a custom one designed specifically for our two classes (Normal vs. Pneumonia).\n",
219
+ "\n",
220
+ "Simultaneously, we will calculate the inverse frequency of our classes to generate **Class Weights**, injecting them directly into the PyTorch loss function to penalize the model more heavily if it misclassifies a minority \"Normal\" scan."
221
+ ],
222
+ "metadata": {
223
+ "id": "MPPAHhh2nlVz"
224
+ }
225
+ },
226
+ {
227
+ "cell_type": "code",
228
+ "source": [
229
+ "# --- 1. Handling the Imbalance (Class Weights) ---\n",
230
+ "# ImageFolder automatically stores the label for every image in the .targets attribute\n",
231
+ "labels = train_dataset.targets\n",
232
+ "class_counts = np.bincount(labels)\n",
233
+ "total_samples = len(labels)\n",
234
+ "num_classes = len(train_dataset.classes)\n",
235
+ "\n",
236
+ "# Inverse frequency formula: Weight = Total / (NumClasses * ClassCount)\n",
237
+ "weights = total_samples / (num_classes * class_counts)\n",
238
+ "class_weights = torch.FloatTensor(weights).to(device)\n",
239
+ "\n",
240
+ "print(f\"Class Counts: NORMAL={class_counts[0]}, PNEUMONIA={class_counts[1]}\")\n",
241
+ "print(f\"Applied Weights: NORMAL={weights[0]:.4f}, PNEUMONIA={weights[1]:.4f}\\n\")\n",
242
+ "\n",
243
+ "# --- 2. Model Architecture (DenseNet121) ---\n",
244
+ "# Download the pre-trained neural network\n",
245
+ "model = models.densenet121(weights=models.DenseNet121_Weights.IMAGENET1K_V1)\n",
246
+ "\n",
247
+ "# Freeze the core convolutional layers so we don't destroy the pre-trained visual features\n",
248
+ "for param in model.parameters():\n",
249
+ " param.requires_grad = False\n",
250
+ "\n",
251
+ "# Replace the final classification layer (originally 1000 classes) with our custom 2-class head\n",
252
+ "num_ftrs = model.classifier.in_features\n",
253
+ "\n",
254
+ "# Push the entire model architecture to the GPU\n",
255
+ "model = model.to(device)\n",
256
+ "\n",
257
+ "# --- 3. Loss Function and Optimizer ---\n",
258
+ "# Inject our calculated class weights into the loss function\n",
259
+ "criterion = nn.CrossEntropyLoss(weight=class_weights)\n",
260
+ "\n",
261
+ "# We only want the optimizer to update our newly added classifier layers\n",
262
+ "optimizer = optim.Adam(model.classifier.parameters(), lr=0.001)\n",
263
+ "\n",
264
+ "print(\"Model architecture, optimizer, and weighted loss function configured successfully!\")"
265
+ ],
266
+ "metadata": {
267
+ "id": "3cTqFqcvnVG2",
268
+ "outputId": "19533754-f990-4485-b848-d9c2be3f782b",
269
+ "colab": {
270
+ "base_uri": "https://localhost:8080/"
271
+ }
272
+ },
273
+ "execution_count": 7,
274
+ "outputs": [
275
+ {
276
+ "output_type": "stream",
277
+ "name": "stdout",
278
+ "text": [
279
+ "Class Counts: NORMAL=1341, PNEUMONIA=3875\n",
280
+ "Applied Weights: NORMAL=1.9448, PNEUMONIA=0.6730\n",
281
+ "\n",
282
+ "Model architecture, optimizer, and weighted loss function configured successfully!\n"
283
+ ]
284
+ }
285
+ ]
286
+ },
287
  {
288
  "cell_type": "code",
289
  "source": [],
290
  "metadata": {
291
+ "id": "HrV6nUsjodCM"
292
  },
293
  "execution_count": null,
294
  "outputs": []