Skip to content

Commit 4bd0109

Browse files
committed
moving folders
1 parent e3ef012 commit 4bd0109

4 files changed

+2282
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,334 @@
1+
{
2+
"cells": [
3+
{
4+
"cell_type": "code",
5+
"execution_count": null,
6+
"metadata": {
7+
"id": "51FPMElM8M4b"
8+
},
9+
"outputs": [],
10+
"source": [
11+
"!pip install torchmetrics"
12+
]
13+
},
14+
{
15+
"cell_type": "code",
16+
"execution_count": null,
17+
"metadata": {
18+
"id": "6aISv-P98Tjn"
19+
},
20+
"outputs": [],
21+
"source": [
22+
"import io\n",
23+
"import ast\n",
24+
"import torch\n",
25+
"import pickle\n",
26+
"import numpy as np\n",
27+
"from PIL import Image\n",
28+
"\n",
29+
"import torch.nn as nn\n",
30+
"import torch.optim as optim\n",
31+
"from torchvision import models\n",
32+
"import torch.nn.functional as F\n",
33+
"from torchvision import transforms\n",
34+
"from torch.utils.data import random_split, DataLoader, TensorDataset\n",
35+
"\n",
36+
"from torch.utils.tensorboard import SummaryWriter\n",
37+
"from torchmetrics.classification import MultilabelPrecision, MultilabelRecall, MultilabelF1Score"
38+
]
39+
},
40+
{
41+
"cell_type": "code",
42+
"execution_count": null,
43+
"metadata": {
44+
"id": "2u446Niv8Vb2"
45+
},
46+
"outputs": [],
47+
"source": [
48+
"def load_data(original_data_pickle, batch_size, train_percent, val_percent, target_size=(224, 224)):\n",
49+
" images = []\n",
50+
" demographics = []\n",
51+
" labels= []\n",
52+
"\n",
53+
" resize_transform = transforms.Compose([\n",
54+
" transforms.Resize(target_size),\n",
55+
" transforms.ToTensor()\n",
56+
" ])\n",
57+
"\n",
58+
" with open(original_data_pickle, 'rb') as f:\n",
59+
" data = pickle.load(f)\n",
60+
"\n",
61+
" for item in data.values():\n",
62+
"\n",
63+
" \"\"\"\n",
64+
" The image data we get would be in bytes. We need to open it and convert it to grey scale and then resize. Recheck it. What are we doing with resizing before then?\n",
65+
" \"\"\"\n",
66+
" image_data = item['image_data']\n",
67+
" image = Image.open(io.BytesIO(image_data)).convert('L')\n",
68+
" image = resize_transform(image) # Resizing and converting to tensor with shape (1, H, W) --> got an error without it\n",
69+
"\n",
70+
" label= item['image_label']\n",
71+
" label = ast.literal_eval(label)\n",
72+
" label = np.array(label, dtype=int)\n",
73+
"\n",
74+
" age = torch.tensor([item['age']], dtype=torch.float32)\n",
75+
" gender = torch.tensor(item['gender'], dtype=torch.float32)\n",
76+
"\n",
77+
" images.append(image)\n",
78+
" demographics.append(torch.cat([age, gender]))\n",
79+
" labels.append(label)\n",
80+
"\n",
81+
" \"\"\"\n",
82+
" Stacking images and demographics.\n",
83+
" images Shape: (num_samples, channels, height, width)\n",
84+
" demographics Shape: (num_samples, num_features)\n",
85+
" \"\"\"\n",
86+
" images = torch.stack(images)\n",
87+
" demographics = torch.stack(demographics)\n",
88+
" labels = torch.stack([torch.tensor(label, dtype=torch.long) for label in labels])\n",
89+
" #labels = torch.tensor(labels, dtype= torch.long)\n",
90+
"\n",
91+
" dataset = TensorDataset(images, demographics, labels)\n",
92+
"\n",
93+
" train_size = int(train_percent * len(dataset))\n",
94+
" val_size = int(val_percent * len(dataset))\n",
95+
" test_size = len(dataset) - train_size - val_size\n",
96+
"\n",
97+
" train_dataset, val_dataset, test_dataset = random_split(dataset, [train_size, val_size, test_size])\n",
98+
"\n",
99+
" train_loader = DataLoader(train_dataset, batch_size=32, shuffle=True)\n",
100+
" val_loader = DataLoader(val_dataset, batch_size=32, shuffle=False)\n",
101+
" test_loader = DataLoader(test_dataset, batch_size=32, shuffle=False)\n",
102+
"\n",
103+
" print(f\"Training samples: {len(train_dataset)}, Validation samples: {len(val_dataset)}, Test samples: {len(test_dataset)}\")\n",
104+
"\n",
105+
" return train_loader, val_loader, test_loader"
106+
]
107+
},
108+
{
109+
"cell_type": "code",
110+
"execution_count": null,
111+
"metadata": {},
112+
"outputs": [],
113+
"source": [
114+
"class CustomResNet18(nn.Module):\n",
115+
" def __init__(self, num_demographics, num_classes=15):\n",
116+
" super(CustomResNet18, self).__init__()\n",
117+
"\n",
118+
" self.resnet = models.resnet18(weights=models.ResNet18_Weights.IMAGENET1K_V1)\n",
119+
"\n",
120+
" # Modifying the first convolutional layer to accept grayscale images (1 channel) --> generally ResNet expects 3 channels\n",
121+
" #for RGB\n",
122+
" self.resnet.conv1 = nn.Conv2d(1, 64, kernel_size=(7, 7), stride=(2, 2), padding=(3, 3), bias=False)\n",
123+
"\n",
124+
" # Removing the final fully connected layer in ResNet\n",
125+
" self.resnet = nn.Sequential(*list(self.resnet.children())[:-1])\n",
126+
"\n",
127+
" # this fc processes the demographics (age + gender)\n",
128+
" self.demographics_fc = nn.Sequential(\n",
129+
" nn.Linear(num_demographics, 32),\n",
130+
" nn.ReLU(),\n",
131+
" nn.Dropout(0.5)\n",
132+
" )\n",
133+
"\n",
134+
" self.fc = nn.Linear(512 + 32, num_classes) # 512 from ResNet, 32 from demographics_fc, can make it 64?\n",
135+
"\n",
136+
" def forward(self, images, demographics):\n",
137+
" x = self.resnet(images) # Passing images through the modified ResNet (without its last layer)\n",
138+
" x = x.view(x.size(0), -1) # Flattening the ResNet output\n",
139+
"\n",
140+
" demographics_features = self.demographics_fc(demographics)\n",
141+
" x = torch.cat((x, demographics_features), dim=1)\n",
142+
"\n",
143+
" #print(\"Shape after concatenating demographics:\", x.shape)\n",
144+
"\n",
145+
" x = self.fc(x)\n",
146+
" #print(\"Output shape before returning:\", x.shape)\n",
147+
"\n",
148+
" return x"
149+
]
150+
},
151+
{
152+
"cell_type": "code",
153+
"execution_count": null,
154+
"metadata": {
155+
"id": "sg1GtW1Z8dWX"
156+
},
157+
"outputs": [],
158+
"source": [
159+
"writer = SummaryWriter(\"runs/CustomResNet18_experiment\")\n",
160+
"\n",
161+
"def freeze_unfreeze_layers(model, freeze=True, layers_to_train=[\"layer4\", \"fc\"]):\n",
162+
" for name, param in model.named_parameters():\n",
163+
" if any(layer in name for layer in layers_to_train):\n",
164+
" param.requires_grad = not freeze\n",
165+
" else:\n",
166+
" param.requires_grad = freeze\n",
167+
"\n",
168+
"\n",
169+
"def train_model(train_loader, val_loader, model, criterion, optimizer, num_epochs= 10):\n",
170+
"\n",
171+
" model.train()\n",
172+
" for epoch in range(num_epochs):\n",
173+
" running_loss = 0.0\n",
174+
" for inputs, demographics, labels in train_loader:\n",
175+
" inputs, demographics, labels = inputs.to(device), demographics.to(device), labels.to(device)\n",
176+
"\n",
177+
" # Repeating grayscale images to make them 3 channels - this is not needed now since I changed the ResNet to accept grayscale,\n",
178+
" #in-general ResNet expects RGB images ig\n",
179+
"\n",
180+
" #inputs = inputs.repeat(1, 3, 1, 1)\n",
181+
"\n",
182+
" optimizer.zero_grad()\n",
183+
"\n",
184+
" outputs = model(inputs, demographics)\n",
185+
" loss = criterion(outputs, labels.float())\n",
186+
"\n",
187+
" loss.backward()\n",
188+
" optimizer.step()\n",
189+
"\n",
190+
" running_loss += loss.item()\n",
191+
"\n",
192+
" avg_train_loss = running_loss / len(train_loader)\n",
193+
" writer.add_scalar(\"Loss/Train\", avg_train_loss, epoch)\n",
194+
"\n",
195+
"\n",
196+
" model.eval()\n",
197+
" val_loss = 0.0\n",
198+
" correct = 0\n",
199+
" total = 0\n",
200+
" with torch.no_grad():\n",
201+
" for inputs, demographics, labels in val_loader:\n",
202+
" inputs, demographics, labels = inputs.to(device), demographics.to(device), labels.to(device)\n",
203+
" outputs = model(inputs, demographics)\n",
204+
"\n",
205+
" val_loss += criterion(outputs, labels.float()).item()\n",
206+
" probabilities = torch.sigmoid(outputs)\n",
207+
" predicted = (probabilities >= 0.5).int()\n",
208+
"\n",
209+
" correct += (predicted == labels).sum().item()\n",
210+
" total += labels.numel()\n",
211+
"\n",
212+
" avg_val_loss = val_loss / len(val_loader)\n",
213+
" val_accuracy = 100 * correct / total\n",
214+
" writer.add_scalar(\"Loss/Validation\", avg_val_loss, epoch)\n",
215+
" writer.add_scalar(\"Accuracy/Validation\", val_accuracy, epoch)\n",
216+
"\n",
217+
" accuracy = 100 * correct / total\n",
218+
" print(f\"Epoch {epoch+1}/{num_epochs}, Training Loss: {avg_train_loss}, Validation Loss: {avg_val_loss}, Validation Accuracy: {val_accuracy}%\")"
219+
]
220+
},
221+
{
222+
"cell_type": "code",
223+
"execution_count": null,
224+
"metadata": {
225+
"id": "vV4_0dnN8r4I"
226+
},
227+
"outputs": [],
228+
"source": [
229+
"def evaluate_model(test_loader, model, criterion, precision_metric, recall_metric, f1_metric, confidence= 0.3):\n",
230+
" model.eval()\n",
231+
" correct = 0\n",
232+
" total = 0\n",
233+
" test_loss = 0.0\n",
234+
"\n",
235+
" precision_metric.reset()\n",
236+
" recall_metric.reset()\n",
237+
" f1_metric.reset()\n",
238+
"\n",
239+
" with torch.no_grad():\n",
240+
" for inputs, demographics, labels in test_loader:\n",
241+
" inputs, demographics, labels = inputs.to(device), demographics.to(device), labels.to(device)\n",
242+
" outputs = model(inputs, demographics)\n",
243+
"\n",
244+
" test_loss += criterion(outputs, labels.float()).item()\n",
245+
"\n",
246+
" probabilities = torch.sigmoid(outputs)\n",
247+
" predicted = (probabilities >= confidence).int()\n",
248+
"\n",
249+
" correct += (predicted == labels).sum().item()\n",
250+
" total += labels.numel()\n",
251+
"\n",
252+
" #print(\"predicted:\", predicted)\n",
253+
" #print(\"labels: \", labels)\n",
254+
"\n",
255+
" precision_metric.update(predicted, labels)\n",
256+
" recall_metric.update(predicted, labels)\n",
257+
" f1_metric.update(predicted, labels)\n",
258+
"\n",
259+
" test_accuracy = 100 * correct / total\n",
260+
" precision = precision_metric.compute().item()\n",
261+
" recall = recall_metric.compute().item()\n",
262+
" f1_score = f1_metric.compute().item()\n",
263+
" avg_test_loss = test_loss / len(test_loader)\n",
264+
"\n",
265+
" writer.add_scalar(\"Loss/Test\", avg_test_loss)\n",
266+
" writer.add_scalar(\"Accuracy/Test\", test_accuracy)\n",
267+
" writer.add_scalar(\"Precision/Test\", precision)\n",
268+
" writer.add_scalar(\"Recall/Test\", recall)\n",
269+
" writer.add_scalar(\"F1-Score/Test\", f1_score)\n",
270+
"\n",
271+
" print(f'Test Loss: {avg_test_loss:.4f}')\n",
272+
" print(f'Test Accuracy: {test_accuracy:.2f}%')\n",
273+
" print(f'Test Precision: {precision:.4f}, Recall: {recall:.4f}, F1-score: {f1_score:.4f}')"
274+
]
275+
},
276+
{
277+
"cell_type": "code",
278+
"execution_count": null,
279+
"metadata": {
280+
"id": "Po2rD48u8v-L"
281+
},
282+
"outputs": [],
283+
"source": [
284+
"if __name__ == \"__main__\":\n",
285+
" config = {\n",
286+
" \"file_path\": \"preprocessed_dummy_data.pkl\",\n",
287+
" \"batch_size\": 32,\n",
288+
" \"num_epochs\": 10,\n",
289+
" \"learning_rate\": 1e-5,\n",
290+
" \"num_demographics\": 3,\n",
291+
" \"num_classes\": 15,\n",
292+
" \"train_percent\": 0.7,\n",
293+
" \"val_percent\": 0.1\n",
294+
" }\n",
295+
"\n",
296+
" device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\n",
297+
" train_loader, val_loader, test_loader = load_data(config[\"file_path\"], config[\"batch_size\"], config[\"train_percent\"], config[\"val_percent\"])\n",
298+
"\n",
299+
" model = CustomResNet18(num_demographics=config[\"num_demographics\"], num_classes=config[\"num_classes\"]).to(device)\n",
300+
" freeze_unfreeze_layers(model, freeze=True, layers_to_train=[\"layer4\", \"fc\"])\n",
301+
"\n",
302+
" criterion = nn.BCEWithLogitsLoss()\n",
303+
" optimizer = optim.Adam(filter(lambda p: p.requires_grad, model.parameters()), lr=config[\"learning_rate\"])\n",
304+
"\n",
305+
" \"\"\"precision_metric = torchmetrics.Precision(average='micro').to(device)\n",
306+
" recall_metric = torchmetrics.Recall(average='micro').to(device)\n",
307+
" f1_metric = torchmetrics.F1Score(average='micro').to(device)\"\"\"\n",
308+
"\n",
309+
" precision_metric = MultilabelPrecision(num_labels= config[\"num_classes\"], average='macro').to(device)\n",
310+
" recall_metric = MultilabelRecall(num_labels= config[\"num_classes\"], average='macro').to(device)\n",
311+
" f1_metric = MultilabelF1Score(num_labels= config[\"num_classes\"], average='macro').to(device)\n",
312+
"\n",
313+
" train_model(train_loader, val_loader, model, criterion, optimizer, config[\"num_epochs\"])\n",
314+
" evaluate_model(test_loader, model, criterion, precision_metric, recall_metric, f1_metric)\n",
315+
"\n",
316+
" writer.close()"
317+
]
318+
}
319+
],
320+
"metadata": {
321+
"colab": {
322+
"provenance": []
323+
},
324+
"kernelspec": {
325+
"display_name": "Python 3",
326+
"name": "python3"
327+
},
328+
"language_info": {
329+
"name": "python"
330+
}
331+
},
332+
"nbformat": 4,
333+
"nbformat_minor": 0
334+
}

0 commit comments

Comments
 (0)