Image classification using pretrained models with finetune and others improvements
In this notebook uses the pretrained resnet 18 network, which is finetune with our photos
Some functions taken from this notebook
What you can try to improve score:
- Train model a bit longer
- Tune hyperparameters
- Take another optimizer
- Use learning rate scheduler https://pytorch.org/docs/stable/optim.html
- Choose another pretrained model, may be even smaller model because I saw overfiting https://pytorch.org/vision/stable/models.html
- Freeze some layers and train only last one
- Try upsampling (photos with age 90-100 more than the rest)
- Use augmentations
Login to AIcrowd and download files¶
In [1]:
!pip install aicrowd-cli
%load_ext aicrowd.magic
In [2]:
%aicrowd login
In [3]:
!mkdir data
%aicrowd ds dl -c age-prediction -o data
In [4]:
!unzip data/train.zip -d data/train > /dev/null
!unzip data/val.zip -d data/train > /dev/null
!unzip data/test.zip -d data/test > /dev/null
In [5]:
!rm ./data/test.zip
!rm ./data/train.zip
!rm ./data/val.zip
Import modules and define utils¶
In [6]:
!pip install albumentations==0.4.6
!pip install opencv-python
In [22]:
import torch
import numpy as np
import pandas as pd
import PIL
import pickle
import cv2
from skimage import io
from tqdm import tqdm, tqdm_notebook
from PIL import Image
from pathlib import Path
from sklearn.metrics import f1_score
from torchvision import transforms
from multiprocessing.pool import ThreadPool
from sklearn.preprocessing import LabelEncoder
from torch.utils.data import Dataset, DataLoader
import torch.nn as nn
import torchvision.models as models
import random
import os
from collections import Counter
import albumentations
from albumentations.pytorch import ToTensorV2
from albumentations import ImageOnlyTransform
from albumentations import Compose, Normalize, Resize
from matplotlib import colors, pyplot as plt
%matplotlib inline
import warnings
warnings.filterwarnings(action='ignore', category=DeprecationWarning)
In [8]:
data_dir = './data'
OUTPUT_DIR = './'
if not os.path.exists(OUTPUT_DIR):
os.makedirs(OUTPUT_DIR)
TRAIN_PATH = './data/train'
TEST_PATH = './data/test'
DEVICE = torch.device("cuda")
SEED = 42
size = 384
n_classes = 10
batch_size = 4
n_epochs = 5
lr = 3e-4
n_folds = 3
In [27]:
train_df = pd.read_csv(os.path.join(data_dir,"train.csv"))
val_df = pd.read_csv(os.path.join(data_dir,"val.csv"))
test_df = pd.read_csv(os.path.join(data_dir,"test.csv"))
sample = pd.read_csv(os.path.join(data_dir,"sample_submission.csv"))
train_df.shape, test_df.shape, val_df.shape
Out[27]:
In [10]:
train_df
Out[10]:
In [28]:
label_2_id = {'0-10': 0,
'10-20':1,
'20-30':2,
'30-40':3,
'40-50':4,
'50-60':5,
'60-70':6,
'70-80':7,
'80-90':8,
'90-100':9
}
id_2_label = {0: '0-10',
1: '10-20',
2: '20-30',
3: '30-40',
4: '40-50',
5: '50-60',
6: '60-70',
7: '70-80',
8: '80-90',
9: '90-100'}
train_df['age'] = train_df['age'].map(label_2_id)
val_df['age'] = val_df['age'].map(label_2_id)
test_df['age'] = test_df['age'].map(label_2_id)
In [29]:
train_df['ImageID'] = train_df['ImageID'].apply(lambda x: x+".jpg")
val_df['ImageID'] = val_df['ImageID'].apply(lambda x: x+".jpg")
test_df['ImageID'] = test_df['ImageID'].apply(lambda x: x+".jpg")
In [13]:
def seed_torch(seed=42):
random.seed(seed)
os.environ['PYTHONHASHSEED'] = str(seed)
np.random.seed(seed)
torch.manual_seed(seed)
torch.cuda.manual_seed(seed)
torch.backends.cudnn.deterministic = True
seed_torch(seed=SEED)
In [14]:
class TrainDataset(Dataset):
def __init__(self, df, transform=None):
self.df = df
self.file_names = df['ImageID'].values
self.labels = df['age'].values
self.transform = transform
def __len__(self):
return len(self.df)
def __getitem__(self, idx):
file_name = self.file_names[idx]
file_path = f'{TRAIN_PATH}/{file_name}'
image = cv2.imread(file_path)
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
if self.transform:
augmented = self.transform(image=image)
image = augmented['image']
label = torch.tensor(self.labels[idx]).long()
return image, label
class TestDataset(Dataset):
def __init__(self, df, transform=None):
self.df = df
self.file_names = df['ImageID'].values
self.transform = transform
def __len__(self):
return len(self.df)
def __getitem__(self, idx):
file_name = self.file_names[idx]
file_path = f'{TEST_PATH}/{file_name}'
image = cv2.imread(file_path)
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
if self.transform:
augmented = self.transform(image=image)
image = augmented['image']
return image
In [15]:
def get_transforms(*, data):
if data == 'train':
return Compose([
Resize(size, size),
Normalize(
mean=[0.485, 0.456, 0.406],
std=[0.229, 0.224, 0.225],
),
ToTensorV2(),
])
elif data == 'valid':
return Compose([
Resize(size, size),
Normalize(
mean=[0.485, 0.456, 0.406],
std=[0.229, 0.224, 0.225],
),
ToTensorV2(),
])
Train model¶
In [16]:
def fit_epoch(model, train_loader, criterion, optimizer):
running_loss = 0.0
running_corrects = 0
processed_data = 0
for inputs, labels in train_loader:
inputs = inputs.to(DEVICE)
labels = labels.to(DEVICE)
optimizer.zero_grad()
outputs = model(inputs)
loss = criterion(outputs, labels)
loss.backward()
optimizer.step()
preds = torch.argmax(outputs, 1)
running_loss += loss.item() * inputs.size(0)
running_corrects += torch.sum(preds == labels.data)
processed_data += inputs.size(0)
train_loss = running_loss / processed_data
train_acc = running_corrects.cpu().numpy() / processed_data
return train_loss, train_acc
In [17]:
def eval_epoch(model, val_loader, criterion):
model.eval()
running_loss = 0.0
running_corrects = 0
processed_size = 0
for inputs, labels in val_loader:
inputs = inputs.to(DEVICE)
labels = labels.to(DEVICE)
with torch.set_grad_enabled(False):
outputs = model(inputs)
loss = criterion(outputs, labels)
preds = torch.argmax(outputs, 1)
running_loss += loss.item() * inputs.size(0)
running_corrects += torch.sum(preds == labels.data)
processed_size += inputs.size(0)
val_loss = running_loss / processed_size
val_acc = running_corrects.double() / processed_size
return val_loss, val_acc
In [18]:
def train(train_files, val_files, model, epochs, batch_size, learning_rate=3e-4, loss_weights=None):
train_loader = DataLoader(train_files, batch_size=batch_size, shuffle=True)
val_loader = DataLoader(val_files, batch_size=batch_size, shuffle=False)
history = []
log_template = "\nEpoch {ep:03d} train_loss: {t_loss:0.4f} \
val_loss {v_loss:0.4f} train_acc {t_acc:0.4f} val_acc {v_acc:0.4f}"
with tqdm(desc="epoch", total=epochs) as pbar_outer:
opt = torch.optim.Adam(model.parameters(), lr=learning_rate)
criterion = nn.CrossEntropyLoss()
for epoch in range(epochs):
train_loss, train_acc = fit_epoch(model, train_loader, criterion, opt)
print("loss", train_loss)
val_loss, val_acc = eval_epoch(model, val_loader, criterion)
history.append((train_loss, train_acc, val_loss, val_acc))
pbar_outer.update(1)
tqdm.write(log_template.format(ep=epoch+1, t_loss=train_loss,\
v_loss=val_loss, t_acc=train_acc, v_acc=val_acc))
return history
In [19]:
def predict(model, test_loader):
with torch.no_grad():
logits = []
for inputs in test_loader:
inputs = inputs.to(DEVICE)
model.eval()
outputs = model(inputs).cpu()
logits.append(outputs)
probs = nn.functional.softmax(torch.cat(logits), dim=-1).numpy()
return probs
In [20]:
model = models.resnet18(pretrained=True)
# for param in model.parameters():
# param.requires_grad = False
model.fc = nn.Linear(512, n_classes)
model.to(DEVICE)
Out[20]:
In [30]:
train_dataset = TrainDataset(train_df, transform=get_transforms(data='train'))
valid_dataset = TrainDataset(val_df, transform=get_transforms(data='valid'))
history = train(train_dataset, valid_dataset, model=model, epochs=n_epochs, batch_size=batch_size, learning_rate=lr)
In [31]:
loss, acc, val_loss, val_acc = zip(*history)
plt.figure(figsize=(15, 9))
plt.plot(loss, label="train_loss")
plt.plot(val_loss, label="val_loss")
plt.legend(loc='best')
plt.xlabel("epochs")
plt.ylabel("loss")
plt.show()
Predict and submit results¶
In [32]:
test_dataset = TestDataset(test_df, transform=get_transforms(data='valid'))
test_loader = DataLoader(test_dataset, shuffle=False, batch_size=4)
predictions = predict(model, test_loader)
In [33]:
!rm -rf assets
!mkdir assets
In [34]:
test_df['age'] = predictions.argmax(1)
test_df['age'] = test_df['age'].map(id_2_label)
test_df['ImageID'] = test_df['ImageID'].apply(lambda x: x.split(".")[0] )
test_df[['ImageID', 'age']].to_csv(os.path.join("assets", "submission.csv"), index=False)
In [35]:
test_df
Out[35]:
In [36]:
%aicrowd notebook submit -c age-prediction -a assets --no-verify
Content
Comments
You must login before you can post a comment.