From a1b8605aaa02beadac84b58e5083b8a2f5b04dd6 Mon Sep 17 00:00:00 2001 From: Alexander Date: Sun, 27 Oct 2024 15:05:07 +1000 Subject: [PATCH 01/11] added required 4 modules and function to read in and process nfti files --- recognition/dataset.py | 49 ++++++++++++++++++++++++++++++++++++++++++ recognition/modules.py | 1 + recognition/predict.py | 1 + recognition/train.py | 1 + 4 files changed, 52 insertions(+) create mode 100644 recognition/dataset.py create mode 100644 recognition/modules.py create mode 100644 recognition/predict.py create mode 100644 recognition/train.py diff --git a/recognition/dataset.py b/recognition/dataset.py new file mode 100644 index 000000000..652308d24 --- /dev/null +++ b/recognition/dataset.py @@ -0,0 +1,49 @@ +# This is for the dataset + +import numpy as np +import nibabel as nib +from tqdm import tqdm +def to_channels(arr: np.ndarray, dtype=np.uint8) -> np.ndarray: + channels = np.unique(arr) + res = np.zeros(arr.shape + (len(channels),), dtype=dtype) + for c in channels: + c = int(c) + res[..., c:c+1][arr == c] = 1 + + return res + +def load_data_2D(imageNames, normImage=False, categorical=False, dtype=np.float32, getAffines=False, early_stop=False): + affines = [] + num = len(imageNames) + first_case = nib.load(imageNames[0]).get_fdata(caching='unchanged') + if (len(first_case.shape) == 3): + first_case = first_case[:,:,0] + if categorical: + first_case = to_channels(first_case, dtype=dtype) + rows, cols, channels = first_case.shape + images = np.zeros((num, rows, cols, channels), dtype=dtype) + else: + rows, cols = first_case.shape + images = np.zeros((num, rows, cols), dtype=dtype) + + for i, inName in enumerate(tqdm(imageNames)): + niftiImage = nib.load(inName) + inImage = niftiImage.get_fdata(caching='unchanged') + affine = niftiImage.affine + if (len(inImage.shape) == 3): + inImage = inImage[:, :, 0] + inImage = inImage.astype(dtype) + if normImage: + inImage = ((inImage - inImage.mean()) / inImage.std()) + if categorical: + inImage = to_channels(inImage, dtype=dtype) + images[i, :, :, :] = inImage + else: + images[i, :, :] = inImage + affines.append(affine) + if i > 20 and early_stop: + break + if getAffines: + return images, affines + else: + return images \ No newline at end of file diff --git a/recognition/modules.py b/recognition/modules.py new file mode 100644 index 000000000..90bd14fd9 --- /dev/null +++ b/recognition/modules.py @@ -0,0 +1 @@ +# This is for the modules diff --git a/recognition/predict.py b/recognition/predict.py new file mode 100644 index 000000000..b71fb1906 --- /dev/null +++ b/recognition/predict.py @@ -0,0 +1 @@ +# Trained Model Usage diff --git a/recognition/train.py b/recognition/train.py new file mode 100644 index 000000000..25002c66a --- /dev/null +++ b/recognition/train.py @@ -0,0 +1 @@ +# This is for the training algorithm From f08bc903e38a004850781865b1fb19f0f684e808 Mon Sep 17 00:00:00 2001 From: Alexander Date: Sun, 27 Oct 2024 20:23:10 +1000 Subject: [PATCH 02/11] added basic functional UNET module based on the one shakes showed in class and prac 2 --- recognition/dataset.py | 1 + recognition/modules.py | 49 +++++++++++++++++++++++++++++++++++++++++- 2 files changed, 49 insertions(+), 1 deletion(-) diff --git a/recognition/dataset.py b/recognition/dataset.py index 652308d24..927e27a3a 100644 --- a/recognition/dataset.py +++ b/recognition/dataset.py @@ -3,6 +3,7 @@ import numpy as np import nibabel as nib from tqdm import tqdm + def to_channels(arr: np.ndarray, dtype=np.uint8) -> np.ndarray: channels = np.unique(arr) res = np.zeros(arr.shape + (len(channels),), dtype=dtype) diff --git a/recognition/modules.py b/recognition/modules.py index 90bd14fd9..a50dc0537 100644 --- a/recognition/modules.py +++ b/recognition/modules.py @@ -1 +1,48 @@ -# This is for the modules +import numpy as np +import tensorflow as tf +from tensorflow.keras import layers, models + +def unet(tensor_in, depth=64): + + # For each layer of the UNET filter convolute twice and then pool. + encode1 = layers.Conv2D(depth, 3, activation='relu', padding='same')(tensor_in) + encode1 = layers.Conv2D(depth, 3, activation='relu', padding='same')(encode1) + pool1 = layers.MaxPooling2D(pool_size=(2,2), padding='same')(encode1) + + encode2 = layers.Conv2D(depth*2, 3, activation='relu', padding='same')(pool1) + encode2 = layers.Conv2D(depth*2, 3, activation='relu', padding='same')(encode2) + pool2 = layers.MaxPooling2D(pool_size=(2,2), padding='same')(encode2) + + encode3 = layers.Conv2D(depth*4, 3, activation='relu', padding='same')(pool2) + encode3 = layers.Conv2D(depth*4, 3, activation='relu', padding='same')(encode3) + pool3 = layers.MaxPooling2D(pool_size=(2,2), padding='same')(encode3) + + encode4 = layers.Conv2D(depth*8, 3, activation='relu', padding='same')(pool3) + encode4 = layers.Conv2D(depth*8, 3, activation='relu', padding='same')(encode4) + pool4 = layers.MaxPooling2D(pool_size=(2,2), padding='same')(encode4) + + encode5 = layers.Conv2D(depth*16, 3, activation='relu', padding='same')(pool4) + encode5 = layers.Conv2D(depth*16, 3, activation='relu', padding='same')(encode5) + + up1 = layers.Conv2DTranspose(depth*8, 2, strides=2, padding='same')(encode5) + merge1 = layers.concatenate([encode4, up1]) + decode1 = layers.Conv2D(depth*8, 3, activation='relu', padding='same')(merge1) + decode1 = layers.Conv2D(depth*8, 3, activation='relu', padding='same')(decode1) + + up2 = layers.Conv2DTranspose(depth*4, 2, strides=2, padding='same')(decode1) + merge2 = layers.concatenate([encode3, up2]) + decode2 = layers.Conv2D(depth*4, 3, activation='relu', padding='same')(merge2) + decode2 = layers.Conv2D(depth*4, 3, activation='relu', padding='same')(decode2) + + up3 = layers.Conv2DTranspose(depth*2, 2, strides=2, padding='same')(decode2) + merge3 = layers.concatenate([encode2, up3]) + decode3 = layers.Conv2D(depth*2, 3, activation='relu', padding='same')(merge3) + decode3 = layers.Conv2D(depth*2, 3, activation='relu', padding='same')(decode3) + + up4 = layers.Conv2DTranspose(depth, 2, strides=2, padding='same')(decode3) + merge4 = layers.concatenate([encode1, up4]) + decode4 = layers.Conv2D(depth, 3, activation='relu', padding='same')(merge4) + decode4 = layers.Conv2D(depth, 3, activation='relu', padding='same')(decode4) + tensor_out = layers.Conv2D(1, 1, activation='sigmoid', padding='same')(decode4) + model = models.Model(inputs=tensor_in, outputs=tensor_out) + \ No newline at end of file From 926cc228d666e1b6538c8129f8cb4c8d881d510e Mon Sep 17 00:00:00 2001 From: Alexander Date: Mon, 28 Oct 2024 00:49:35 +1000 Subject: [PATCH 03/11] added basic training module based off of lecture example and medium guide --- recognition/modules.py | 6 ++++-- recognition/train.py | 22 ++++++++++++++++++++++ 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/recognition/modules.py b/recognition/modules.py index a50dc0537..3d13436df 100644 --- a/recognition/modules.py +++ b/recognition/modules.py @@ -2,7 +2,8 @@ import tensorflow as tf from tensorflow.keras import layers, models -def unet(tensor_in, depth=64): + +def init_unet(tensor_in, depth=64): # For each layer of the UNET filter convolute twice and then pool. encode1 = layers.Conv2D(depth, 3, activation='relu', padding='same')(tensor_in) @@ -43,6 +44,7 @@ def unet(tensor_in, depth=64): merge4 = layers.concatenate([encode1, up4]) decode4 = layers.Conv2D(depth, 3, activation='relu', padding='same')(merge4) decode4 = layers.Conv2D(depth, 3, activation='relu', padding='same')(decode4) - tensor_out = layers.Conv2D(1, 1, activation='sigmoid', padding='same')(decode4) + tensor_out = layers.Conv2D(1, 1, activation='softmax', padding='same')(decode4) model = models.Model(inputs=tensor_in, outputs=tensor_out) + return model \ No newline at end of file diff --git a/recognition/train.py b/recognition/train.py index 25002c66a..b4303126c 100644 --- a/recognition/train.py +++ b/recognition/train.py @@ -1 +1,23 @@ # This is for the training algorithm +import numpy as np +import tensorflow as tf +from tensorflow.keras import layers, models +from tensorflow.keras import backend as K +from tensorflow.keras.callbacks import EarlyStopping + +from modules import init_unet +from dataset import load_data_2D, to_channels + +# https://medium.com/@vipul.sarode007/u-net-unleashed-a-step-by-step-guide-on-implementing-and-training-your-own-segmentation-model-in-a90ed89399c6 +def dice_coeff(y_true, y_pred, smooth = 1): + intersection = K.sum(y_true*y_pred, axis = -1) + union = K.sum(y_true, axis = -1) + K.sum(y_pred, axis = -1) + dice_coeff = (2*intersection+smooth) / (union + smooth) + return dice_coeff + +def train_unet( data_train, data_train_cat, data_test, data_test_cat, epochs=32, batch_size=8): + init_inputs = layers.Input(shape=(256, 128, 1)) + model = init_unet(init_inputs) + early_stopping = EarlyStopping(monitor = 'val_loss', patience = 3, restore_best_weights = True) + model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy', dice_coeff]) + model.fit(data_train, data_train_cat, epochs=epochs, batch_size=batch_size, validation_data=(data_test, data_test_cat), callbacks=[early_stopping]) \ No newline at end of file From b8bd6904c44568347c8f6209569876055094db17 Mon Sep 17 00:00:00 2001 From: Alexander Date: Mon, 28 Oct 2024 12:31:21 +1000 Subject: [PATCH 04/11] added functionality for saving and loading data --- recognition/predict.py | 34 +++++++++++++++++++++++++++++++++- recognition/train.py | 18 ++++++++++++------ 2 files changed, 45 insertions(+), 7 deletions(-) diff --git a/recognition/predict.py b/recognition/predict.py index b71fb1906..3f8d637f6 100644 --- a/recognition/predict.py +++ b/recognition/predict.py @@ -1 +1,33 @@ -# Trained Model Usage +import numpy as np +import tensorflow as tf +from sklearn.metrics import classification_report +from dataset import load_data_2D +from train import train_unet +import matplotlib.pyplot as plt + + +# Function to take a trained unet model and use it for predictions. +def unet_predict(model, data_test): + + predictions = model.predict(data_test) + # Get predicted segmented images + print(np.argmax(predictions, axis=1)) + print(classification_report(data_test, predictions)) + + # Create a plot to plot predictions against tests + # reference: https://matplotlib.org/stable/gallery/subplots_axes_and_figures/subplots_demo.html + _, ax = plt.subplots(len(predictions), 2) + for i in range(len(predictions)): + ax[0, i].set_title(f"[{i}] prediction") + ax[0, i].im_show(predictions[i].squeeze()) + ax[1, i].set_title("Test Data") + ax[1, i].im_show(data_test[i].squeeze()) + plt.show() + return predictions + +def main(): + # load and evaluate model reference: https://www.tensorflow.org/tutorials/keras/save_and_load + model = tf.keras.models.load_model('mri_unet.keras') + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/recognition/train.py b/recognition/train.py index b4303126c..f9e930dd2 100644 --- a/recognition/train.py +++ b/recognition/train.py @@ -9,15 +9,21 @@ from dataset import load_data_2D, to_channels # https://medium.com/@vipul.sarode007/u-net-unleashed-a-step-by-step-guide-on-implementing-and-training-your-own-segmentation-model-in-a90ed89399c6 -def dice_coeff(y_true, y_pred, smooth = 1): - intersection = K.sum(y_true*y_pred, axis = -1) - union = K.sum(y_true, axis = -1) + K.sum(y_pred, axis = -1) - dice_coeff = (2*intersection+smooth) / (union + smooth) +# Function to calculate the mathmatical dice coefficient +def dice_coeff(y_true, y_predicted): + intersection = K.sum(y_true*y_predicted, axis = -1) + union = K.sum(y_true, axis = -1) + K.sum(y_predicted, axis = -1) + dice_coeff = (2*intersection) / (union) return dice_coeff -def train_unet( data_train, data_train_cat, data_test, data_test_cat, epochs=32, batch_size=8): + +#Function to train the Unet model, using tensorflow's inbuilt keras.model.fit() function +def train_unet( data_train, data_train_seg, data_validate, data_validate_seg, epochs=32, batch_size=8): init_inputs = layers.Input(shape=(256, 128, 1)) model = init_unet(init_inputs) early_stopping = EarlyStopping(monitor = 'val_loss', patience = 3, restore_best_weights = True) + # Compile and train the UNET model on the training set and validation set model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy', dice_coeff]) - model.fit(data_train, data_train_cat, epochs=epochs, batch_size=batch_size, validation_data=(data_test, data_test_cat), callbacks=[early_stopping]) \ No newline at end of file + model.fit(data_train, data_train_seg, epochs=epochs, batch_size=batch_size, validation_data=(data_validate, data_validate_seg), callbacks=[early_stopping]) + return model + From 1075154b0aeada75886bc48d8a69c4c176835d5d Mon Sep 17 00:00:00 2001 From: Alexander Date: Mon, 28 Oct 2024 12:41:41 +1000 Subject: [PATCH 05/11] basic predict printing predicted data and test data --- recognition/predict.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/recognition/predict.py b/recognition/predict.py index 3f8d637f6..03badef36 100644 --- a/recognition/predict.py +++ b/recognition/predict.py @@ -23,11 +23,13 @@ def unet_predict(model, data_test): ax[1, i].set_title("Test Data") ax[1, i].im_show(data_test[i].squeeze()) plt.show() - return predictions def main(): # load and evaluate model reference: https://www.tensorflow.org/tutorials/keras/save_and_load + # load the trained model model = tf.keras.models.load_model('mri_unet.keras') + data_test = load_data_2D() + unet_predict(model, data_test) if __name__ == "__main__": main() \ No newline at end of file From 18821d635c172af981572ff55913c93999a80f94 Mon Sep 17 00:00:00 2001 From: Alexander Date: Mon, 28 Oct 2024 13:11:51 +1000 Subject: [PATCH 06/11] updated predict and train to better reflect seperation of steps between files --- recognition/modules.py | 3 +-- recognition/predict.py | 12 +++++++++++- recognition/train.py | 18 +++++++++++++++--- 3 files changed, 27 insertions(+), 6 deletions(-) diff --git a/recognition/modules.py b/recognition/modules.py index 3d13436df..abfbd8233 100644 --- a/recognition/modules.py +++ b/recognition/modules.py @@ -46,5 +46,4 @@ def init_unet(tensor_in, depth=64): decode4 = layers.Conv2D(depth, 3, activation='relu', padding='same')(decode4) tensor_out = layers.Conv2D(1, 1, activation='softmax', padding='same')(decode4) model = models.Model(inputs=tensor_in, outputs=tensor_out) - return model - \ No newline at end of file + return model \ No newline at end of file diff --git a/recognition/predict.py b/recognition/predict.py index 03badef36..443cbe44a 100644 --- a/recognition/predict.py +++ b/recognition/predict.py @@ -8,7 +8,9 @@ # Function to take a trained unet model and use it for predictions. def unet_predict(model, data_test): - + """ + Function to predict the model + """ predictions = model.predict(data_test) # Get predicted segmented images print(np.argmax(predictions, axis=1)) @@ -24,12 +26,20 @@ def unet_predict(model, data_test): ax[1, i].im_show(data_test[i].squeeze()) plt.show() + def main(): # load and evaluate model reference: https://www.tensorflow.org/tutorials/keras/save_and_load # load the trained model model = tf.keras.models.load_model('mri_unet.keras') + model.summary() + data_train = load_data_2D() + data_validate = load_data_2D() data_test = load_data_2D() + _, acc = model.evaluate(data_train, data_validate, verbose=2) + print('Restored model, accuracy: {:5.2f}%'.format(100 * acc)) unet_predict(model, data_test) + + if __name__ == "__main__": main() \ No newline at end of file diff --git a/recognition/train.py b/recognition/train.py index f9e930dd2..e2f560ddb 100644 --- a/recognition/train.py +++ b/recognition/train.py @@ -18,12 +18,24 @@ def dice_coeff(y_true, y_predicted): #Function to train the Unet model, using tensorflow's inbuilt keras.model.fit() function -def train_unet( data_train, data_train_seg, data_validate, data_validate_seg, epochs=32, batch_size=8): +def train_unet(data_train, data_train_seg, data_validate, data_validate_seg, epochs=32, batch_size=8): init_inputs = layers.Input(shape=(256, 128, 1)) model = init_unet(init_inputs) early_stopping = EarlyStopping(monitor = 'val_loss', patience = 3, restore_best_weights = True) # Compile and train the UNET model on the training set and validation set model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy', dice_coeff]) model.fit(data_train, data_train_seg, epochs=epochs, batch_size=batch_size, validation_data=(data_validate, data_validate_seg), callbacks=[early_stopping]) - return model - + # save model reference: https://www.tensorflow.org/tutorials/keras/save_and_load + model.save('mri_unet.keras') + +def main(): + data_train = load_data_2D() + data_train_seg = load_data_2D() + + data_validate = load_data_2D() + data_validate_seg = load_data_2D() + + train_unet(data_train, data_train_seg, data_validate, data_validate_seg) + +if __name__ == "__main__": + main() \ No newline at end of file From f4c962cce21781fba24f951de382b85434a92505 Mon Sep 17 00:00:00 2001 From: Alexander Date: Mon, 28 Oct 2024 14:59:29 +1000 Subject: [PATCH 07/11] added README report --- recognition/README.md | 31 +++++++++++++++++++++---------- recognition/modules.py | 13 ++++++++++++- recognition/predict.py | 5 ++++- recognition/train.py | 10 +++++++++- 4 files changed, 46 insertions(+), 13 deletions(-) diff --git a/recognition/README.md b/recognition/README.md index 32c99e899..983c16b89 100644 --- a/recognition/README.md +++ b/recognition/README.md @@ -1,10 +1,21 @@ -# 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 \ No newline at end of file +# 2D UNet For Predicting Prostate Cancer from HipMRI Scans +**NOTE** -- Unable to conduct tests as training data was made unavaliable (https://edstem.org/au/courses/18266/discussion/2340286) & email to shakes. +## Prostate Cancer HipMRI Application +The purpose of this CNN UNet model is to assist in the diagnosis of prostate cancer through analysis of Hip MRI scans. The model's purpose is to be trained on images, such that it can segment and identify points of interest within the image. By training the Unet model in such a way that it recognises propoerties of cancer and malignent cells, it can assist profesionals in pinpointing the precise location of these issues. +## Algorithm Description + +### UNet Model +The UNet model is a convoluted neural network that works by taking image segmentation, encoding it via several convolution layers. Then decoding it via the transpose convolution, while taking raw imports from the opposite encoding stage. That is to say, that a 2D Unet Model, for each layer of convolution stores the resultant convoluted image, and then concatenates these convoluted images with the same depth and width images obtained during the decoding steps. In doing this trianing can be conducted faster as more of the initial image is retained during training steps. + +### Encoding steps +Each level of encoding consists of 2 convolution steps, used for capturing the context of the image, and then a pooling step which reduces the dimensions of the image in preperation of the next step. This dimension reduction results in a increase in channels porportional to the level of dimension reduction. This spacial reduction is done until a desired bottleneck dimension, with a large number of convolution channels, is reached. Each convolution step is done using the Tensorflow.keras library's inbuilt Convolution function (https://www.tensorflow.org/api_docs/python/tf/keras/layers/Conv2D). The kernel size used was 3x3, with the convolution being padded such that the output was the same dimensions as the input. The activation function chosen was a rectifier function, such that only the positive components of the arguments are used. + +### Decoding steps +Once the bottleneck is reached, 2 more convolution steps are done, but no further dimension reduction is conducted on the image, in preperation of the restoration of the image. During the decode steps of the model, The reduced image is transformed using Tensorflow.keras library's transpose convolution (https://www.tensorflow.org/api_docs/python/tf/keras/layers/Conv2DTranspose), such that a dimension reduction step is undone. Resulting in a halving of channels, but a doubling of image size. The image is then concatenated with a snapshot of the image at the encoded layer of the same dimension. this combined image is then convoluted twice using the same parameters as encode, before undergoing the next decoding step. Once the image is of the original image's dimension it is convoluted once with a 1x1 kernel through 1 channel, using softmax activation so a segmented image is obtained. + +The model is then trained, such that each channel's weighting and bias can be accurately estimated by the model. Resulting in a reduction of the loss value. The loss function chosen was the catagorical crossentropy (https://www.tensorflow.org/api_docs/python/tf/keras/losses/categorical_crossentropy). + +~~ Example usage~~ + +### Preprocessing +The program handles all several aspects of dataset loading and manipulation prior to the main training loop to ensure efficent training procedure. \ No newline at end of file diff --git a/recognition/modules.py b/recognition/modules.py index abfbd8233..73eeee0a6 100644 --- a/recognition/modules.py +++ b/recognition/modules.py @@ -4,10 +4,21 @@ def init_unet(tensor_in, depth=64): - + """ + Function to initial the convolution layers of the UNet. + parameters: + - tensor_in - the initial tensor filter to use for the model + - depth - the number of channels/depth to use at the top layer of extraction (default 64) + Return: + NULL + """ + # For each layer of the UNET filter convolute twice and then pool. + # Complete 2 convolutions to condense the image + # Model based on https://colab.research.google.com/drive/1K2kiAJSCa6IiahKxfAIv4SQ4BFq7YDYO?usp=sharing encode1 = layers.Conv2D(depth, 3, activation='relu', padding='same')(tensor_in) encode1 = layers.Conv2D(depth, 3, activation='relu', padding='same')(encode1) + # Complete dimension reduction pool1 = layers.MaxPooling2D(pool_size=(2,2), padding='same')(encode1) encode2 = layers.Conv2D(depth*2, 3, activation='relu', padding='same')(pool1) diff --git a/recognition/predict.py b/recognition/predict.py index 443cbe44a..9fb7909db 100644 --- a/recognition/predict.py +++ b/recognition/predict.py @@ -10,6 +10,9 @@ def unet_predict(model, data_test): """ Function to predict the model + Paramaters: + - model - the trained unet model to use for the predictions + - data_test - the data set to use for predictions """ predictions = model.predict(data_test) # Get predicted segmented images @@ -38,7 +41,7 @@ def main(): _, acc = model.evaluate(data_train, data_validate, verbose=2) print('Restored model, accuracy: {:5.2f}%'.format(100 * acc)) unet_predict(model, data_test) - + if __name__ == "__main__": diff --git a/recognition/train.py b/recognition/train.py index e2f560ddb..16943a2f7 100644 --- a/recognition/train.py +++ b/recognition/train.py @@ -11,6 +11,10 @@ # https://medium.com/@vipul.sarode007/u-net-unleashed-a-step-by-step-guide-on-implementing-and-training-your-own-segmentation-model-in-a90ed89399c6 # Function to calculate the mathmatical dice coefficient def dice_coeff(y_true, y_predicted): + """ + Function to calculate the max label dice coefficient + + """ intersection = K.sum(y_true*y_predicted, axis = -1) union = K.sum(y_true, axis = -1) + K.sum(y_predicted, axis = -1) dice_coeff = (2*intersection) / (union) @@ -19,12 +23,16 @@ def dice_coeff(y_true, y_predicted): #Function to train the Unet model, using tensorflow's inbuilt keras.model.fit() function def train_unet(data_train, data_train_seg, data_validate, data_validate_seg, epochs=32, batch_size=8): + """ + Training function for the unet based on provided data and segmented data. + """ init_inputs = layers.Input(shape=(256, 128, 1)) model = init_unet(init_inputs) early_stopping = EarlyStopping(monitor = 'val_loss', patience = 3, restore_best_weights = True) # Compile and train the UNET model on the training set and validation set + # Using the loss function for ones hot encoding model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy', dice_coeff]) - model.fit(data_train, data_train_seg, epochs=epochs, batch_size=batch_size, validation_data=(data_validate, data_validate_seg), callbacks=[early_stopping]) + model.fit(data_train, data_train_seg, epochs=epochs, batch_size=batch_size, validation_data=(data_validate, data_validate_seg), callbacks=[early_stopping], verbose=1) # save model reference: https://www.tensorflow.org/tutorials/keras/save_and_load model.save('mri_unet.keras') From ba1a5eace1ef4a7285544dbdea521695b21087bf Mon Sep 17 00:00:00 2001 From: Alexander Date: Mon, 28 Oct 2024 15:06:04 +1000 Subject: [PATCH 08/11] corrected initial depth --- recognition/modules.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/recognition/modules.py b/recognition/modules.py index 73eeee0a6..f4cc939d6 100644 --- a/recognition/modules.py +++ b/recognition/modules.py @@ -3,16 +3,15 @@ from tensorflow.keras import layers, models -def init_unet(tensor_in, depth=64): +def init_unet(tensor_in, depth=32): """ Function to initial the convolution layers of the UNet. parameters: - tensor_in - the initial tensor filter to use for the model - - depth - the number of channels/depth to use at the top layer of extraction (default 64) + - depth - the number of channels/depth to use at the top layer of extraction (default 32 (based on lecture)) Return: NULL """ - # For each layer of the UNET filter convolute twice and then pool. # Complete 2 convolutions to condense the image # Model based on https://colab.research.google.com/drive/1K2kiAJSCa6IiahKxfAIv4SQ4BFq7YDYO?usp=sharing From 96aa5ceb06d26a501f32d33a71cdab919b437a56 Mon Sep 17 00:00:00 2001 From: Alexander Date: Mon, 28 Oct 2024 15:22:16 +1000 Subject: [PATCH 09/11] optimized dice calculation --- recognition/train.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/recognition/train.py b/recognition/train.py index 16943a2f7..b3e69d06c 100644 --- a/recognition/train.py +++ b/recognition/train.py @@ -10,15 +10,12 @@ # https://medium.com/@vipul.sarode007/u-net-unleashed-a-step-by-step-guide-on-implementing-and-training-your-own-segmentation-model-in-a90ed89399c6 # Function to calculate the mathmatical dice coefficient -def dice_coeff(y_true, y_predicted): +def dice(y_true, y_predicted): """ Function to calculate the max label dice coefficient """ - intersection = K.sum(y_true*y_predicted, axis = -1) - union = K.sum(y_true, axis = -1) + K.sum(y_predicted, axis = -1) - dice_coeff = (2*intersection) / (union) - return dice_coeff + return (2 * K.sum(y_true*y_predicted, axis = -1)) / (K.sum(y_true, axis = -1) + K.sum(y_predicted, axis = -1)) #Function to train the Unet model, using tensorflow's inbuilt keras.model.fit() function @@ -31,7 +28,7 @@ def train_unet(data_train, data_train_seg, data_validate, data_validate_seg, epo early_stopping = EarlyStopping(monitor = 'val_loss', patience = 3, restore_best_weights = True) # Compile and train the UNET model on the training set and validation set # Using the loss function for ones hot encoding - model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy', dice_coeff]) + model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy', dice]) model.fit(data_train, data_train_seg, epochs=epochs, batch_size=batch_size, validation_data=(data_validate, data_validate_seg), callbacks=[early_stopping], verbose=1) # save model reference: https://www.tensorflow.org/tutorials/keras/save_and_load model.save('mri_unet.keras') From 6fa99dd3f2bc907676934be5e6a1a78339119032 Mon Sep 17 00:00:00 2001 From: Alexander Date: Mon, 28 Oct 2024 15:28:01 +1000 Subject: [PATCH 10/11] added dependicies and references --- recognition/README.md | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/recognition/README.md b/recognition/README.md index 983c16b89..495ce0d02 100644 --- a/recognition/README.md +++ b/recognition/README.md @@ -15,7 +15,17 @@ Once the bottleneck is reached, 2 more convolution steps are done, but no furthe The model is then trained, such that each channel's weighting and bias can be accurately estimated by the model. Resulting in a reduction of the loss value. The loss function chosen was the catagorical crossentropy (https://www.tensorflow.org/api_docs/python/tf/keras/losses/categorical_crossentropy). -~~ Example usage~~ +## ~~ Example usage~~ ### Preprocessing -The program handles all several aspects of dataset loading and manipulation prior to the main training loop to ensure efficent training procedure. \ No newline at end of file +The program handles all several aspects of dataset loading and manipulation prior to the main training loop to ensure efficent training procedure. + +## Dependicies +**Python3** +**Tensorflow** +**Numpy** +**matplotlib.pyplot** +**sklearn.metrics** + +## References +relevant references are linked throughout the report, alongside the lecture example for a UNet and succesful pull requests from previous years. From 3200528532af012bed9ca062cd8f8622a1b59a0e Mon Sep 17 00:00:00 2001 From: Alexander Date: Wed, 20 Nov 2024 16:57:00 +1000 Subject: [PATCH 11/11] corrected file structure and directory struture --- recognition/{ => 2D_Unet_47423510}/README.md | 0 recognition/{ => 2D_Unet_47423510}/dataset.py | 0 recognition/{ => 2D_Unet_47423510}/modules.py | 0 recognition/{ => 2D_Unet_47423510}/predict.py | 0 recognition/{ => 2D_Unet_47423510}/train.py | 0 5 files changed, 0 insertions(+), 0 deletions(-) rename recognition/{ => 2D_Unet_47423510}/README.md (100%) rename recognition/{ => 2D_Unet_47423510}/dataset.py (100%) rename recognition/{ => 2D_Unet_47423510}/modules.py (100%) rename recognition/{ => 2D_Unet_47423510}/predict.py (100%) rename recognition/{ => 2D_Unet_47423510}/train.py (100%) diff --git a/recognition/README.md b/recognition/2D_Unet_47423510/README.md similarity index 100% rename from recognition/README.md rename to recognition/2D_Unet_47423510/README.md diff --git a/recognition/dataset.py b/recognition/2D_Unet_47423510/dataset.py similarity index 100% rename from recognition/dataset.py rename to recognition/2D_Unet_47423510/dataset.py diff --git a/recognition/modules.py b/recognition/2D_Unet_47423510/modules.py similarity index 100% rename from recognition/modules.py rename to recognition/2D_Unet_47423510/modules.py diff --git a/recognition/predict.py b/recognition/2D_Unet_47423510/predict.py similarity index 100% rename from recognition/predict.py rename to recognition/2D_Unet_47423510/predict.py diff --git a/recognition/train.py b/recognition/2D_Unet_47423510/train.py similarity index 100% rename from recognition/train.py rename to recognition/2D_Unet_47423510/train.py