-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathfive_samples.py
More file actions
167 lines (146 loc) · 6.77 KB
/
Copy pathfive_samples.py
File metadata and controls
167 lines (146 loc) · 6.77 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
import os
import torch.nn as nn
from torchvision.models import resnet18, ResNet18_Weights
import torch
import torchvision
from tqdm import tqdm
from torchvision import transforms
import numpy as np
from sklearn.linear_model import LogisticRegression
import matplotlib.pyplot as plt
class ResNet18(nn.Module):
def __init__(self, pretrained=False, probing=False):
super(ResNet18, self).__init__()
if pretrained:
weights = ResNet18_Weights.IMAGENET1K_V1
self.transform = ResNet18_Weights.IMAGENET1K_V1.transforms()
self.resnet18 = resnet18(weights=weights)
else:
self.transform = transforms.Compose([transforms.Resize((224, 224)), transforms.ToTensor()])
self.resnet18 = resnet18()
in_features_dim = self.resnet18.fc.in_features
self.resnet18.fc = nn.Identity()
if probing:
for name, param in self.resnet18.named_parameters():
param.requires_grad = False
self.logistic_regression = nn.Linear(in_features_dim, 1)
def forward(self, x):
features = self.resnet18(x)
return self.logistic_regression(features)
def get_loaders(path, transform, batch_size):
"""
Get the data loaders for the train, validation and test sets.
:param path: The path to the 'whichfaceisreal' directory.
:param transform: The transform to apply to the images.
:param batch_size: The batch size.
:return: The train, validation and test data loaders.
"""
train_set = torchvision.datasets.ImageFolder(root=os.path.join(path, 'train'), transform=transform)
val_set = torchvision.datasets.ImageFolder(root=os.path.join(path, 'val'), transform=transform)
test_set = torchvision.datasets.ImageFolder(root=os.path.join(path, 'test'), transform=transform)
train_loader = torch.utils.data.DataLoader(train_set, batch_size=batch_size, shuffle=True)
val_loader = torch.utils.data.DataLoader(val_set, batch_size=batch_size, shuffle=False)
test_loader = torch.utils.data.DataLoader(test_set, batch_size=batch_size, shuffle=False)
return train_loader, val_loader, test_loader
def compute_accuracy(model, data_loader, device):
"""
Compute the accuracy of the model on the data in data_loader
:param model: The model to evaluate.
:param data_loader: The data loader.
:param device: The device to run the evaluation on.
:return: The accuracy of the model on the data in data_loader
"""
model.eval()
correct = 0
total = 0
predictions = []
with torch.no_grad():
for images, labels in data_loader:
images, labels = images.to(device), labels.to(device)
outputs = model(images)
predicted = (outputs > 0).long().squeeze(1)
predictions.extend(predicted.cpu().numpy())
total += labels.size(0)
correct += (predicted == labels).sum().item()
accuracy = correct / total
return accuracy, predictions
def run_training_epoch(model, criterion, optimizer, train_loader, device):
"""
Run a single training epoch
:param model: The model to train
:param criterion: The loss function
:param optimizer: The optimizer
:param train_loader: The data loader
:param device: The device to run the training on
:return: The average loss for the epoch.
"""
model.train()
total_loss = 0.0
for (imgs, labels) in tqdm(train_loader, total=len(train_loader)):
imgs, labels = imgs.to(device), labels.to(device)
optimizer.zero_grad()
outputs = model(imgs)
loss = criterion(outputs.squeeze(), labels.float())
loss.backward()
optimizer.step()
total_loss += loss.item()
return total_loss / len(train_loader)
# Set the random seed for reproducibility
torch.manual_seed(0)
### UNCOMMENT THE FOLLOWING LINES TO TRAIN THE MODEL ###
# From Scratch
model_scratch = ResNet18(pretrained=False, probing=False)
# Linear probing
# model_linear_probe = ResNet18(pretrained=True, probing=True)
# Fine-tuning
model_finetune = ResNet18(pretrained=True, probing=False)
transform = model_scratch.transform
batch_size = 32
num_of_epochs = 1
learning_rate = 0.01
path = 'C:/Users/Yehuda Frist/ML Methods/exercise_4/whichfaceisreal'
train_loader, val_loader, test_loader = get_loaders(path, transform, batch_size)
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
model_scratch = model_scratch.to(device)
model_finetune = model_finetune.to(device)
### Define the loss function and the optimizer
criterion = torch.nn.BCEWithLogitsLoss()
optimizer = torch.optim.Adam(model_scratch.parameters(), lr=learning_rate)
### Train the model
# Train the model
for epoch in range(num_of_epochs):
# Run a training epoch
loss_scratch = run_training_epoch(model_scratch, criterion, optimizer, train_loader, device)
loss_ft = run_training_epoch(model_finetune, criterion, optimizer, train_loader, device)
# Compute the accuracy
train_acc_scratch = compute_accuracy(model_scratch, train_loader, device)
train_acc_ft = compute_accuracy(model_finetune, train_loader, device)
# Compute the validation accuracy
val_acc_scratch, _ = compute_accuracy(model_scratch, val_loader, device)
val_acc_ft, _ = compute_accuracy(model_finetune, val_loader, device)
print(f'Model scratch: Epoch {epoch + 1}/{num_of_epochs}, Loss: {loss_scratch:.4f}, Val accuracy: {val_acc_scratch:.4f}')
print(f'Model FT: Epoch {epoch + 1}/{num_of_epochs}, Loss: {loss_ft:.4f}, Val accuracy: {val_acc_ft:.4f}')
_, scratch_pred = compute_accuracy(model_scratch, test_loader, device)
_, ft_pred = compute_accuracy(model_finetune, test_loader, device)
# Find samples correctly classified by linear probing but misclassified by training from scratch
misclassified_indices = \
[i for i, (true_label, pred_ft, pred_scratch)
in enumerate(zip(test_loader.dataset.targets, ft_pred, scratch_pred))
if pred_ft == true_label and pred_scratch != true_label]
print("Number of misclassified indices:", len(misclassified_indices))
print("Misclassified indices:", misclassified_indices)
# Visualize 5 samples
for idx in misclassified_indices[:5]:
print("Index:", idx)
true_label = val_loader.dataset.targets[idx]
pred_ft = ft_pred[idx]
pred_scratch = scratch_pred[idx]
print("True Label:", true_label)
print("Predicted by Fine tuning:", pred_ft)
print("Predicted by Scratch:", pred_scratch)
image, label = val_loader.dataset[idx]
plt.imshow(image.permute(1, 2, 0).numpy())
plt.title(f"True Label: {label}, "
f"Predicted by Fine tuning: {pred_ft}, "
f"Predicted by Scratch: {int(pred_scratch)}")
plt.show()