Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
79 commits
Select commit Hold shift + click to select a range
9fca4c3
Set up basic project skeleton
KnowerSmyf Sep 22, 2023
fe25335
created .gitignore to store data and model files
KnowerSmyf Sep 22, 2023
82802d5
Setup the UNet modules according to the paper
KnowerSmyf Oct 20, 2023
9becad1
Attempted to build the UNet according to paper...
KnowerSmyf Oct 20, 2023
597515b
Implemented Encoder, changed UNet accordingly
KnowerSmyf Oct 22, 2023
da0ebe4
Changed UNet Encoder input args
KnowerSmyf Oct 22, 2023
241cbb7
Implemented upsampling module
KnowerSmyf Oct 22, 2023
c70f54a
Implemented Localisation module
KnowerSmyf Oct 22, 2023
5edc8b1
Fixed afew bugs
KnowerSmyf Oct 22, 2023
6f6ff64
Worked on UNet + created Segmentation class
KnowerSmyf Oct 22, 2023
82d0e90
Finished Unet + Segmentation
KnowerSmyf Oct 22, 2023
3cb04a7
Cleaned up afew things
KnowerSmyf Oct 22, 2023
d9f60b0
Cleaned up the modules
KnowerSmyf Oct 22, 2023
171396a
Made UNet 2D, setup Dataloader, added utils
KnowerSmyf Oct 23, 2023
3b364fe
Did some debugging on modules
KnowerSmyf Oct 24, 2023
a0ff43a
Started implementing the training loop etc
KnowerSmyf Oct 24, 2023
1b3e31d
Realised this was the wrong spot...
KnowerSmyf Oct 24, 2023
e465710
Switched from softmax to sigmoid UNet output
KnowerSmyf Oct 24, 2023
66f6652
Implemented loader methods
KnowerSmyf Oct 24, 2023
ea20f0c
Fixed afew things
KnowerSmyf Oct 24, 2023
b431fce
Improved training loop
KnowerSmyf Oct 24, 2023
78e3bde
Implemented a dice score method
KnowerSmyf Oct 24, 2023
0e99346
set up my model testing
KnowerSmyf Oct 24, 2023
c791103
started my predict.py file
KnowerSmyf Oct 24, 2023
d444b7a
committing a slurm script
KnowerSmyf Oct 24, 2023
0d0f302
fixed bug in dataset
KnowerSmyf Oct 24, 2023
efd6aab
modified ISICDataset __init__ method
KnowerSmyf Oct 24, 2023
6f8b2f9
added a check for inconsistencies in the DB
KnowerSmyf Oct 24, 2023
6308bde
Put some inconsistency fixes and check in
KnowerSmyf Oct 24, 2023
662f1b3
fixed check_consistency bug
KnowerSmyf Oct 24, 2023
8e3db88
fixed consistency logic further
KnowerSmyf Oct 24, 2023
82b143b
put some debugging print statements
KnowerSmyf Oct 24, 2023
c9453bf
Hopefully fixed the incompatibility for dice_loss
KnowerSmyf Oct 24, 2023
01b2f19
the previous 'fix' failed, trying something else
KnowerSmyf Oct 24, 2023
c761df7
Trying out expanding masks channels
KnowerSmyf Oct 24, 2023
f1f20dc
added print statements for debugging
KnowerSmyf Oct 25, 2023
2fa3f4c
testing the save method
KnowerSmyf Oct 25, 2023
940a21e
Reduced the model a lil for debugging efficiency
KnowerSmyf Oct 25, 2023
0ca5655
Training and testing passed debugging!
KnowerSmyf Oct 25, 2023
5c3701d
Added training feedback to loop
KnowerSmyf Oct 25, 2023
204afb6
removed redundent methods
KnowerSmyf Oct 25, 2023
5470f3b
Implemented validation set stuff
KnowerSmyf Oct 25, 2023
1eae806
Included mask channel expansion to validation
KnowerSmyf Oct 25, 2023
0450191
Changed num_classes to 1 and implemented thresholding on output. dice…
KnowerSmyf Oct 26, 2023
075b9ba
fixed bug in random_split
KnowerSmyf Oct 26, 2023
1d6848c
Downsized the debugging dataset, trying to lower gpu memory
KnowerSmyf Oct 26, 2023
48bc775
Using batched operations in dice_loss and dice_coefficient methods to…
KnowerSmyf Oct 26, 2023
bec7d66
Removed non-differentiable binary conversion from training loop, also…
KnowerSmyf Oct 26, 2023
64ca95d
including testing feedback for debugging
KnowerSmyf Oct 26, 2023
97dad35
Gonna try 3 epochs in debugging instead of 1
KnowerSmyf Oct 26, 2023
091e4d9
using 15 epochs for debugging
KnowerSmyf Oct 26, 2023
90627dd
Debugging with epochs = 3, samples = 200, batches = 50
KnowerSmyf Oct 26, 2023
365f642
Trying a ChatGPT generated DiceLoss module as criterion for debugging…
KnowerSmyf Oct 26, 2023
7cfc99a
removed some dice loss debugging methods
KnowerSmyf Oct 26, 2023
6561a0d
experimenting with different batch sizes
KnowerSmyf Oct 26, 2023
035a0d5
trying training batch size of 50, been problematic previously
KnowerSmyf Oct 26, 2023
f40097f
trying batch size of 40
KnowerSmyf Oct 26, 2023
ca6b114
Changed the threshold from 127.5 to 0.5 for binarizing the mask
KnowerSmyf Oct 26, 2023
c0f75e7
The model is finally working, going to try a real training cycle
KnowerSmyf Oct 26, 2023
7a171a8
forgot to declare 'no_improvement', fixed
KnowerSmyf Oct 26, 2023
296a12b
fixed the error in the debugging random_split
KnowerSmyf Oct 26, 2023
8cc9427
Using test partition in slurm script
KnowerSmyf Oct 26, 2023
498f92c
Testing out validation and test batch size = 100
KnowerSmyf Oct 26, 2023
e82203c
The model works, time to train properly!
KnowerSmyf Oct 26, 2023
051f27d
Switched back to vgpu partition
KnowerSmyf Oct 26, 2023
a0d80de
Gave the problem directory a meaningful name
KnowerSmyf Oct 26, 2023
1dda16a
Made the stoppage criteria less strict in training
KnowerSmyf Oct 26, 2023
d951222
deleted the old directory
KnowerSmyf Oct 26, 2023
856d8b8
Worked on my README file
KnowerSmyf Oct 26, 2023
e2f9ba6
Training again, lost a lot of data ):
KnowerSmyf Oct 26, 2023
72b48cb
created individual image and mask pre-processing
KnowerSmyf Oct 26, 2023
dc5ac4d
Added images
KnowerSmyf Oct 26, 2023
4b321fa
Added best_model.pth
KnowerSmyf Oct 26, 2023
1ae7398
Wrote the README file, finished the predict.py file
KnowerSmyf Oct 26, 2023
056988d
Removed debugging stuff
KnowerSmyf Oct 26, 2023
7f30cbf
Improved the README file
KnowerSmyf Oct 26, 2023
76d6260
If the best_model.pth DNE execute train.py
KnowerSmyf Oct 26, 2023
fa0eea2
performance images
KnowerSmyf Oct 26, 2023
dd27659
Fixed afew README image naming bugs
KnowerSmyf Oct 26, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 73 additions & 10 deletions recognition/README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,73 @@
# Recognition Tasks
Various recognition tasks solved in deep learning frameworks.

Tasks may include:
* Image Segmentation
* Object detection
* Graph node classification
* Image super resolution
* Disease classification
* Generative modelling with StyleGAN and Stable Diffusion
# ISIC Lesion Segmentation Algorithm

## Description
The ISIC Lesion Segmentation Algorithm was designed to automatically segment skin lesion boundaries from dermatoscopic images. Early detection of malignant skin lesions is crucial in improving the prognosis of skin cancers such as melanoma. The algorithm operates by analyzing input images and leverages a convolutional neural network (CNN) to identify and segment potential skin lesions, distinguishing them from healthy skin. The Dice similarity coefficient is used to compare the algorithm output to the ground truth reference mask, which essentially measures the proportion of output pixels that match the true image.

The model is a modified UNet and is composed of several CNN layers, skip connections, and uses deep supervision facilitated by segmentation layers that connect different levels of the network to the final output. The architecture of the model was inspired by the [improved UNet](https://arxiv.org/abs/1802.10508v1) (Figure 1), which proved to be an effective 3D brain tumor segmentation model during the BRATS 2017 challenge. The network is trained using the 2018 ISIC (International Skin Imaging Collaboration) dataset, which contains annotated images of various skin lesions.

![Image of the improved UNet architecture](./UNet_Segmentation_s4745275/images/Figure_1.png)
Figure 1: Improved UNet architecture. Designed by F. Isensee et al.

## Dependencies

To run the ISIC Lesion Segmentation Algorithm, you'll need the following libraries:

- Python (only verified for 3.7+)
- numpy: For numerical computations and some tensor operations
- PyTorch: For building and training the neural network
- matplotlib: For plotting and visualisation
- PIL (Pillow): For loading the dataset and visualisation

To install any dependencies you can use `pip install`

## Reproducability

To run the algorithm and reproduce the results I've obtained, please be aware of the following considerations:

1. Directory Paths for ISICDataset: The paths specified when initializing ISICDataset may need to be modified to match the directory structure on your machine. Ensure that you point it to the correct location where your dataset resides.

2. Model State Dictionary Directory: The directory where the model state dictionary is saved/loaded may differ based on your setup. Adjust the path accordingly to ensure the algorithm can access the model or save it correctly.

Always ensure that you have the necessary permissions to read/write in the specified directories and that the paths are correctly formatted.

## Usage
#### See predict.py for a full usage demonstration of the model.
### Input
torch.Tensor with shape [batch_size, 6, 256, 256]
- The batch_size denotes the number of inputted images, this is the only argument that varies
- 6 channels (3 for RGB and 3 for HSV)
- The image has dimensions 256x256

### Output

torch.Tensor was shape [batch_size, 1, 256, 256]
- The batch_size denotes the number of inputted images, this is the only argument that varies
- 1 channel containing probabilities of being
- The image has dimensions 256x256

## Results
Ultimately, after extensive training over 50 epochs, the model attained an average Dice similarity coefficient of 0.7364 on the test set. This performance indicates potential areas for improvement. Given more time, I would delve into techniques like hyperparameter tuning and possibly experiment with alternative optimizers.

![Beautiful demonstation of the model efficacy](./UNet_Segmentation_s4745275/images/Figure_2.png)
Figure 2: An example output from a random sample. Black indicates non-lesion, white indicates lesion. (25 epochs)

That said, the model does exhibit proficiency in segmenting the image. This is evident in Figure 2, where the output mask closely mirrors the true mask, especially around the edges.

## Pre-processing
Various transformation pipelines were implemented for both pre-processing and data augmentation. You can find these in the dataset.py file. They serve to convert the provided images or masks into tensors compatible with the model (refer to the Input and Output section), as well as to normalize the inputs. During training, the process_and_augment pipeline was employed, performing random scalings, flips, rotations, and more to enhance the model's generalizability during learning.


# Data Splits

The data was partitioned as follows:

- Training: 70%
- Validation: 20%
- Testing: 10%

With this configuration, a significant majority (70%) of the data is allocated for training. Deep learning models, like the UNet I implemented, require a robust volume of data for effective training. By dedicating a larger segment of the dataset to training, the model can encounter a more diverse array of samples, which is essential for discerning and internalizing underlying patterns. Given the dataset's substantial size (over 2500 samples), allocating 70% to training felt appropriate.

The validation set serves a dual purpose: it allows for ongoing evaluation during training and aids in determining when to cease training — a tactic known as early stopping — to mitigate overfitting. A generous validation set is imperative to ensure that the decision to halt training is anchored in a trustworthy performance metric rather than the inconsistencies of a smaller subset.

Finally, the test set offers an objective assessment of the model's performance post-training. While 10% might seem modest, given the dataset's magnitude, it still yields a significant number of samples. Consequently, the test set furnishes a dependable measure of how the model is likely to perform in real-world scenarios.
The data was divided as follows:
Empty file.
158 changes: 158 additions & 0 deletions recognition/UNet_Segmentation_s4745275/dataset.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
"""
File containing the data loaders used for loading and preprocessing the data.
"""

import os
import torch
from utils import RandomCenterCrop, RandomRotate90, DictTransform
from torch.utils.data import Dataset
from torchvision import transforms
import torchvision.transforms.functional as TF
from PIL import Image
import numpy as np

# These are the default paths for me, they may not apply to you. Modify as required
image_path = "/home/groups/comp3710/ISIC2018/ISIC2018_Task1-2_Training_Input_x2"
mask_path = "/home/groups/comp3710/ISIC2018/ISIC2018_Task1_Training_GroundTruth_x2"
inconsistent_path = "/home/Student/s4745275/PatternAnalysis-2023/recognition/UNet_Segmentation_s4745275/inconsistent_ids.txt"


def check_consistency(
image_dir=image_path, mask_dir=mask_path, inconsistent_path=inconsistent_path
):
image_ids = {
img.split(".")[0] for img in os.listdir(image_dir) if img.endswith(".jpg")
}
mask_ids = {
mask.split("_segmentation.")[0]
for mask in os.listdir(mask_dir)
if mask.endswith("_segmentation.png")
}

# Using list differences to find inconsistencies
images_without_masks = image_ids - mask_ids
masks_without_images = mask_ids - image_ids

if images_without_masks or masks_without_images:
inconsistent_ids = images_without_masks.union(masks_without_images)
# Save to a file
with open(inconsistent_path, "w") as file:
for ID in inconsistent_ids:
file.write(f"{ID}\n")

print(f"Detected {len(inconsistent_ids)} inconsistencies, fixed em tho")


class ISICDataset(Dataset):
def __init__(
self,
transform,
image_dir=image_path,
mask_dir=mask_path,
inconsistent_path=inconsistent_path,
):
# Load the inconsistent IDs
with open(inconsistent_path, "r") as file:
excluded_ids = set(line.strip() for line in file)

self.image_dir = image_dir
self.mask_dir = mask_dir
self.image_ids = [
img.split(".")[0]
for img in os.listdir(image_dir)
if img.endswith(".jpg") and img.split(".")[0] not in excluded_ids
]
self.transform = transform

def __len__(self):
return len(self.image_ids)

def handle_inconsistency(self):
images_without_masks, masks_without_images = check_consistency(
self.image_dir, self.mask_dir
)
inconsistent_ids = images_without_masks.union(masks_without_images)

# Save to a file
with open(inconsistent_path, "a") as file: # 'a' mode for appending
for ID in inconsistent_ids:
file.write(f"{ID}\n")

def __getitem__(self, idx):
img_name = os.path.join(self.image_dir, self.image_ids[idx] + ".jpg")
mask_name = os.path.join(
self.mask_dir, self.image_ids[idx] + "_segmentation.png"
)

try:
with Image.open(img_name) as image, Image.open(mask_name) as mask:
image = image.convert("RGB")
mask = mask.convert("L")
sample = {"image": image, "mask": mask}

if self.transform:
sample = self.transform(sample)

# Convert mask to binary 0/1 tensor
sample["mask"] = (torch.tensor(np.array(sample["mask"])) > 0.5).float()

return sample["image"], sample["mask"]

except FileNotFoundError:
self.handle_inconsistency()
return self.__getitem__(idx)


pre_process_image = transforms.Compose(
[
transforms.Resize((256, 256)),
transforms.ToTensor(),
transforms.Lambda(
lambda img_tensor: torch.cat(
[
img_tensor,
TF.to_tensor(TF.to_pil_image(img_tensor).convert("HSV")),
],
dim=0,
)
),
transforms.Normalize(
[0.5, 0.5, 0.5, 0.5, 0.5, 0.5], [0.5, 0.5, 0.5, 0.5, 0.5, 0.5]
),
]
)

pre_process_mask = transforms.Compose(
[transforms.Resize((256, 256)), transforms.ToTensor()]
)


# Transformation pipeline to pre-process and augment the dataset
process_and_augment = transforms.Compose(
[
RandomRotate90(),
RandomCenterCrop(),
DictTransform(transforms.RandomHorizontalFlip()),
DictTransform(transforms.RandomVerticalFlip()),
DictTransform(transforms.Resize((256, 256))),
DictTransform(transforms.ToTensor()),
DictTransform(
transforms.Lambda(
lambda img_tensor: torch.cat(
[
img_tensor,
TF.to_tensor(TF.to_pil_image(img_tensor).convert("HSV")),
],
dim=0,
)
),
False,
),
DictTransform(
transforms.Normalize(
[0.5, 0.5, 0.5, 0.5, 0.5, 0.5], [0.5, 0.5, 0.5, 0.5, 0.5, 0.5]
),
False,
),
]
)
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading