Skip to content

Commit 8136ee1

Browse files
DR kaggle
1 parent 9675f0d commit 8136ee1

File tree

6 files changed

+565
-0
lines changed

6 files changed

+565
-0
lines changed
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import torch
2+
import albumentations as A
3+
from albumentations.pytorch import ToTensorV2
4+
5+
DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
6+
LEARNING_RATE = 3e-5
7+
WEIGHT_DECAY = 5e-4
8+
BATCH_SIZE = 20
9+
NUM_EPOCHS = 100
10+
NUM_WORKERS = 6
11+
CHECKPOINT_FILE = "b3.pth.tar"
12+
PIN_MEMORY = True
13+
SAVE_MODEL = True
14+
LOAD_MODEL = True
15+
16+
# Data augmentation for images
17+
train_transforms = A.Compose(
18+
[
19+
A.Resize(width=760, height=760),
20+
A.RandomCrop(height=728, width=728),
21+
A.HorizontalFlip(p=0.5),
22+
A.VerticalFlip(p=0.5),
23+
A.RandomRotate90(p=0.5),
24+
A.Blur(p=0.3),
25+
A.CLAHE(p=0.3),
26+
A.ColorJitter(p=0.3),
27+
A.CoarseDropout(max_holes=12, max_height=20, max_width=20, p=0.3),
28+
A.IAAAffine(shear=30, rotate=0, p=0.2, mode="constant"),
29+
A.Normalize(
30+
mean=[0.3199, 0.2240, 0.1609],
31+
std=[0.3020, 0.2183, 0.1741],
32+
max_pixel_value=255.0,
33+
),
34+
ToTensorV2(),
35+
]
36+
)
37+
38+
val_transforms = A.Compose(
39+
[
40+
A.Resize(height=728, width=728),
41+
A.Normalize(
42+
mean=[0.3199, 0.2240, 0.1609],
43+
std=[0.3020, 0.2183, 0.1741],
44+
max_pixel_value=255.0,
45+
),
46+
ToTensorV2(),
47+
]
48+
)
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import config
2+
import os
3+
import pandas as pd
4+
import numpy as np
5+
from torch.utils.data import Dataset, DataLoader
6+
from PIL import Image
7+
from tqdm import tqdm
8+
9+
10+
class DRDataset(Dataset):
11+
def __init__(self, images_folder, path_to_csv, train=True, transform=None):
12+
super().__init__()
13+
self.data = pd.read_csv(path_to_csv)
14+
self.images_folder = images_folder
15+
self.image_files = os.listdir(images_folder)
16+
self.transform = transform
17+
self.train = train
18+
19+
def __len__(self):
20+
return self.data.shape[0] if self.train else len(self.image_files)
21+
22+
def __getitem__(self, index):
23+
if self.train:
24+
image_file, label = self.data.iloc[index]
25+
else:
26+
# if test simply return -1 for label, I do this in order to
27+
# re-use same dataset class for test set submission later on
28+
image_file, label = self.image_files[index], -1
29+
image_file = image_file.replace(".jpeg", "")
30+
31+
image = np.array(Image.open(os.path.join(self.images_folder, image_file+".jpeg")))
32+
33+
if self.transform:
34+
image = self.transform(image=image)["image"]
35+
36+
return image, label, image_file
37+
38+
39+
if __name__ == "__main__":
40+
"""
41+
Test if everything works ok
42+
"""
43+
dataset = DRDataset(
44+
images_folder="../train/images_resized_650/",
45+
path_to_csv="../train/trainLabels.csv",
46+
transform=config.val_transforms,
47+
)
48+
loader = DataLoader(
49+
dataset=dataset, batch_size=32, num_workers=2, shuffle=True, pin_memory=True
50+
)
51+
52+
for x, label, file in tqdm(loader):
53+
print(x.shape)
54+
print(label.shape)
55+
import sys
56+
sys.exit()
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
"""
2+
Tries to remove unnecessary black borders around the images, and
3+
"trim" the images to they take up the entirety of the image.
4+
It's hacky & not very nice but it works :))
5+
"""
6+
7+
import os
8+
import numpy as np
9+
from PIL import Image
10+
import warnings
11+
from multiprocessing import Pool
12+
from tqdm import tqdm
13+
import cv2
14+
15+
16+
def trim(im):
17+
"""
18+
Converts image to grayscale using cv2, then computes binary matrix
19+
of the pixels that are above a certain threshold, then takes out
20+
the first row where a certain percetage of the pixels are above the
21+
threshold will be the first clip point. Same idea for col, max row, max col.
22+
"""
23+
percentage = 0.02
24+
25+
img = np.array(im)
26+
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
27+
im = img_gray > 0.1 * np.mean(img_gray[img_gray != 0])
28+
row_sums = np.sum(im, axis=1)
29+
col_sums = np.sum(im, axis=0)
30+
rows = np.where(row_sums > img.shape[1] * percentage)[0]
31+
cols = np.where(col_sums > img.shape[0] * percentage)[0]
32+
min_row, min_col = np.min(rows), np.min(cols)
33+
max_row, max_col = np.max(rows), np.max(cols)
34+
im_crop = img[min_row : max_row + 1, min_col : max_col + 1]
35+
return Image.fromarray(im_crop)
36+
37+
38+
def resize_maintain_aspect(image, desired_size):
39+
"""
40+
Stole this from some stackoverflow post but can't remember which,
41+
this will add padding to maintain the aspect ratio.
42+
"""
43+
old_size = image.size # old_size[0] is in (width, height) format
44+
ratio = float(desired_size) / max(old_size)
45+
new_size = tuple([int(x * ratio) for x in old_size])
46+
im = image.resize(new_size, Image.ANTIALIAS)
47+
new_im = Image.new("RGB", (desired_size, desired_size))
48+
new_im.paste(im, ((desired_size - new_size[0]) // 2, (desired_size - new_size[1]) // 2))
49+
return new_im
50+
51+
52+
def save_single(args):
53+
img_file, input_path_folder, output_path_folder, output_size = args
54+
image_original = Image.open(os.path.join(input_path_folder, img_file))
55+
image = trim(image_original)
56+
image = resize_maintain_aspect(image, desired_size=output_size[0])
57+
image.save(os.path.join(output_path_folder + img_file))
58+
59+
60+
def fast_image_resize(input_path_folder, output_path_folder, output_size=None):
61+
"""
62+
Uses multiprocessing to make it fast
63+
"""
64+
if not output_size:
65+
warnings.warn("Need to specify output_size! For example: output_size=100")
66+
exit()
67+
68+
if not os.path.exists(output_path_folder):
69+
os.makedirs(output_path_folder)
70+
71+
jobs = [
72+
(file, input_path_folder, output_path_folder, output_size)
73+
for file in os.listdir(input_path_folder)
74+
]
75+
76+
with Pool() as p:
77+
list(tqdm(p.imap_unordered(save_single, jobs), total=len(jobs)))
78+
79+
80+
if __name__ == "__main__":
81+
fast_image_resize("../train/images/", "../train/images_resized_150/", output_size=(150, 150))
82+
fast_image_resize("../test/images/", "../test/images_resized_150/", output_size=(150, 150))
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
import torch
2+
from torch import nn, optim
3+
import os
4+
import config
5+
from torch.utils.data import DataLoader
6+
from tqdm import tqdm
7+
from sklearn.metrics import cohen_kappa_score
8+
from efficientnet_pytorch import EfficientNet
9+
from dataset import DRDataset
10+
from torchvision.utils import save_image
11+
from utils import (
12+
load_checkpoint,
13+
save_checkpoint,
14+
check_accuracy,
15+
make_prediction,
16+
get_csv_for_blend,
17+
)
18+
19+
20+
def train_one_epoch(loader, model, optimizer, loss_fn, scaler, device):
21+
losses = []
22+
loop = tqdm(loader)
23+
for batch_idx, (data, targets, _) in enumerate(loop):
24+
# save examples and make sure they look ok with the data augmentation,
25+
# tip is to first set mean=[0,0,0], std=[1,1,1] so they look "normal"
26+
#save_image(data, f"hi_{batch_idx}.png")
27+
28+
data = data.to(device=device)
29+
targets = targets.to(device=device)
30+
31+
# forward
32+
with torch.cuda.amp.autocast():
33+
scores = model(data)
34+
loss = loss_fn(scores, targets.unsqueeze(1).float())
35+
36+
losses.append(loss.item())
37+
38+
# backward
39+
optimizer.zero_grad()
40+
scaler.scale(loss).backward()
41+
scaler.step(optimizer)
42+
scaler.update()
43+
loop.set_postfix(loss=loss.item())
44+
45+
print(f"Loss average over epoch: {sum(losses)/len(losses)}")
46+
47+
48+
def main():
49+
train_ds = DRDataset(
50+
images_folder="train/images_preprocessed_1000/",
51+
path_to_csv="train/trainLabels.csv",
52+
transform=config.val_transforms,
53+
)
54+
val_ds = DRDataset(
55+
images_folder="train/images_preprocessed_1000/",
56+
path_to_csv="train/valLabels.csv",
57+
transform=config.val_transforms,
58+
)
59+
test_ds = DRDataset(
60+
images_folder="test/images_preprocessed_1000",
61+
path_to_csv="train/trainLabels.csv",
62+
transform=config.val_transforms,
63+
train=False,
64+
)
65+
test_loader = DataLoader(
66+
test_ds, batch_size=config.BATCH_SIZE, num_workers=6, shuffle=False
67+
)
68+
train_loader = DataLoader(
69+
train_ds,
70+
batch_size=config.BATCH_SIZE,
71+
num_workers=config.NUM_WORKERS,
72+
pin_memory=config.PIN_MEMORY,
73+
shuffle=False,
74+
)
75+
val_loader = DataLoader(
76+
val_ds,
77+
batch_size=config.BATCH_SIZE,
78+
num_workers=2,
79+
pin_memory=config.PIN_MEMORY,
80+
shuffle=False,
81+
)
82+
loss_fn = nn.MSELoss()
83+
84+
model = EfficientNet.from_pretrained("efficientnet-b3")
85+
model._fc = nn.Linear(1536, 1)
86+
model = model.to(config.DEVICE)
87+
optimizer = optim.Adam(model.parameters(), lr=config.LEARNING_RATE, weight_decay=config.WEIGHT_DECAY)
88+
scaler = torch.cuda.amp.GradScaler()
89+
90+
if config.LOAD_MODEL and config.CHECKPOINT_FILE in os.listdir():
91+
load_checkpoint(torch.load(config.CHECKPOINT_FILE), model, optimizer, config.LEARNING_RATE)
92+
93+
# Run after training is done and you've achieved good result
94+
# on validation set, then run train_blend.py file to use information
95+
# about both eyes concatenated
96+
get_csv_for_blend(val_loader, model, "../train/val_blend.csv")
97+
get_csv_for_blend(train_loader, model, "../train/train_blend.csv")
98+
get_csv_for_blend(test_loader, model, "../train/test_blend.csv")
99+
make_prediction(model, test_loader, "submission_.csv")
100+
import sys
101+
sys.exit()
102+
#make_prediction(model, test_loader)
103+
104+
for epoch in range(config.NUM_EPOCHS):
105+
train_one_epoch(train_loader, model, optimizer, loss_fn, scaler, config.DEVICE)
106+
107+
# get on validation
108+
preds, labels = check_accuracy(val_loader, model, config.DEVICE)
109+
print(f"QuadraticWeightedKappa (Validation): {cohen_kappa_score(labels, preds, weights='quadratic')}")
110+
111+
# get on train
112+
#preds, labels = check_accuracy(train_loader, model, config.DEVICE)
113+
#print(f"QuadraticWeightedKappa (Training): {cohen_kappa_score(labels, preds, weights='quadratic')}")
114+
115+
if config.SAVE_MODEL:
116+
checkpoint = {
117+
"state_dict": model.state_dict(),
118+
"optimizer": optimizer.state_dict(),
119+
}
120+
save_checkpoint(checkpoint, filename=f"b3_{epoch}.pth.tar")
121+
122+
123+
124+
if __name__ == "__main__":
125+
main()

0 commit comments

Comments
 (0)