Skip to content

Commit

Permalink
First commit
Browse files Browse the repository at this point in the history
  • Loading branch information
DSharifi committed Aug 29, 2021
1 parent b701b6b commit 0157ea2
Show file tree
Hide file tree
Showing 15 changed files with 173 additions and 165 deletions.
9 changes: 9 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,15 @@

data/*/*

predictions/*
!predictions/.keep

checkpoints/*
!checkpoints/.keep

runs/*
!runs/.keep

data/train_x/*
!data/train_x/.keep
data/train_y/*
Expand Down
Binary file removed ISIC_0000003.jpg
Binary file not shown.
91 changes: 0 additions & 91 deletions Untitled.ipynb

This file was deleted.

File renamed without changes.
57 changes: 53 additions & 4 deletions eval.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import os
import torch
from torch.functional import Tensor
import torch.nn.functional as F
from tqdm import tqdm
import ntpath

import numpy as np

from dice_loss import dice_coeff

Expand All @@ -20,6 +24,10 @@ def eval_net(net, loader, device, **kwargs):
else:
desc = 'Validation round'

loss = 0

booleanTOT = 0

with tqdm(total=n_val, desc=desc, unit='batch', leave=False) as pbar:
for batch in loader:
imgs, true_masks, id = batch['image'], batch['mask'], batch['id'][0]
Expand All @@ -31,17 +39,58 @@ def eval_net(net, loader, device, **kwargs):

pred = torch.sigmoid(mask_pred)
pred = (pred > 0.5).float()
tot += dice_coeff(pred, true_masks).item()
tot += dice_coeff(pred, true_masks)

if 'output_directory' in kwargs: # save output
save_prediction(pred, id, **kwargs)

if 'criterion' in kwargs:
loss += kwargs['criterion'](pred, true_masks).item()

# print(f'')

pbar.update()

net.train()
if 'criterion' in kwargs:
return tot/n_val, loss/n_val

return tot / n_val

def save_prediction(pred, id, **kwargs):
output_path = os.path.join(kwargs['output_directory'], f'{id.strip(".jpg")}.png')
outputDir = os.path.join(kwargs['output_directory'] + '/' + ntpath.basename(kwargs['model_path']))
if not os.path.exists(outputDir):
os.makedirs(outputDir)

pred = pred[:,:,:,:]
output_path = os.path.join(outputDir, f'{id.strip(".jpg")}.png')
save_image(pred, output_path)



# def DSC(prediction: Tensor, ground_truth: Tensor) -> float:

# #assert prediction.shape() == ground_truth.shape()

# # intersection = 1,1 ; 0,0
# # intersection = |set| - |xor|
# intersection = prediction.numel() - torch.sum(torch.logical_xor(prediction, ground_truth))

# numerator = 2 * intersection
# denominator = prediction.numel() + ground_truth.numel()

# return numerator / denominator

# def booleanDSC(prediction, ground_truth) -> float:

# # false_positives = 1,1
# true_positives = sum(torch.logical_and(prediction, ground_truth))

# # false_positives = 1,0
# # false_negative = 0,1
# # XOR = false_positives + false_negatives
# xor = torch.sum(torch.logical_xor(prediction, ground_truth))

# numerator = 2 * true_positives
# denominator = 2 * true_positives + xor


# return numerator / denominator
8 changes: 5 additions & 3 deletions eval_on_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ def parse_args() -> argparse.Namespace:
'--scale',
type=float,
default='.5',
dest='scale',
help='Scaling of images')

parser.add_argument('-u',
Expand All @@ -61,16 +62,17 @@ def parse_args() -> argparse.Namespace:
def run_predictions(args):
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
dataset = MelanomiaDataset(args.image_directory,
args.mask_directory)
loader = DataLoader(dataset, num_workers=8, pin_memory=True)
args.mask_directory,
args.scale)
loader = DataLoader(dataset, num_workers=1, pin_memory=True)

net = Unet(addPadding=args.padding)
net.load_state_dict(
torch.load(args.model_path, map_location=device)
)
net.to(device=device)

eval_net(net, loader, device, desc = 'Testing round', **vars(args))
print(eval_net(net, loader, device, desc = 'Testing round', **vars(args)))


if __name__ == '__main__':
Expand Down
83 changes: 37 additions & 46 deletions main.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,29 +10,19 @@

from tqdm import tqdm
from eval import eval_net

from unet import Unet

from torch.utils.tensorboard import SummaryWriter
from torch.utils.data import DataLoader, random_split

from melanomia_dataset import MelanomiaDataset

from datetime import datetime

from helpers import BasicDataset

from PIL import Image
from image_processor import preprocess_image
from torchvision.utils import save_image



dir_img = 'data/ISBI2016_ISIC_Part1_Training_Data/'
dir_mask = 'data/ISBI2016_ISIC_Part1_Training_GroundTruth/'


dir_checkpoint = 'checkpoints/'


def train_net(net,
Expand All @@ -42,18 +32,24 @@ def train_net(net,
lr=0.001,
val_percent=0.1,
save_cp=True,
img_scale=0.5):
img_scale=0.5,
time='',
padding=0):


dir_checkpoint = f'checkpoints/{time}LR_{lr}_BS_{batch_size}_SCALE_{img_scale}_PADDING{padding}'

dataset = MelanomiaDataset(dir_img, dir_mask, img_scale)
#dataset = BasicDataset(dir_img,dir_mask,img_scale,mask_suffix="_Segmentation")

n_val = int(len(dataset) * val_percent)
n_train = len(dataset) - n_val

train, val = random_split(dataset, [n_train, n_val])
train_loader = DataLoader(train, batch_size=batch_size, shuffle=True, num_workers=2, pin_memory=True)
val_loader = DataLoader(val, batch_size=batch_size, shuffle=False, num_workers=2, pin_memory=True, drop_last=True)
train_loader = DataLoader(train, batch_size=batch_size, shuffle=True, num_workers=1, pin_memory=True)
val_loader = DataLoader(val, batch_size=batch_size, shuffle=False, num_workers=1, pin_memory=True, drop_last=True)

writer = SummaryWriter(comment=f'LR_{lr}_BS_{batch_size}_SCALE_{img_scale}_PADDING{padding}')

writer = SummaryWriter(comment=f'LR_{lr}_BS_{batch_size}_SCALE_{img_scale}')
global_step = 0

logging.info(f'''Starting training:
Expand All @@ -72,62 +68,56 @@ def train_net(net,

criterion = nn.BCEWithLogitsLoss()

# Alternative loss, better iwht more classes
#criterion = nn.CrossEntropyLoss()



for epoch in range(epochs):
net.train()

epoch_loss = 0
with tqdm(total=n_train, desc=f'Epoch {epoch + 1}/{epochs}', unit='img') as pbar:
for batch in train_loader:
imgs = batch['image']
true_masks = batch['mask']
assert imgs.shape[1] == net.n_channels, \
f'Network has been defined with {net.n_channels} input channels, ' \
f'but loaded images have {imgs.shape[1]} channels. Please check that ' \
'the images are loaded correctly.'
image_batch = batch['image'].to(device=device, dtype=torch.float32)
mask_batch = batch['mask'].to(device=device, dtype=torch.float32)

imgs = imgs.to(device=device, dtype=torch.float32)
mask_type = torch.float32
true_masks = true_masks.to(device=device, dtype=mask_type)
predictions = net(image_batch)

masks_pred = net(imgs)

loss = criterion(masks_pred, true_masks)
loss = criterion(predictions, mask_batch)
epoch_loss += loss.item()
writer.add_scalar('Loss/train', loss.item(), global_step)

pbar.set_postfix(**{'loss (batch)': loss.item()})
writer.add_scalar('Loss/train', loss.item(), global_step)
pbar.set_postfix(**{'loss (epoch)': epoch_loss})

# training step
optimizer.zero_grad()
loss.backward()
nn.utils.clip_grad_value_(net.parameters(), 0.1)
optimizer.step()

pbar.update(imgs.shape[0])
pbar.update(image_batch.shape[0])

global_step += 1

if global_step % (n_train // (10 * batch_size)) == 0:
for tag, value in net.named_parameters():
tag = tag.replace('.', '/')
writer.add_histogram('weights/' + tag, value.data.cpu().numpy(), global_step)
writer.add_histogram('grads/' + tag, value.grad.data.cpu().numpy(), global_step)
val_score = eval_net(net, val_loader, device)
scheduler.step(val_score)
writer.add_scalar('learning_rate', optimizer.param_groups[0]['lr'], global_step)


logging.info('Validation Dice Coeff: {}'.format(val_score))
writer.add_scalar('Dice/test', val_score, global_step)
writer.add_images('images', imgs, global_step)
writer.add_images('masks/true', true_masks, global_step)
writer.add_images('masks/pred', torch.sigmoid(masks_pred) > 0.5, global_step)
writer.add_scalar('learning_rate', optimizer.param_groups[0]['lr'], global_step)
writer.add_images('images', image_batch, global_step)
writer.add_images('masks/true', mask_batch, global_step)
writer.add_images('masks/pred', torch.sigmoid(predictions) > 0.5, global_step)

if torch.cuda.is_available():
torch.cuda.empty_cache()


# Write the total loss and eval score.
val_dice_score, val_loss = eval_net(net, val_loader, device, criterion = criterion)

writer.add_scalar('Loss/Train Epoch', epoch_loss/n_train, epoch + 1)
writer.add_scalar('Loss/Validation Epoch', val_loss, epoch + 1)
writer.add_scalar('Dice/Validation', val_dice_score, epoch + 1)

scheduler.step(epoch_loss)
if save_cp:
try:
os.mkdir(dir_checkpoint)
Expand Down Expand Up @@ -155,7 +145,7 @@ def get_args():
help='Downscaling factor of the images')
parser.add_argument('-v', '--validation', dest='val', type=float, default=10.0,
help='Percent of the data that is used as validation (0-100)')
parser.add_argument('-p', '--padding', dest='padding', type=bool, default=True,
parser.add_argument('-p', '--padding', dest='padding', type=int, default=1,
help='Add padding in the convolutions')

return parser.parse_args()
Expand Down Expand Up @@ -196,7 +186,8 @@ def get_args():
lr=args.lr,
device=device,
img_scale=args.scale,
val_percent=args.val / 100)
val_percent=args.val / 100,
time = current_time)
except KeyboardInterrupt:
torch.save(net.state_dict(), 'INTERRUPTED.pth')
logging.info('Saved interrupt')
Expand Down
3 changes: 3 additions & 0 deletions melanomia_dataset.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@ def scale_file_(self, image: Image, mask: Image) -> Tuple[Tensor, Tensor]:
width, height = image.size
width, height = int(width*self.scale), int(height*self.scale)

# width = 224
# height = 224

image = image.resize((width, height))
mask = mask.resize((width, height))
image, mask = np.array(image), np.array(mask)
Expand Down
33 changes: 33 additions & 0 deletions plots/data_points.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
LR: 0.00015 Scale: 0.42
0.5328661203384399
0.6925103664398193
0.6969958543777466
2.6342222625430622e-08
0.6203827261924744
0.7055454850196838
0.6871829032897949
0.7076613903045654
0.7101237177848816
0.6999033689498901
LR: 0.0003 Scale: 0.42
0.5149078965187073
3.7212257719687614e-09
0.5762770771980286
0.647400975227356
0.668533444404602
0.48211535811424255
0.6727656722068787
0.669792890548706
0.6599106192588806
0.3272610306739807
LR: 0.0001 Scale: 0.42
0.543988823890686
0.6076177954673767
0.4291623532772064
0.47379541397094727
0.5646342635154724
0.597223162651062
0.5686771869659424
0.6129987239837646
0.5954622030258179
0.6290056705474854
32 changes: 32 additions & 0 deletions plots/plotter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
from typing import Dict, List
from matplotlib import pyplot as plt


def get_data_points(text_file: str) -> List[Dict]:
data_points = []

with open(text_file) as data_file:
for _ in range(3):
dice_scores = []
name = data_file.readline().strip('\n')

for i in range(10):
dice_scores.append(float(data_file.readline()))

data_points.append({'Name' : name, "Dice Scores" : dice_scores})


return data_points


if __name__ == '__main__':
results = get_data_points('data_points.txt')

for result in results:
plt.plot([epoch for epoch in range(1, 11)], result['Dice Scores'], label = result['Name'])

plt.title('Dice Score - Validation Data')
plt.xlabel('Epoch')
plt.ylabel('Dice Score')
plt.legend()
plt.show()
Empty file added predictions/.keep
Empty file.
5 changes: 0 additions & 5 deletions requirements.txt

This file was deleted.

Empty file added runOvernight.py
Empty file.
Empty file added runs/.keep
Empty file.
Loading

0 comments on commit 0157ea2

Please sign in to comment.