diff --git a/examples/vision/3D_image_classification.py b/examples/vision/3D_image_classification.py index 2987dc96d6..77996d0b89 100644 --- a/examples/vision/3D_image_classification.py +++ b/examples/vision/3D_image_classification.py @@ -2,9 +2,10 @@ Title: 3D image classification from CT scans Author: [Hasib Zunair](https://twitter.com/hasibzunair) Date created: 2020/09/23 -Last modified: 2020/09/23 +Last modified: 2024/01/11 Description: Train a 3D convolutional neural network to predict presence of pneumonia. Accelerator: GPU +Converted to Keras 3 by: [Sitam Meur](https://github.com/sitamgithub-MSIT) """ """ ## Introduction @@ -27,12 +28,15 @@ """ import os + +os.environ["KERAS_BACKEND"] = "tensorflow" import zipfile import numpy as np -import tensorflow as tf -from tensorflow import keras -from tensorflow.keras import layers +import keras +from keras import ops +from keras import layers +import tensorflow as tf """ ## Downloading the MosMedData: Chest CT Scans with COVID-19 Related Findings @@ -177,19 +181,19 @@ def process_scan(path): # Read and process the scans. # Each scan is resized across height, width, and depth and rescaled. -abnormal_scans = np.array([process_scan(path) for path in abnormal_scan_paths]) -normal_scans = np.array([process_scan(path) for path in normal_scan_paths]) +abnormal_scans = ops.array([process_scan(path) for path in abnormal_scan_paths]) +normal_scans = ops.array([process_scan(path) for path in normal_scan_paths]) # For the CT scans having presence of viral pneumonia # assign 1, for the normal ones assign 0. -abnormal_labels = np.array([1 for _ in range(len(abnormal_scans))]) -normal_labels = np.array([0 for _ in range(len(normal_scans))]) +abnormal_labels = ops.array([1 for _ in range(len(abnormal_scans))]) +normal_labels = ops.array([0 for _ in range(len(normal_scans))]) # Split data in the ratio 70-30 for training and validation. -x_train = np.concatenate((abnormal_scans[:70], normal_scans[:70]), axis=0) -y_train = np.concatenate((abnormal_labels[:70], normal_labels[:70]), axis=0) -x_val = np.concatenate((abnormal_scans[70:], normal_scans[70:]), axis=0) -y_val = np.concatenate((abnormal_labels[70:], normal_labels[70:]), axis=0) +x_train = ops.concatenate((abnormal_scans[:70], normal_scans[:70]), axis=0) +y_train = ops.concatenate((abnormal_labels[:70], normal_labels[:70]), axis=0) +x_val = ops.concatenate((abnormal_scans[70:], normal_scans[70:]), axis=0) +y_val = ops.concatenate((abnormal_labels[70:], normal_labels[70:]), axis=0) print( "Number of samples in train and validation are %d and %d." % (x_train.shape[0], x_val.shape[0]) @@ -210,37 +214,28 @@ def process_scan(path): from scipy import ndimage +random_rotation = layers.RandomRotation(factor=(-0.06, 0.06)) + -@tf.function def rotate(volume): """Rotate the volume by a few degrees""" - - def scipy_rotate(volume): - # define some rotation angles - angles = [-20, -10, -5, 5, 10, 20] - # pick angles at random - angle = random.choice(angles) - # rotate volume - volume = ndimage.rotate(volume, angle, reshape=False) - volume[volume < 0] = 0 - volume[volume > 1] = 1 - return volume - - augmented_volume = tf.numpy_function(scipy_rotate, [volume], tf.float32) - return augmented_volume + # rotate volume + volume = random_rotation(volume) + volume = ops.clip(volume, 0, 1) + return volume def train_preprocessing(volume, label): """Process training data by rotating and adding a channel.""" # Rotate volume volume = rotate(volume) - volume = tf.expand_dims(volume, axis=3) + volume = ops.expand_dims(volume, axis=3) return volume, label def validation_preprocessing(volume, label): """Process validation data by only adding a channel.""" - volume = tf.expand_dims(volume, axis=3) + volume = ops.expand_dims(volume, axis=3) return volume, label @@ -278,10 +273,10 @@ def validation_preprocessing(volume, label): data = train_dataset.take(1) images, labels = list(data)[0] -images = images.numpy() +images = ops.convert_to_numpy(images) image = images[0] print("Dimension of the CT scan is:", image.shape) -plt.imshow(np.squeeze(image[:, :, 30]), cmap="gray") +plt.imshow(ops.squeeze(image[:, :, 30]), cmap="gray") """ @@ -291,9 +286,9 @@ def validation_preprocessing(volume, label): def plot_slices(num_rows, num_columns, width, height, data): """Plot a montage of 20 CT slices""" - data = np.rot90(np.array(data)) - data = np.transpose(data) - data = np.reshape(data, (num_rows, num_columns, width, height)) + data = ndimage.rotate(ops.array(data), 90, reshape=False) + data = ops.transpose(data) + data = ops.reshape(data, (num_rows, num_columns, width, height)) rows_data, columns_data = data.shape[0], data.shape[1] heights = [slc[0].shape[0] for slc in data] widths = [slc.shape[1] for slc in data[0]] @@ -379,7 +374,7 @@ def get_model(width=128, height=128, depth=64): # Define callbacks. checkpoint_cb = keras.callbacks.ModelCheckpoint( - "3d_image_classification.h5", save_best_only=True + "3d_image_classification.keras", save_best_only=True ) early_stopping_cb = keras.callbacks.EarlyStopping(monitor="val_acc", patience=15) @@ -426,8 +421,8 @@ def get_model(width=128, height=128, depth=64): """ # Load best weights. -model.load_weights("3d_image_classification.h5") -prediction = model.predict(np.expand_dims(x_val[0], axis=0))[0] +model.load_weights("3d_image_classification.keras") +prediction = model.predict(ops.expand_dims(x_val[0], axis=0))[0] scores = [1 - prediction[0], prediction[0]] class_names = ["normal", "abnormal"] diff --git a/examples/vision/ipynb/3D_image_classification.ipynb b/examples/vision/ipynb/3D_image_classification.ipynb index a2ecf82785..3c2f2bbe4d 100644 --- a/examples/vision/ipynb/3D_image_classification.ipynb +++ b/examples/vision/ipynb/3D_image_classification.ipynb @@ -10,7 +10,7 @@ "\n", "**Author:** [Hasib Zunair](https://twitter.com/hasibzunair)
\n", "**Date created:** 2020/09/23
\n", - "**Last modified:** 2020/09/23
\n", + "**Last modified:** 2024/01/11
\n", "**Description:** Train a 3D convolutional neural network to predict presence of pneumonia." ] }, @@ -47,19 +47,21 @@ }, { "cell_type": "code", - "execution_count": 0, + "execution_count": null, "metadata": { "colab_type": "code" }, "outputs": [], "source": [ "import os\n", + "os.environ[\"KERAS_BACKEND\"] = \"tensorflow\"\n", "import zipfile\n", "import numpy as np\n", - "import tensorflow as tf\n", "\n", - "from tensorflow import keras\n", - "from tensorflow.keras import layers" + "import keras\n", + "from keras import ops\n", + "from keras import layers\n", + "import tensorflow as tf" ] }, { @@ -81,7 +83,7 @@ }, { "cell_type": "code", - "execution_count": 0, + "execution_count": null, "metadata": { "colab_type": "code" }, @@ -135,7 +137,7 @@ }, { "cell_type": "code", - "execution_count": 0, + "execution_count": null, "metadata": { "colab_type": "code" }, @@ -199,8 +201,7 @@ " volume = normalize(volume)\n", " # Resize width, height and depth\n", " volume = resize_volume(volume)\n", - " return volume\n", - "" + " return volume\n" ] }, { @@ -214,7 +215,7 @@ }, { "cell_type": "code", - "execution_count": 0, + "execution_count": null, "metadata": { "colab_type": "code" }, @@ -234,8 +235,7 @@ "]\n", "\n", "print(\"CT scans with normal lung tissue: \" + str(len(normal_scan_paths)))\n", - "print(\"CT scans with abnormal lung tissue: \" + str(len(abnormal_scan_paths)))\n", - "" + "print(\"CT scans with abnormal lung tissue: \" + str(len(abnormal_scan_paths)))\n" ] }, { @@ -252,7 +252,7 @@ }, { "cell_type": "code", - "execution_count": 0, + "execution_count": null, "metadata": { "colab_type": "code" }, @@ -260,19 +260,19 @@ "source": [ "# Read and process the scans.\n", "# Each scan is resized across height, width, and depth and rescaled.\n", - "abnormal_scans = np.array([process_scan(path) for path in abnormal_scan_paths])\n", - "normal_scans = np.array([process_scan(path) for path in normal_scan_paths])\n", + "abnormal_scans = ops.array([process_scan(path) for path in abnormal_scan_paths])\n", + "normal_scans = ops.array([process_scan(path) for path in normal_scan_paths])\n", "\n", "# For the CT scans having presence of viral pneumonia\n", "# assign 1, for the normal ones assign 0.\n", - "abnormal_labels = np.array([1 for _ in range(len(abnormal_scans))])\n", - "normal_labels = np.array([0 for _ in range(len(normal_scans))])\n", + "abnormal_labels = ops.array([1 for _ in range(len(abnormal_scans))])\n", + "normal_labels = ops.array([0 for _ in range(len(normal_scans))])\n", "\n", "# Split data in the ratio 70-30 for training and validation.\n", - "x_train = np.concatenate((abnormal_scans[:70], normal_scans[:70]), axis=0)\n", - "y_train = np.concatenate((abnormal_labels[:70], normal_labels[:70]), axis=0)\n", - "x_val = np.concatenate((abnormal_scans[70:], normal_scans[70:]), axis=0)\n", - "y_val = np.concatenate((abnormal_labels[70:], normal_labels[70:]), axis=0)\n", + "x_train = ops.concatenate((abnormal_scans[:70], normal_scans[:70]), axis=0)\n", + "y_train = ops.concatenate((abnormal_labels[:70], normal_labels[:70]), axis=0)\n", + "x_val = ops.concatenate((abnormal_scans[70:], normal_scans[70:]), axis=0)\n", + "y_val = ops.concatenate((abnormal_labels[70:], normal_labels[70:]), axis=0)\n", "print(\n", " \"Number of samples in train and validation are %d and %d.\"\n", " % (x_train.shape[0], x_val.shape[0])\n", @@ -297,7 +297,7 @@ }, { "cell_type": "code", - "execution_count": 0, + "execution_count": null, "metadata": { "colab_type": "code" }, @@ -307,39 +307,29 @@ "\n", "from scipy import ndimage\n", "\n", + "random_rotation = layers.RandomRotation(factor=(-0.06, 0.06))\n", + "\n", "\n", - "@tf.function\n", "def rotate(volume):\n", " \"\"\"Rotate the volume by a few degrees\"\"\"\n", - "\n", - " def scipy_rotate(volume):\n", - " # define some rotation angles\n", - " angles = [-20, -10, -5, 5, 10, 20]\n", - " # pick angles at random\n", - " angle = random.choice(angles)\n", - " # rotate volume\n", - " volume = ndimage.rotate(volume, angle, reshape=False)\n", - " volume[volume < 0] = 0\n", - " volume[volume > 1] = 1\n", - " return volume\n", - "\n", - " augmented_volume = tf.numpy_function(scipy_rotate, [volume], tf.float32)\n", - " return augmented_volume\n", + " # rotate volume\n", + " volume = random_rotation(volume)\n", + " volume = ops.clip(volume, 0, 1)\n", + " return volume\n", "\n", "\n", "def train_preprocessing(volume, label):\n", " \"\"\"Process training data by rotating and adding a channel.\"\"\"\n", " # Rotate volume\n", " volume = rotate(volume)\n", - " volume = tf.expand_dims(volume, axis=3)\n", + " volume = ops.expand_dims(volume, axis=3)\n", " return volume, label\n", "\n", "\n", "def validation_preprocessing(volume, label):\n", " \"\"\"Process validation data by only adding a channel.\"\"\"\n", - " volume = tf.expand_dims(volume, axis=3)\n", - " return volume, label\n", - "" + " volume = ops.expand_dims(volume, axis=3)\n", + " return volume, label\n" ] }, { @@ -355,7 +345,7 @@ }, { "cell_type": "code", - "execution_count": 0, + "execution_count": null, "metadata": { "colab_type": "code" }, @@ -393,7 +383,7 @@ }, { "cell_type": "code", - "execution_count": 0, + "execution_count": null, "metadata": { "colab_type": "code" }, @@ -403,11 +393,10 @@ "\n", "data = train_dataset.take(1)\n", "images, labels = list(data)[0]\n", - "images = images.numpy()\n", + "images = ops.convert_to_numpy(images)\n", "image = images[0]\n", "print(\"Dimension of the CT scan is:\", image.shape)\n", - "plt.imshow(np.squeeze(image[:, :, 30]), cmap=\"gray\")\n", - "" + "plt.imshow(ops.squeeze(image[:, :, 30]), cmap=\"gray\")\n" ] }, { @@ -421,7 +410,7 @@ }, { "cell_type": "code", - "execution_count": 0, + "execution_count": null, "metadata": { "colab_type": "code" }, @@ -430,9 +419,9 @@ "\n", "def plot_slices(num_rows, num_columns, width, height, data):\n", " \"\"\"Plot a montage of 20 CT slices\"\"\"\n", - " data = np.rot90(np.array(data))\n", - " data = np.transpose(data)\n", - " data = np.reshape(data, (num_rows, num_columns, width, height))\n", + " data = ndimage.rotate(ops.array(data), 90, reshape=False)\n", + " data = ops.transpose(data)\n", + " data = ops.reshape(data, (num_rows, num_columns, width, height))\n", " rows_data, columns_data = data.shape[0], data.shape[1]\n", " heights = [slc[0].shape[0] for slc in data]\n", " widths = [slc.shape[1] for slc in data[0]]\n", @@ -472,7 +461,7 @@ }, { "cell_type": "code", - "execution_count": 0, + "execution_count": null, "metadata": { "colab_type": "code" }, @@ -527,7 +516,7 @@ }, { "cell_type": "code", - "execution_count": 0, + "execution_count": null, "metadata": { "colab_type": "code" }, @@ -546,7 +535,7 @@ "\n", "# Define callbacks.\n", "checkpoint_cb = keras.callbacks.ModelCheckpoint(\n", - " \"3d_image_classification.h5\", save_best_only=True\n", + " \"3d_image_classification.keras\", save_best_only=True\n", ")\n", "early_stopping_cb = keras.callbacks.EarlyStopping(monitor=\"val_acc\", patience=15)\n", "\n", @@ -590,7 +579,7 @@ }, { "cell_type": "code", - "execution_count": 0, + "execution_count": null, "metadata": { "colab_type": "code" }, @@ -619,15 +608,15 @@ }, { "cell_type": "code", - "execution_count": 0, + "execution_count": null, "metadata": { "colab_type": "code" }, "outputs": [], "source": [ "# Load best weights.\n", - "model.load_weights(\"3d_image_classification.h5\")\n", - "prediction = model.predict(np.expand_dims(x_val[0], axis=0))[0]\n", + "model.load_weights(\"3d_image_classification.keras\")\n", + "prediction = model.predict(ops.expand_dims(x_val[0], axis=0))[0]\n", "scores = [1 - prediction[0], prediction[0]]\n", "\n", "class_names = [\"normal\", \"abnormal\"]\n", @@ -668,4 +657,4 @@ }, "nbformat": 4, "nbformat_minor": 0 -} \ No newline at end of file +} diff --git a/examples/vision/md/3D_image_classification.md b/examples/vision/md/3D_image_classification.md index c2cc2a7d4d..d8ff99f65f 100644 --- a/examples/vision/md/3D_image_classification.md +++ b/examples/vision/md/3D_image_classification.md @@ -2,7 +2,7 @@ **Author:** [Hasib Zunair](https://twitter.com/hasibzunair)
**Date created:** 2020/09/23
-**Last modified:** 2020/09/23
+**Last modified:** 2024/01/11
**Description:** Train a 3D convolutional neural network to predict presence of pneumonia. @@ -33,12 +33,14 @@ equivalent: it takes as input a 3D volume or a sequence of 2D frames (e.g. slice ```python import os +os.environ["KERAS_BACKEND"] = "tensorflow" import zipfile import numpy as np -import tensorflow as tf -from tensorflow import keras -from tensorflow.keras import layers +import keras +from keras import ops +from keras import layers +import tensorflow as tf ``` --- @@ -206,19 +208,19 @@ Lastly, split the dataset into train and validation subsets. ```python # Read and process the scans. # Each scan is resized across height, width, and depth and rescaled. -abnormal_scans = np.array([process_scan(path) for path in abnormal_scan_paths]) -normal_scans = np.array([process_scan(path) for path in normal_scan_paths]) +abnormal_scans = ops.array([process_scan(path) for path in abnormal_scan_paths]) +normal_scans = ops.array([process_scan(path) for path in normal_scan_paths]) # For the CT scans having presence of viral pneumonia # assign 1, for the normal ones assign 0. -abnormal_labels = np.array([1 for _ in range(len(abnormal_scans))]) -normal_labels = np.array([0 for _ in range(len(normal_scans))]) +abnormal_labels = ops.array([1 for _ in range(len(abnormal_scans))]) +normal_labels = ops.array([0 for _ in range(len(normal_scans))]) # Split data in the ratio 70-30 for training and validation. -x_train = np.concatenate((abnormal_scans[:70], normal_scans[:70]), axis=0) -y_train = np.concatenate((abnormal_labels[:70], normal_labels[:70]), axis=0) -x_val = np.concatenate((abnormal_scans[70:], normal_scans[70:]), axis=0) -y_val = np.concatenate((abnormal_labels[70:], normal_labels[70:]), axis=0) +x_train = ops.concatenate((abnormal_scans[:70], normal_scans[:70]), axis=0) +y_train = ops.concatenate((abnormal_labels[:70], normal_labels[:70]), axis=0) +x_val = ops.concatenate((abnormal_scans[70:], normal_scans[70:]), axis=0) +y_val = ops.concatenate((abnormal_labels[70:], normal_labels[70:]), axis=0) print( "Number of samples in train and validation are %d and %d." % (x_train.shape[0], x_val.shape[0]) @@ -247,37 +249,28 @@ import random from scipy import ndimage +random_rotation = layers.RandomRotation(factor=(-0.06, 0.06)) + -@tf.function def rotate(volume): """Rotate the volume by a few degrees""" - - def scipy_rotate(volume): - # define some rotation angles - angles = [-20, -10, -5, 5, 10, 20] - # pick angles at random - angle = random.choice(angles) - # rotate volume - volume = ndimage.rotate(volume, angle, reshape=False) - volume[volume < 0] = 0 - volume[volume > 1] = 1 - return volume - - augmented_volume = tf.numpy_function(scipy_rotate, [volume], tf.float32) - return augmented_volume + # rotate volume + volume = random_rotation(volume) + volume = ops.clip(volume, 0, 1) + return volume def train_preprocessing(volume, label): """Process training data by rotating and adding a channel.""" # Rotate volume volume = rotate(volume) - volume = tf.expand_dims(volume, axis=3) + volume = ops.expand_dims(volume, axis=3) return volume, label def validation_preprocessing(volume, label): """Process validation data by only adding a channel.""" - volume = tf.expand_dims(volume, axis=3) + volume = ops.expand_dims(volume, axis=3) return volume, label ``` @@ -317,10 +310,10 @@ import matplotlib.pyplot as plt data = train_dataset.take(1) images, labels = list(data)[0] -images = images.numpy() +images = ops.convert_to_numpy(images) image = images[0] print("Dimension of the CT scan is:", image.shape) -plt.imshow(np.squeeze(image[:, :, 30]), cmap="gray") +plt.imshow(ops.squeeze(image[:, :, 30]), cmap="gray") ``` @@ -342,9 +335,9 @@ Since a CT scan has many slices, let's visualize a montage of the slices. def plot_slices(num_rows, num_columns, width, height, data): """Plot a montage of 20 CT slices""" - data = np.rot90(np.array(data)) - data = np.transpose(data) - data = np.reshape(data, (num_rows, num_columns, width, height)) + data = ndimage.rotate(ops.array(data), 90, reshape=False) + data = ops.transpose(data) + data = ops.reshape(data, (num_rows, num_columns, width, height)) rows_data, columns_data = data.shape[0], data.shape[1] heights = [slc[0].shape[0] for slc in data] widths = [slc.shape[1] for slc in data[0]] @@ -485,7 +478,7 @@ model.compile( # Define callbacks. checkpoint_cb = keras.callbacks.ModelCheckpoint( - "3d_image_classification.h5", save_best_only=True + "3d_image_classification.keras", save_best_only=True ) early_stopping_cb = keras.callbacks.EarlyStopping(monitor="val_acc", patience=15) @@ -607,8 +600,8 @@ for i, metric in enumerate(["acc", "loss"]): ```python # Load best weights. -model.load_weights("3d_image_classification.h5") -prediction = model.predict(np.expand_dims(x_val[0], axis=0))[0] +model.load_weights("3d_image_classification.keras") +prediction = model.predict(ops.expand_dims(x_val[0], axis=0))[0] scores = [1 - prediction[0], prediction[0]] class_names = ["normal", "abnormal"]