|
|
|
"""UpsideDown.ipynb |
|
|
|
Automatically generated by Colaboratory. |
|
|
|
Original file is located at |
|
https://colab.research.google.com/drive/12aS57Wk69CKJyuVVNvXjyAU6mPo1dP4_ |
|
|
|
# Fatima Fellowship Quick Coding Challenge (Pick 1) |
|
|
|
Thank you for applying to the Fatima Fellowship. To help us select the Fellows and assess your ability to do machine learning research, we are asking that you complete a short coding challenge. Please pick **1 of these 5** coding challenges, whichever is most aligned with your interests. |
|
|
|
**Due date: 1 week** |
|
|
|
**How to submit**: Please make a copy of this colab notebook, add your code and results, and submit your colab notebook to the submission link below. If you have never used a colab notebook, [check out this video](https://www.youtube.com/watch?v=i-HnvsehuSw). |
|
|
|
**Submission link**: https://airtable.com/shrXy3QKSsO2yALd3 |
|
|
|
# 1. Deep Learning for Vision |
|
|
|
**Upside down detector**: Train a model to detect if images are upside down |
|
|
|
* Pick a dataset of natural images (we suggest looking at datasets on the [Hugging Face Hub](https://huggingface.co/datasets?task_categories=task_categories:image-classification&sort=downloads)) |
|
* Synthetically turn some of images upside down. Create a training and test set. |
|
* Build a neural network (using Tensorflow, PyTorch, or any framework you like) |
|
* Train it to classify image orientation until a reasonable accuracy is reached |
|
* [Upload the the model to the Hugging Face Hub](https://huggingface.co/docs/hub/adding-a-model), and add a link to your model below. |
|
* Look at some of the images that were classified incorrectly. Please explain what you might do to improve your model's performance on these images in the future (you do not need to impelement these suggestions) |
|
|
|
**Submission instructions**: Please write your code below and include some examples of images that were classified |
|
|
|
# Upside down detector: Train a model to detect if images are upside down |
|
* Pick a dataset of natural images \ |
|
Pick a dataset of natural images (we suggest looking at datasets on the Hugging Face Hub) \ |
|
We chose a fine-grained images dataset of faces \ |
|
description: |
|
|
|
My model is an inverted image detector and can help detect if images are inverted with 99% accuracy. \ |
|
I used a dataset containing people with and without masks. I trained my model on ~ 300 images of people without masks and tested it on ~ 60 of the same images distribution: \ |
|
author = {Prasoon Kottarathil}, \ |
|
title = {Face Mask Lite Dataset}, \ |
|
year = {2020}, \ |
|
publisher = {kaggle}, \ |
|
journal = {Kaggle Data}, \ |
|
how published = {\url{https://www.kaggle.com/prasoonkottarathil/face-mask-lite-dataset}} \ |
|
|
|
|
|
All Images in this dataset are Generated Using Style GAN-2, \ |
|
10,000 HD images in each Folder With mask and without mask |
|
|
|
|
|
* Synthetically turn some of images upside down. Create a training and test set. |
|
* Build a neural network (using Tensorflow, PyTorch, or any framework you like) |
|
* Train it to classify image orientation until a reasonable accuracy is reached |
|
* [Upload the the model to the Hugging Face Hub](https://huggingface.co/docs/hub/adding-a-model), and add a link to your model below. |
|
|
|
link: https://huggingface.co/DIANKHA/upside-down/tree/main |
|
|
|
Synthetically turn some of images upside down. Create a training and test set. |
|
""" |
|
|
|
import os |
|
def makedir(path): |
|
if not os.path.exists(path): |
|
os.makedirs(path) |
|
|
|
makedir('./without_mask') |
|
|
|
from google.colab import drive |
|
drive.mount('/content/drive') |
|
|
|
|
|
|
|
|
|
|
|
import os |
|
import sys |
|
from glob import glob |
|
import torch |
|
import torchvision |
|
|
|
import numpy as np |
|
import datetime as dt |
|
|
|
|
|
import torch.nn as nn |
|
import torch.nn.functional as F |
|
import matplotlib.pyplot as plt |
|
import cv2 |
|
|
|
from PIL import Image |
|
from torch.utils.data import Dataset |
|
from torch.autograd import Variable |
|
from torch.optim import lr_scheduler |
|
|
|
from torch.utils.data import Dataset, DataLoader |
|
from torch.utils.data.sampler import SubsetRandomSampler |
|
from torchvision import transforms, datasets, models |
|
from os import listdir, makedirs, getcwd, remove |
|
from os.path import isfile, join, abspath, exists, isdir, expanduser |
|
from torchvision.transforms.functional import vflip |
|
from PIL import Image |
|
from torch.utils.data import random_split |
|
from PIL import ImageFile |
|
ImageFile.LOAD_TRUNCATED_IMAGES = True |
|
np.random.seed(42) |
|
|
|
|
|
|
|
|
|
|
|
data_path = "./without_mask" |
|
images = glob("./without_mask/*.png", recursive=True) |
|
|
|
import shutil |
|
!rm -rf ./train_data |
|
!rm -rf ./test_data |
|
makedir('./train_data') |
|
makedir('./test_data') |
|
split = int(np.floor(len(images) * .8)) |
|
np.random.seed(22) |
|
N = len(images) |
|
K = split |
|
|
|
dst_dir = 'train_data' |
|
for pngfile in images[:split]: |
|
shutil.copy(pngfile, dst_dir) |
|
dst_dir = 'test_data' |
|
for pngfile in images[split:]: |
|
shutil.copy(pngfile, dst_dir) |
|
|
|
|
|
mean=[0.5894, 0.4595, 0.3966] |
|
std=[0.2404, 0.2020, 0.1959] |
|
|
|
|
|
|
|
train_transforms = transforms.Compose([transforms.Resize(500), |
|
transforms.ToTensor(), |
|
transforms.Normalize(mean,std)]) |
|
|
|
test_transforms = transforms.Compose([ transforms.Resize(500), |
|
transforms.ToTensor(), |
|
transforms.Normalize(mean,std)]) |
|
|
|
class Dataset(Dataset): |
|
def __init__(self, path, transform=None): |
|
self.file_list = glob(path+"*.png", recursive=True) |
|
print(self.file_list) |
|
self.transform = transform |
|
|
|
files = [] |
|
split = int(np.floor(len(self.file_list) * .5)) |
|
|
|
N = len(self.file_list) |
|
K = split |
|
indices = np.array([0] * K + [1] * (N-K)) |
|
np.random.shuffle(indices) |
|
print(indices) |
|
|
|
final_pth = path |
|
for idx,img_pth in enumerate(self.file_list): |
|
if indices[idx]: |
|
img = Image.open(img_pth) |
|
img = vflip(img) |
|
img_name = img_pth.split('/')[-1] |
|
img.save(final_pth+img_name, format="png") |
|
files.append([indices[idx],final_pth+img_name]) |
|
else: |
|
img = Image.open(img_pth) |
|
img_name = img_pth.split('/')[-1] |
|
img.save(final_pth+img_name, format="png") |
|
files.append([indices[idx],final_pth+img_name]) |
|
self.file_list = files |
|
print(self.file_list) |
|
files = None |
|
|
|
|
|
def __len__(self): |
|
return len(self.file_list) |
|
|
|
def __getitem__(self, idx): |
|
fileName = self.file_list[idx][1] |
|
classCategory = self.file_list[idx][0] |
|
im = Image.open(fileName) |
|
if self.transform: |
|
im = self.transform(im) |
|
|
|
return im.view(3, 500, 500), classCategory |
|
|
|
train_data = Dataset('./train_data/', transform=train_transforms) |
|
|
|
test_data = Dataset('./test_data/', transform=test_transforms) |
|
|
|
train_loader = torch.utils.data.DataLoader(train_data, batch_size=32) |
|
|
|
test_loader = torch.utils.data.DataLoader(test_data, batch_size=32) |
|
|
|
|
|
|
|
device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu') |
|
|
|
|
|
|
|
|
|
|
|
class Classifier(nn.Module): |
|
def __init__(self, num_classes): |
|
super(Classifier, self).__init__() |
|
|
|
self.conv1 = nn.Conv2d(in_channels = 3, out_channels = 32, kernel_size = 5, stride = 2, padding = 2) |
|
self.relu1 = nn.ReLU() |
|
self.maxpool1 = nn.MaxPool2d(kernel_size = 2) |
|
|
|
|
|
self.conv2 = nn.Conv2d(in_channels = 32, out_channels = 64, kernel_size = 5, stride = 2, padding = 2) |
|
self.relu2 = nn.ReLU() |
|
self.maxpool2 = nn.MaxPool2d(kernel_size=2) |
|
|
|
|
|
self.conv3 = nn.Conv2d(in_channels = 64, out_channels = 64, kernel_size = 3, stride = 2, padding = 2) |
|
self.relu3 = nn.ReLU() |
|
self.maxpool3 = nn.MaxPool2d(kernel_size=2) |
|
|
|
|
|
self.conv4 = nn.Conv2d(in_channels = 64, out_channels = 64, kernel_size = 3, stride = 2, padding = 2) |
|
self.relu4 = nn.ReLU() |
|
self.maxpool4 = nn.MaxPool2d(kernel_size=2) |
|
|
|
|
|
self.conv5 = nn.Conv2d(in_channels = 64, out_channels = 32, kernel_size = 3, stride = 2, padding = 2) |
|
self.relu5 = nn.ReLU() |
|
self.maxpool5 = nn.MaxPool2d(kernel_size=2) |
|
|
|
|
|
self.fc = nn.Linear(32, num_classes) |
|
self.dropout = nn.Dropout(.1) |
|
|
|
|
|
def forward(self, input): |
|
|
|
x = self.maxpool1(self.relu1(self.conv1(input))) |
|
x = self.dropout(x) |
|
x = self.maxpool2(self.relu2(self.conv2(x))) |
|
x = self.dropout(x) |
|
x = self.maxpool3(self.relu3(self.conv3(x))) |
|
x = self.dropout(x) |
|
x = self.maxpool4(self.relu4(self.conv4(x))) |
|
x = self.dropout(x) |
|
x = self.maxpool5(self.relu5(self.conv5(x))) |
|
x = self.dropout(x) |
|
|
|
x = x.view(x.size(0), -1) |
|
x = self.fc(x) |
|
return x |
|
|
|
def train(model, criterion, data_loader, optimizer, num_epochs): |
|
"""Simple training loop for a PyTorch model.""" |
|
|
|
|
|
model.train() |
|
|
|
|
|
model.to(device) |
|
|
|
|
|
ema_loss = None |
|
|
|
print('----- Training Loop -----') |
|
|
|
for epoch in range(num_epochs): |
|
|
|
|
|
for batch_idx, (features, target) in enumerate(data_loader): |
|
|
|
|
|
output = model(features.to(device)) |
|
output = output.squeeze() |
|
target = target.float() |
|
loss = criterion(output.to(device), target.to(device)) |
|
|
|
|
|
optimizer.zero_grad() |
|
loss.backward() |
|
optimizer.step() |
|
|
|
|
|
if ema_loss is None: |
|
ema_loss = loss.item() |
|
else: |
|
ema_loss += (loss.item() - ema_loss) * 0.01 |
|
|
|
|
|
print('Epoch: {} \tLoss: {:.6f}'.format(epoch, ema_loss)) |
|
|
|
def test(model, data_loader): |
|
"""Measures the accuracy of a model on a data set.""" |
|
|
|
model.eval() |
|
correct = 0 |
|
print('----- Model Evaluation -----') |
|
|
|
with torch.no_grad(): |
|
|
|
|
|
for features, target in data_loader: |
|
|
|
|
|
output = model(features.to(device)) |
|
|
|
|
|
|
|
pred = torch.where(output>0.5,torch.ones(output.shape),torch.zeros(output.shape)) |
|
|
|
correct += pred.cpu().eq(target.view_as(pred)).sum().item() |
|
|
|
|
|
percent = 100. * correct / len(data_loader.dataset) |
|
print(f'Test accuracy: {correct} / {len(data_loader.dataset)} ({percent:.0f}%)') |
|
torch.save(model.state_dict(), 'model.ckpt') |
|
return percent |
|
|
|
num_epochs = 10 |
|
model = Classifier(1) |
|
criterion = torch.nn.BCEWithLogitsLoss() |
|
optimizer = torch.optim.Adam(model.parameters(), lr=1e-2) |
|
|
|
train(model, criterion, train_loader, optimizer, num_epochs=num_epochs) |
|
|
|
test(model, test_loader) |
|
|
|
test(model, train_loader) |
|
|
|
"""**Write up**: |
|
* Link to the model on Hugging Face Hub: |
|
* Include some examples of misclassified images. Please explain what you might do to improve your model's performance on these images in the future (you do not need to impelement these suggestions) |
|
""" |
|
|
|
model1 = Classifier(1) |
|
model1.load_state_dict(torch.load('./model.ckpt')) |
|
model1.eval() |
|
classes = ['no_flip','flip'] |
|
|
|
test(model1, train_loader) |
|
test(model1, test_loader) |
|
|
|
import random |
|
from IPython import display |
|
|
|
|
|
def random_predictions(data): |
|
for i in random.sample(range(len(data)),3): |
|
tensor,target = data[i] |
|
|
|
if device == "cuda": |
|
tensor = tensor.cuda() |
|
|
|
prediction = model1(tensor.unsqueeze(0)).item() |
|
if prediction>.5: |
|
prediction = 1 |
|
else: |
|
prediction = 0 |
|
print( |
|
"Img %d. Excpected class %s, but predicted class %s." |
|
|
|
i, |
|
classes[target], |
|
classes[prediction], |
|
) |
|
) |
|
img = Image.open(data.file_list[i][1]) |
|
img = transforms.Resize(224)(img) |
|
display.display(img) |
|
|
|
random_predictions(test_data) |
|
|
|
|
|
def false_predictions(data): |
|
for i in range(len(data)): |
|
tensor, target = data[i] |
|
|
|
if device == "cuda": |
|
tensor = tensor.cuda() |
|
|
|
prediction = model1(tensor.unsqueeze(0)).item() |
|
if prediction>.5: |
|
prediction = 1 |
|
else: |
|
prediction = 0 |
|
|
|
if prediction != target: |
|
print( |
|
"Img id=%d. Excpected class %s, but predicted class %s." |
|
|
|
i, |
|
classes[target], |
|
classes[prediction], |
|
) |
|
) |
|
img = Image.open(data.file_list[i][1]) |
|
img = transforms.Resize(224)(img) |
|
display.display(img) |
|
|
|
"""* Include some examples of poorly ranked images. Please explain what you could do to improve the performance of your model on these images. |
|
We do not have enough information to comment on this misclassified image in the dataset. |
|
This model was trained on a small dataset of ~300 images and tested on ~60 HD images generated with a GAN. |
|
This dataset has the same distribution and is assumed to be not representive of the wide diversity of images in the real world. |
|
We hope that our model can be useful in the context of creating a profile on a dating site or social network. |
|
Our model could be improved by increasing the size and diversity of the training dataset. |
|
""" |
|
|
|
false_predictions(train_data) |
|
|
|
addition_data = Dataset('./subset_without_mask/', transform=train_transforms) |
|
|
|
false_predictions(addition_data) |
|
|
|
|