diff --git a/Hyperspectral Image Classification/HSI.ipynb b/Hyperspectral Image Classification/HSI.ipynb new file mode 100644 index 0000000..6e82396 --- /dev/null +++ b/Hyperspectral Image Classification/HSI.ipynb @@ -0,0 +1,1501 @@ +{ + "nbformat": 4, + "nbformat_minor": 0, + "metadata": { + "colab": { + "name": "KSC_PCA.ipynb", + "provenance": [], + "collapsed_sections": [], + "machine_shape": "hm" + }, + "kernelspec": { + "name": "python3", + "display_name": "Python 3" + } + }, + "cells": [ + { + "cell_type": "code", + "metadata": { + "id": "cJTVxOQGz-cw" + }, + "source": [ + "import numpy as np\n", + "from sklearn.decomposition import PCA\n", + "import scipy.io as sio\n", + "from sklearn.model_selection import train_test_split\n", + "from sklearn import preprocessing\n", + "import os\n", + "import random\n", + "from random import shuffle\n", + "from skimage.transform import rotate\n", + "import scipy.ndimage\n", + "import matplotlib.pyplot as plt\n", + "%matplotlib inline" + ], + "execution_count": 60, + "outputs": [] + }, + { + "cell_type": "code", + "metadata": { + "id": "pQrsee9I0Qaa" + }, + "source": [ + "import torch" + ], + "execution_count": 61, + "outputs": [] + }, + { + "cell_type": "code", + "metadata": { + "id": "n3f-2cIx0USV" + }, + "source": [ + "from torch.utils.data import Dataset, DataLoader" + ], + "execution_count": 62, + "outputs": [] + }, + { + "cell_type": "code", + "metadata": { + "id": "8YegeH3I0YAk" + }, + "source": [ + "hsi_data= sio.loadmat('KSC.mat')['KSC']\n", + "labels = sio.loadmat('KSC_gt.mat')['KSC_gt']" + ], + "execution_count": 63, + "outputs": [] + }, + { + "cell_type": "code", + "metadata": { + "id": "X9RGVlN30YW4" + }, + "source": [ + "[height,width,depth]=hsi_data.shape" + ], + "execution_count": 64, + "outputs": [] + }, + { + "cell_type": "code", + "metadata": { + "id": "_u36Tv-i0YkC" + }, + "source": [ + "def splitTrainTestSet(X, y, testRatio=0.10):\n", + " X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=testRatio, random_state=345,\n", + " stratify=y)\n", + " return X_train, X_test, y_train, y_test\n", + "\n", + "def oversampleWeakClasses(X, y):\n", + " uniqueLabels, labelCounts = np.unique(y, return_counts=True)\n", + " maxCount = np.max(labelCounts)\n", + " labelInverseRatios = maxCount / labelCounts \n", + " # repeat for every label and concat\n", + " newX = X[y == uniqueLabels[0], :, :, :].repeat(round(labelInverseRatios[0]), axis=0)\n", + " newY = y[y == uniqueLabels[0]].repeat(round(labelInverseRatios[0]), axis=0)\n", + " for label, labelInverseRatio in zip(uniqueLabels[1:], labelInverseRatios[1:]):\n", + " cX = X[y== label,:,:,:].repeat(round(labelInverseRatio), axis=0)\n", + " cY = y[y == label].repeat(round(labelInverseRatio), axis=0)\n", + " newX = np.concatenate((newX, cX))\n", + " newY = np.concatenate((newY, cY))\n", + " np.random.seed(seed=42)\n", + " rand_perm = np.random.permutation(newY.shape[0])\n", + " newX = newX[rand_perm, :, :, :]\n", + " newY = newY[rand_perm]\n", + " return newX, newY" + ], + "execution_count": 65, + "outputs": [] + }, + { + "cell_type": "code", + "metadata": { + "id": "C-2VJDlm0Yns" + }, + "source": [ + "def standartizeData(X):\n", + " newX = np.reshape(X, (-1, X.shape[2]))\n", + " scaler = preprocessing.StandardScaler().fit(newX) \n", + " newX = scaler.transform(newX)\n", + " newX = np.reshape(newX, (X.shape[0],X.shape[1],X.shape[2]))\n", + " return newX, scaler\n", + "\n", + "def applyPCA(X, numComponents=75):\n", + " newX = np.reshape(X, (-1, X.shape[2]))\n", + " pca = PCA(n_components=numComponents, whiten=True)\n", + " newX = pca.fit_transform(newX)\n", + " newX = np.reshape(newX, (X.shape[0],X.shape[1], numComponents))\n", + " return newX, pca\n", + "\n", + "def padWithZeros(X, margin=2):\n", + " newX = np.zeros((X.shape[0] + 2 * margin, X.shape[1] + 2* margin, X.shape[2]))\n", + " x_offset = margin\n", + " y_offset = margin\n", + " newX[x_offset:X.shape[0] + x_offset, y_offset:X.shape[1] + y_offset, :] = X\n", + " return newX\n", + "\n", + "def createPatches(X, y, windowSize=11, removeZeroLabels = True):\n", + " margin = int((windowSize - 1) / 2)\n", + " zeroPaddedX = padWithZeros(X, margin=margin)\n", + " # split patches\n", + " patchesData = np.zeros((X.shape[0] * X.shape[1], windowSize, windowSize, X.shape[2]))\n", + " patchesLabels = np.zeros((X.shape[0] * X.shape[1]))\n", + " patchIndex = 0\n", + " for r in range(margin, zeroPaddedX.shape[0] - margin):\n", + " for c in range(margin, zeroPaddedX.shape[1] - margin):\n", + " patch = zeroPaddedX[r - margin:r + margin + 1, c - margin:c + margin + 1] \n", + " patchesData[patchIndex, :, :, :] = patch\n", + " patchesLabels[patchIndex] = y[r-margin, c-margin]\n", + " patchIndex = patchIndex + 1\n", + " if removeZeroLabels:\n", + " patchesData = patchesData[patchesLabels>0,:,:,:]\n", + " patchesLabels = patchesLabels[patchesLabels>0]\n", + " patchesLabels -= 1\n", + " return patchesData, patchesLabels\n", + "\n", + "\n", + "def AugmentData(X_train):\n", + " for i in range(int(X_train.shape[0]/2)):\n", + " patch = X_train[i,:,:,:]\n", + " num = random.randint(0,2)\n", + " if (num == 0):\n", + " \n", + " flipped_patch = np.flipud(patch)\n", + " if (num == 1):\n", + " \n", + " flipped_patch = np.fliplr(patch)\n", + " if (num == 2):\n", + " \n", + " no = random.randrange(-180,180,30)\n", + " flipped_patch = scipy.ndimage.interpolation.rotate(patch, no,axes=(1, 0),\n", + " reshape=False, output=None, order=3, mode='constant', cval=0.0, prefilter=False)\n", + " \n", + " \n", + " patch2 = flipped_patch\n", + " X_train[i,:,:,:] = patch2\n", + " \n", + " return X_train" + ], + "execution_count": 66, + "outputs": [] + }, + { + "cell_type": "code", + "metadata": { + "id": "ufC7Yw8O0Yqk" + }, + "source": [ + "import numpy as np\n", + "import scipy\n", + "import os\n", + "from keras.models import Sequential\n", + "from keras.layers import Dense, Dropout, Flatten\n", + "from keras.layers import Conv2D, MaxPooling2D,BatchNormalization\n", + "from tensorflow.keras.callbacks import EarlyStopping\n", + "from tensorflow.keras.optimizers import SGD\n", + "from keras import backend as K\n", + "from keras.utils import np_utils\n", + "from keras.utils.vis_utils import plot_model" + ], + "execution_count": 67, + "outputs": [] + }, + { + "cell_type": "code", + "metadata": { + "id": "StGbOjVp0Yt1" + }, + "source": [ + "weight_of_size=10" + ], + "execution_count": 68, + "outputs": [] + }, + { + "cell_type": "code", + "metadata": { + "id": "p7ZWKFn80-VW" + }, + "source": [ + "X=hsi_data\n", + "y=labels" + ], + "execution_count": 69, + "outputs": [] + }, + { + "cell_type": "code", + "metadata": { + "id": "ePFCJRgs1AvN" + }, + "source": [ + "X,pca = applyPCA(X,30)" + ], + "execution_count": 70, + "outputs": [] + }, + { + "cell_type": "code", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "Qz9K49eR1Ay8", + "outputId": "c0e55591-90e8-430a-d6be-47a04c5abba5" + }, + "source": [ + "X.shape" + ], + "execution_count": 71, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "(512, 614, 30)" + ] + }, + "metadata": {}, + "execution_count": 71 + } + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "V3F5oORe1A2R" + }, + "source": [ + "XPatches, yPatches = createPatches(X, y, windowSize=15)" + ], + "execution_count": 72, + "outputs": [] + }, + { + "cell_type": "code", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "PuYf0Pct1A5D", + "outputId": "e36ac328-6780-4845-f337-af2edc6b2f7b" + }, + "source": [ + "XPatches.shape" + ], + "execution_count": 73, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "(5211, 15, 15, 30)" + ] + }, + "metadata": {}, + "execution_count": 73 + } + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "kuYNa7oB1A8M" + }, + "source": [ + "X_train, X_test, y_train, y_test = splitTrainTestSet(XPatches, yPatches, testRatio=0.2)" + ], + "execution_count": 74, + "outputs": [] + }, + { + "cell_type": "code", + "metadata": { + "id": "fiFb0Wb51A_T" + }, + "source": [ + "X_train=np.reshape(X_train,(X_train.shape[0],X_train.shape[3],X_train.shape[1],X_train.shape[1]))" + ], + "execution_count": 75, + "outputs": [] + }, + { + "cell_type": "code", + "metadata": { + "id": "_qUQQRem1hRf" + }, + "source": [ + "X_test=np.reshape(X_test,(X_test.shape[0],X_test.shape[3],X_test.shape[1],X_test.shape[1]))" + ], + "execution_count": 76, + "outputs": [] + }, + { + "cell_type": "code", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "TetR8MOG1BCa", + "outputId": "c2d73dfa-a822-4a09-c432-cbaf37e38da6" + }, + "source": [ + "X_train.shape" + ], + "execution_count": 77, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "(4168, 30, 15, 15)" + ] + }, + "metadata": {}, + "execution_count": 77 + } + ] + }, + { + "cell_type": "code", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "1BimoC6S1SUu", + "outputId": "86a83c53-0e00-45b2-906f-1bef8afd2a0b" + }, + "source": [ + "X_train.shape" + ], + "execution_count": 78, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "(4168, 30, 15, 15)" + ] + }, + "metadata": {}, + "execution_count": 78 + } + ] + }, + { + "cell_type": "code", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "WIoAFbJQ1SZN", + "outputId": "b1ad25a7-6a54-40b7-cf28-0a5e5a6f8bb9" + }, + "source": [ + "y_train" + ], + "execution_count": 79, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "array([ 0., 12., 3., ..., 0., 5., 9.])" + ] + }, + "metadata": {}, + "execution_count": 79 + } + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "QYvfhO4a1Sc7" + }, + "source": [ + "class MyDataset(Dataset):\n", + " def __init__(self, data, target, transform=None):\n", + " self.data = torch.from_numpy(data).float()\n", + " self.target = torch.from_numpy(target).int()\n", + " self.transform = transform\n", + " \n", + " def __getitem__(self, index):\n", + " x = self.data[index]\n", + " y = self.target[index]\n", + " \n", + " if self.transform:\n", + " x = self.transform(x)\n", + " \n", + " return x, y\n", + " \n", + " def __len__(self):\n", + " return len(self.data)" + ], + "execution_count": 80, + "outputs": [] + }, + { + "cell_type": "code", + "metadata": { + "id": "pNLLxJ5-1Sgt" + }, + "source": [ + "data_train = MyDataset(X_train, y_train)" + ], + "execution_count": 81, + "outputs": [] + }, + { + "cell_type": "code", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "SokuQpZd1SnS", + "outputId": "e3c84f6b-5b31-45d7-d4e3-f3b200b41f1d" + }, + "source": [ + "input_shape= X_train[0].shape\n", + "print(input_shape)" + ], + "execution_count": 82, + "outputs": [ + { + "output_type": "stream", + "text": [ + "(30, 15, 15)\n" + ], + "name": "stdout" + } + ] + }, + { + "cell_type": "code", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "SS1OdXZ81e8D", + "outputId": "51518c03-877d-44e5-d84a-a4a3d65d56a5" + }, + "source": [ + "data_train.__getitem__(0)[0].shape" + ], + "execution_count": 83, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "torch.Size([30, 15, 15])" + ] + }, + "metadata": {}, + "execution_count": 83 + } + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "8voUJFUk1hGU" + }, + "source": [ + "n_epochs = 8\n", + "batch_size_train = 16\n", + "batch_size_test = 10\n", + "learning_rate = 0.01\n", + "momentum = 0.5\n", + "log_interval = 100\n", + "first_HL = 8" + ], + "execution_count": 84, + "outputs": [] + }, + { + "cell_type": "code", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "y--iqOXK1hKI", + "outputId": "c4db170c-2d60-4835-f138-69646cd4cd4e" + }, + "source": [ + "import torch\n", + "import torchvision\n", + "\n", + "## Call the Dataset Class \n", + "#data_train = torchvision.datasets.IndianPines('./data',download=True,PATCH_LENGTH=2)\n", + "\n", + "## Check the shapes\n", + "print(data_train.__getitem__(0)[0].shape)\n", + "print(data_train.__len__())\n", + "\n", + "\n", + "## Wrap it around a Torch Dataloader\n", + "train_loader = torch.utils.data.DataLoader(data_train,batch_size=16,shuffle=True, num_workers=2)" + ], + "execution_count": 85, + "outputs": [ + { + "output_type": "stream", + "text": [ + "torch.Size([30, 15, 15])\n", + "4168\n" + ], + "name": "stdout" + } + ] + }, + { + "cell_type": "code", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "el20l7Ny1hN0", + "outputId": "cb2c8148-096d-4119-b1ee-d6a40a54fc01" + }, + "source": [ + "len(data_train)" + ], + "execution_count": 86, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "4168" + ] + }, + "metadata": {}, + "execution_count": 86 + } + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "3XrPc66T1hU_" + }, + "source": [ + "data_test=MyDataset(X_test, y_test)" + ], + "execution_count": 87, + "outputs": [] + }, + { + "cell_type": "code", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "iFIxbEZM1uxr", + "outputId": "22252246-ed62-4f5d-9697-90581ed25149" + }, + "source": [ + "import torch\n", + "import torchvision\n", + "\n", + "## Call the Dataset Class \n", + "#data_test = torchvision.datasets.IndianPines('./data',download=True,PATCH_LENGTH=2)\n", + "\n", + "## Check the shapes\n", + "print(data_test.__getitem__(0)[0].shape)\n", + "print(data_test.__len__())" + ], + "execution_count": 88, + "outputs": [ + { + "output_type": "stream", + "text": [ + "torch.Size([30, 15, 15])\n", + "1043\n" + ], + "name": "stdout" + } + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "q3QXolwr1u05" + }, + "source": [ + "test_loader = torch.utils.data.DataLoader(data_test,batch_size=10,shuffle=False, num_workers=2)" + ], + "execution_count": 89, + "outputs": [] + }, + { + "cell_type": "code", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 568 + }, + "id": "GGroI38H1u3_", + "outputId": "532d0011-bf95-406c-bc42-ef2a329d68d1" + }, + "source": [ + "examples = enumerate(test_loader)\n", + "batch_idx, (example_data, example_targets) = next(examples)\n", + "\n", + "print(example_data.shape)\n", + "\n", + "import matplotlib.pyplot as plt\n", + "\n", + "fig = plt.figure()\n", + "for i in range(6):\n", + " plt.subplot(2,3,i+1)\n", + " plt.tight_layout()\n", + " plt.imshow(example_data[i][0], interpolation='none')\n", + " plt.title(\"Ground Truth: {}\".format(example_targets[i]))\n", + " plt.xticks([])\n", + " plt.yticks([])\n", + "fig" + ], + "execution_count": 90, + "outputs": [ + { + "output_type": "stream", + "text": [ + "torch.Size([10, 30, 15, 15])\n" + ], + "name": "stdout" + }, + { + "output_type": "execute_result", + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "execution_count": 90 + }, + { + "output_type": "display_data", + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAZUAAAELCAYAAAARNxsIAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4yLjIsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy+WH4yJAAAgAElEQVR4nO3de5Bc5Xnn8d8zI9SjGQkwl4QQJAHSjJDA3C+JHbtctSROKiGpsik2cTaYO1hCJBvHICeudeJyNqyTSmx2jSvxOuVsQrjE5iYWsCugCxcvBGOTmBikANIIMHfrMveZ7nf/6BZuVOd5pD7zzqhbfD9VqtKct99zznS/c57p7md+bSklAQCQQ9f+PgEAwIGDogIAyIaiAgDIhqICAMiGogIAyIaiAgDI5oAuKmZ2rJklM5uzH469xczOne3jIg/WDsp6t6+daRcVM/tNM3vMzIbN7LXG/1eameU4wZliZkNN/2pmNtr09W+3uK+vm9nnM5/fx8xsa+N+vdPMDsu5/3bA2sm/dszsV83sYTPbbmavmNn/NrMFufbfLlg7M7J2zMz+yMwGzWynmd1iZge3up9pFRUz+6SkL0n6c0lHSfppSVdJer+kuc6c7ukcM5eU0vzd/yQNSjqvadtNu2+3n37bOFHSX0v6HdXv0xFJN872ecwk1s6MOUTS5yUdLWm5pJ9V/T4+YLB2ZsyFql9z3q/6+pkn6X+2vJeUUql/qi/eYUkf3cvtvi7pK5Lubdz+XNUX+3pJ2yU9LenXm26/XtJlTV9fJOnhpq+T6gtoc2P+lyVZY6xb0l9IekPS85JWNW4/Zy/nuEXSuY3/f0jSi5Kuk/SKpL/f8xyazmOppCskTUqakDQkaW3TPv9A0r9K2iHpVkk9+3jf/ndJ/9j09ZLG/heUfbza6R9rZ+bWTsH5fUTSv+3vx5y10/5rR9I3JH2q6ev3SRqT1NvKYzSdZyo/L6ki6a59uO3HJP2ppAWSHpO0VtK3Jf2UpNWSbjKzZS0c+9cknSXpZEkXSPpwY/vljbHTJJ0p6fwW9tnsKEmHSVqs+oPnSin9jaSbJH0h1X/bOK9p+AJJvyzpuMa5XrR7oPHyxC84uz1R0lNNx3hO9cUz0PJ30p5YO5qxtbOnD6p+AT1QsHY0o2vH9vh/RVJ/C9/DtIrKEZLeSClNvX0GZo82TnrUzD7YdNu7UkqPpJRqkk6VNF/S9SmliZTSg5LukfRbLRz7+pTS9pTSoKR1jX1K9TvziymlbSmltyT9WcnvrSbpsyml8ZTSaMl9SNINKaWXG+eytuk8lVI6NKX0sDNvvuq/ZTTbofoPx4GAtbN3ZdfO28zsFyV9XNJ/m8Z5tBvWzt6VXTv3S7qs0WhwiOrPmiSpt5WDT6eovCnpiObX/lJK70spHdoYa973tqb/Hy1pW+OB3m2r6q/97qtXmv4/ovpieXvfe+y3jNdTSmMl5zbzznNvhiTt+QbZwZJ2ZTindsDa2buya0eSZGY/J+kfJZ2fUtqU4XzaBWtn78qunb+VdLPqLwU+rXrhlOovy+2z6RSV70gal/Qb+3Db5ijklyUtNLPmYy+S9FLj/8N6Z2U8qoVz+pGkhXvst4w9o5vfcU5mtuc55Y56flrSKU3HO171p6EHysWBtePfftrM7DRJd0u6JKX0QO7972esHf/205JSqqWUPptSOjaldIzq16GX9JP7aJ+ULioppe2S/kTSjWZ2vpktMLMuMztVUl8w9THVq+e1ZnaQmX1I0nmSbmmMf1/SR8ys18yWSrq0hdO6TdI1ZnaMmb1H0poWvy3PU5JONLNTzaxH0h/vMf6qpOMzHUuqv1Z6npl9wMz6JH1O0u0ppQPimQpr5x2yrh0zO0n1lzFWp5TW5tpvu2DtvEPutXOYmS1ptBavkPSXkj63x7O7vZpWS3FK6QuSfl/Stap/g6+q3gp7naRHnTkTqj+Yv6J6t8SNki5MKT3TuMlfqf6m9KuS/k71C+y++qqkb6n+YDwp6fbWvqNijZcPPifpn1Xv/tjzNcmvSVrReF33zn3ZZ6Mv/QPO8Z5WvdPkJkmvqf5eysqSp9+WWDtvy7p2JH1S0pGSvtb09w8H0hv1rJ2fyL12jtBPuuXuk/S3jYaAluxuiQMAYNoO6JgWAMDsoqgAALKhqAAAsqGoAACyoagAALJpKQlzrlVSj9MKXusvDAeVJC2b9+PC7U8PHeHOSdUgwTpqWEuZk68tOFj2Y7U+ZerNt1QdGm7ruO/2WTcl11QZM7FuSk3zz2Ni8KU3UkpHljuZ2THXKqnHnLWz1F87J8zbXrj9B0OHu3PCtROpzeJ1oOw69fYZ7S84j4nBF92101JR6VGfzukq/vyX0S8f685bd9I3C7cvf+gid87kkL9gNOU/wbLxYMy5A1O3f89GY1a28HnTosXkjP3o+i8Fk9pDj/p0TvcvFY4N/a/F7ryN7/1G4fblGy9250wOH+SO2biffm4TwZ3vDKWuYN3MDdbN5OwVlWj9Dl51bdk4kVnTY336uTkfLhwb+fLCwu2StPG9dxRuH9h4oTtncmeltZNr6BoJUvVLXHMU7K7s2klzio9nU/7+vDmStHXVp9y1w8tfAIBsKCoAgGwoKgCAbCgqAIBsWnqjfnxRnzavObtwLL3gv6nT/+wnWjsrSVa2AyJ4c8mNOQvecJ8z6tfdqYOr7ljXmD+va6z4eFOHTxVulyTzzqMDotvGF/Vq06fPKB7c4s/r3+ysm9bfU9+rWqWlINb6sYI3ObuH/ce/2ucfK2o06XKWR3WBvw5torN/bxxf2KdNa5y18x/+vKU/uKpwe63i/8B0BY+nguURvaHtidZO16g/NnVIcM0Z8R/rOUPFY5PR/oK1GOnsFQcAaCsUFQBANhQVAEA2FBUAQDYUFQBANhQVAEA2LbUUVwaH1b/6icKxiW8d485bd+JdhduXPeTn8ExE2V9BC7AFrbxukF+QwzN1cND+GeTwRJmB1fnF+4zO3W1bjIIL20RlcEQDKx8vHNt53xJ33saTbyvcvmLDpe6cqSD7S8HjVaZ9Mh3k3/fVeeXWTfR41pxoqqhtOMon6wSVwWH1X128dkbvP9ad52Z/bfi4O2dyZ3DNCYTZX44UTKnO9ddO9KcKUT/9VK9zzYnWTolWaYlnKgCAjCgqAIBsKCoAgGwoKgCAbCgqAIBsWgyUDIIBn/PnHf/vxeFuUddVGAwYZf9FjRhOomQYDBiEtEWBkjYe7NMJd5t6TxAo6XUn5f5I4xkwvqhXm/6wOIhUwWcPLtu8snggap6Kfk2KQgGDT2p0QzuDLsTu4PEPAyWDT6D01uLUoSXWTYcYX9Snzdc5IbbP+Y/ZEueaE36Sa/CzZP6Pev5AyeAxmwrCQ7uC8Ft37UShuCXDSDt7xQEA2gpFBQCQDUUFAJANRQUAkA1FBQCQDUUFAJBNi4GSIxq4+ruFY9kDJXcF4W61coGS5syLWgKngnC3qP0zUi0R7ua2Srd/R3F93awqXjdD9y5252187zcKty/feLE7ZzIKlAzu31KBksG6qfYGrathoKQ/5IVUhqGAQQttJ6iH2BYHSg7fd5w7b4Ozdk7YcIk7Z2qXv3ZS1D4etPK6+4sCJYOW87KBktV5xesgahsuu3Z4pgIAyIaiAgDIhqICAMiGogIAyIaiAgDIhqICAMimxZTiPm1ec2bhWJQYevwPryweiDoro1bZoL1PQZun93ndYUpx0HIXJnyOtd6COHlYkDbrtRJ2QMdomG69xZ/Xv/kTxQPlOnLD+ypMKfYEybXdw/66qQZJszbm95p6yccHfErxGifhOkhGH/hhccJ11CbbFf2pQvBY11q6ijb2FyRmd+8KrjmHlEwpHnXWDinFAIB2RlEBAGRDUQEAZENRAQBkQ1EBAGRDUQEAZNNiSvGw+lc/UTg2fv9Cd976k+4s3B6mFO+s+CcSdH9GKcXu7qKU4kqQUhykzaagt3Vqfom0We8crf17iivbRrXsvz5VODZy99HuvI3vvaNw+8BGf91MDgXp1lFKcYn2yag9NUqajdOog306SbOl1k2HqAwOq//q/Z9SHCWjR628XutwCq68pVOKgyFSigEAHYmiAgDIhqICAMiGogIAyIaiAgDIhqICAMgmX0rx81FK8VXFA05qsDSNlOIybXDB/uaM5E8p7hoq3ufU4UHarNe2GPUut4nxhfP07B+eUjz4gj/v+GeddOvgV6GyKcW1uUFsrHesYN10B+smbjcO1o0zFqYeT3b2743ji/q0+boSKcX/7qQUBy3WFvwsRanC0T69kTAZPUiWDq85UUqxs3ZIKQYAtDWKCgAgG4oKACAbigoAIBuKCgAgm1kJlHzgxNsLty9/6CJ3ThgMGHTdRJ/x7WUvhoGSC8p16qSgs626wAkGDLo+OjpQcnBEA6u+Wzg2fO9id54XCrh848XunMnhIBRwKgiUDIL63KagKPyxt9y6idR6nHUTfF/ROuwElcFh9V9TfM3JvnZ2+tecFHSGdQ+XWDtR+GO7BEqWDCPlmQoAIBuKCgAgG4oKACAbigoAIBuKCgAgG4oKACCbFgMle7Xp02cUDwbhbv3PfKJ4IOisDAMlo+y/KNzNG5qBQMmoPbh7lxPudlgQKOm1EnZCoGQURLrFf7z6NznrJgqULLluokBJN2gwDAX0x8JAyWDddI06gZKHllg3HaIeKFm8dtolULIWXUWdlm6bDNaOEzgrSVOHBgGQwbWKQEkAQEeiqAAAsqGoAACyoagAALKhqAAAsqGoAACyaTGleEQDKx8vHBv/9rHuvPUn3Vm4fWDjhe6cmUgpdgVps1Pzg/bPoC0w4rWUhp8n7n1b7d9RXE+aXfVY4djO+5a48zaefFvh9uUbLnXnVIOU4rBdNxjzpIOClOK5QetqtG6CVGE3pThKmg3WdieoJ6MXX3OG7jvOnbdxFlOKo1Ze91oVTQmS0aPPoQ/36aRmk1IMAGhrFBUAQDYUFQBANhQVAEA2FBUAQDYUFQBANi2mFPdp85pzCsfSc3772fHPXNnaWUmyqNwFLcVRSrGXUmtB2mxX0OZbXRAkfI4FKaTDxfucLJVS7E5pG+G62ep/A8s2FyfNhunW0bqJlkYlaB2vOQcM1uGMpBQ7SbPVQ4J1U6JVup3UU4rPLh7MnFLc5T3OUrh2Uom/YjD/0hGnFEepwkEidbfT9hwmrUd/4hDo7BUHAGgrFBUAQDYUFQBANhQVAEA2FBUAQDYUFQBANi2mFPtps6Pf8hND1530zcLtYWJo2ZTioIXSnLbAKMm1Njdo/3RaPCUpBWmzU/NbT5s9UFOKd9y71J234ZSbC7efuOFyd06YUhwkvEbtmN6vXtG6qfaWTCn2FqmClOKo9bPDf22sDA6r/+rWE64fPvmfCrcv23CJO6e6K1g7U8HaGY2uA94kd4qqQTJ6uE4D1XnFa4eUYgBAW6OoAACyoagAALKhqAAAsqGoAACyyRco+YLfKdD/7CeKB4KSFjY1+c0R4efNJ2coCpTsDjqGpoIgv64RP2Wue6T4eJOHB8GA3nl0RKBkrzZ92gkFHPTnLf+Pq4sHggC/cN0E663WE3T5eZ9PHoUCBp9bHgZKBh2F3lqcOvQADpRc2KfN1xVfc/S8P2/gGSdQMrg+hIGSwTWnNjdKmyzeZxgo6QTOStLU/JKBkk6HWhhQGXWjBjp7xQEA2gpFBQCQDUUFAJANRQUAkA1FBQCQDUUFAJBN64GSq58oHBu/f6E7b/1JdxZuH9h4oTtnclfFP5GSgZKedFAQ/hi1mkZhbMFpTDmBcdG5u+FuQQBhu6gMjmhg5eOFY1Gg5EOn3Fq4ffmGS905YaBk0HIZBvU5yy0K3Kv2lmsbjnj7jAIloxbaTlDZNqz+1a0HSm48+bbC7SuCtTO10w+xjf7soGus9ccz+lz7aO10Rde3KKTS2WcYKBmE4kZ4pgIAyIaiAgDIhqICAMiGogIAyIaiAgDIhqICAMjGkhfdW3Rjs9clbZ2500EJi1NKR+7vk4iwbtoWawdluWunpaICAECEl78AANlQVAAA2VBUAADZUFQAANlQVAAA2VBUAADZUFQAANlQVAAA2VBUAADZUFQAANlQVAAA2VBUAADZUFQAANkc0EXFzI41s2Rmc/bDsbeY2bmzfVyUw1pBLu/2tTTtomJmv2lmj5nZsJm91vj/SjOzHCc4U8xsqOlfzcxGm77+7Rb39XUz+3zm81ttZi+Y2U4ze8LMfiHn/vcH1kr+tWJmP2Nmd5vZy40L2bF7jF9gZo+a2YiZrc913P2NtdS+151pFRUz+6SkL0n6c0lHSfppSVdJer+kuc6c7ukcM5eU0vzd/yQNSjqvadtNu2+3n37bOEfS9ZLOl3SIpK9JuqNd7rsyWCszpibpfkkfdcbfkvRF1dfTAYG1NDOyXXdSSqX+NQ46LOmje7nd1yV9RdK9jdufK2m5pPWStkt6WtKvN91+vaTLmr6+SNLDTV8n1RfQ5sb8L+snHzbWLekvJL0h6XlJqxq3n7OXc9wi6dzG/z8k6UVJ10l6RdLf73kOTeexVNIVkiYlTUgakrS2aZ9/IOlfJe2QdKuknn28b/+zpMebvu5rHO9nyj5e+/Mfa2Xm1krTMeY0jnOsM36ZpPX7ey2wltp3LSnTdWc6z1R+XlJF0l37cNuPSfpTSQskPSZpraRvS/opSasl3WRmy1o49q9JOkvSyZIukPThxvbLG2OnSTpT9YpbxlGSDpO0WPUHz5VS+htJN0n6Qqr/tnFe0/AFkn5Z0nGNc71o94CZbQ+eWt4nqdvMzmn8lnCJpO+rvtg6EWtFM7ZW3m1YS2rv6850isoRkt5IKU3t3tB47XZ743XCDzbd9q6U0iMppZqkUyXNl3R9SmkipfSgpHsk/VYLx74+pbQ9pTQoaV1jn1L9zvxiSmlbSuktSX9W8nurSfpsSmk8pTRach+SdENK6eXGuaxtOk+llA5NKT3szNsl6ZuSHpY0Lumzkq5IjV8fOhBrZe/KrpV3G9bS3u3X6850isqbko5ofu0vpfS+lNKhjbHmfW9r+v/RkrY1Hujdtkr62RaO3Vw5R1RfLG/ve4/9lvF6Smms5Nxm3nnuzaWSLpZ0ouqvEf8XSfeY2dEZzml/YK3sXdm18m7DWtq7/XrdmU5R+Y7q1ew39uG2zZXuZUkLzaz52IskvdT4/7Ck3qaxo1o4px9JWrjHfsvYszK/45zMbM9zyv0M4lRJ96SUNqWUaiml+1X/3t6X+TizhbXi3x6tYS35t5+uLNed0kUlpbRd0p9IutHMzjezBWbWZWanqv4Gj+cx1avntWZ2kJl9SNJ5km5pjH9f0kfMrNfMlqpePffVbZKuMbNjzOw9kta0+G15npJ0opmdamY9kv54j/FXJR2f6ViS9C+SftXMjre6X5Q0IOkHGY8xa1gr75B7rahxnErjy0rj691j3Y2v50jqMrMeMzso5/FnE2vpHdryujOtluKU0hck/b6ka1X/Bl+V9NeqdzA86syZUP3B/BXVuyVulHRhSumZxk3+SvWOhlcl/Z3qb0btq69K+pbqD8aTkm5v7TsqllLaJOlzkv5Z9e6PPV+T/JqkFY3Xde/cl302+tI/4Az/H9UX+3pJOyXdIOnKpvuo47BW3pZ7rUjSqOodQJL0TOPr3X6n8fVXJH2g8f+v7stx2xVr6W1ted3Z3RIHAMC0HdAxLQCA2UVRAQBkQ1EBAGRDUQEAZENRAQBk01IS5lyrpB6nFbzaXyncLkknzHurcPsPdh3hH6wWJFhHY2Wa2aKw7GhsNhvnnPOYevMtVYeG2zruO1o3k0t6CrdL0ol9xevm33Yd7h8s+XeFTQR3U7SkvLHoXu8OFkfNHyq13ko++hNbX3ojpXRkudmzg2tOhmOVFZzHxOCL7tppqaj0qE/n2H8qHNtxw1J33iOn3Fa4fdmGS9w51V3+32d1jfpJzDYZXTiKH5EUBDunuf6jGF2kUnRR8fYXLNw0p3h/P/ofX2r5OLOtR306p6v4c4Ne+csT3HmPn31z4fal6y5251Qn/CffPVv9i1C14j9e3pj3mEhSml91xzQWvEBQCSrOuDPvoHIFbPCK68rGicyaHvXpnO5fKhzbccNx7ryHTrm1cPvyDf7fNEbXHBvzLxJdk+6Qe2FOwRIofc2JXndyrn3RL2HRL0ZbV33KXTu8/AUAyIaiAgDIhqICAMiGogIAyKalN+rHF/Vp83XnFI6lLf6bOgObVhbP6QrekIreQArU5gXvTHqHC94g7xrzx6rBsbqCN4y9N09T8Gax+4ZrB0S3jS/u1abPnFk8+Jb/DRy39vLWDxa8eT62eNyfF603b5fB+g33V/JXua6Di98Rro0GP8Zzo1az9je+qE+b1xSvnbTVv/9PeHZV8ZxgfXjvZe9NrafExGrQpTjuj9V6/MfTpoJ9OmOlrjl7wTMVAEA2FBUAQDYUFQBANhQVAEA2FBUAQDYUFQBANi21FFcGh9V/9WOFYz/+v/3uvEdOvaVwe+kcnhE/h6d7xK+TXgtzlP1VC1ruurwsJu0l+8ubFrQElu53bAOVrSMauPKJwrHX7lzmzvveWcXrZskDfvZXbcp/TCqDfvZX9DjXnDbUWpDRlPr87C8L2klT8BOZdswtHjgo6P2MWts7QGVwWP2riq85r9/tr53vnPEPhdtXrLvCnVMb8q85c3b692OY2edlfwWtzVGLctlrjnuNm4FrTmevOABAW6GoAACyoagAALKhqAAAsqGoAACyyRcoOVgi3C34xLqwoyIohVHIoys4VvRJktH5h4Fx3oewRR1jndv8FQdKvhkESt7jBEpG90XQVTO+qGSgpKdkd0ypzkBJXYdOFG6vjvg/xtbpgZILg2vOS/79uOK51cVzgvs+CrGNPhk0DBb1dhk8LKWvOeHHVDvzyn6scYBnKgCAbCgqAIBsKCoAgGwoKgCAbCgqAIBsKCoAgGyyBUq+dc+AO+/R024u3L58/WXunChQsmvUr4VdY60HrkUtytFnONtEubZnL9gyDBqM2lDbXGXriAauerJw7LU7/CBSN1DyQT9QMkWBklv8QMmoZdQbi4JI04Ipdyx8nKOO0e2tB0qmIICwE1S2Dav/d4vDSN+4a4k779HTbyrcvuLBIFByOAqU9B/sMn/+EP08R38WEV7foj/RCNacu7+S15zOXnEAgLZCUQEAZENRAQBkQ1EBAGRDUQEAZENRAQBkky+leFuQUrzZSSkOEmWjANgwpbivTEqxPxS2DQefUR599rPX3hfdH25iaMkk0dlUTyk+vXjwLf/OP26tl1IcfNNBe+3Y4g5IKY4+MvyQ4pTiNHoApxQv6tPm64oTrtOLQUrxZielOLrmBOcRphRHP7feUBR6PBG0DUfnUQ1Ow0tSJqUYANDOKCoAgGwoKgCAbCgqAIBsKCoAgGwoKgCAbLKlFO+4d6k775FTbivcvmzDJe6cKKXYxvzE0O6RoB3PaasrnVI8GaWT+vOSd68Haafq5JTiwREtW/m9wrHXbveTZp8889bC7UvX+SnF1aAdszLopxTXgse55qS/1oKW8tQX9HeW7PKteT8TwdpIwc9KJ6hsG9bA7323cGz73ce680pdc4aCZPQR/37sGg6uOc6PdNTaHP2pQvgnDmVam6NrTrS/AM9UAADZUFQAANlQVAAA2VBUAADZUFQAANlQVAAA2eRLKd7it58NPLOyeE6YUlwuIrM6r0xKsX+srjF/LDqWTfr12qaKt5dKIO2ATuPxRb169jOnFQ++OXspxeOLZjGlONpfyV/luuZPFm6vjQdtw8H90QnGF/Zp05ozCsfCa86zzjUnaL8uG/gdXnO8nZZMRq9VSl5znG+7VBvyXvBMBQCQDUUFAJANRQUAkA1FBQCQDUUFAJBN64GS1zxRODZ872J33sMn3164fWDDx905k7vmumNRl0PUreVJQfNMrcdvgQg/S7pMoGTwufYK9tfuKltHNHDVk4Vjr9w+4M576uybC7cvecAPlEzVIFByix8oWQ0e51KBkr0lAyWDh9kNlHTOT5IUdBJ1gsq2EQ38XnEY6dDaY9x5ua85Gg+uOePBfewFSkbXnJIdXlE3YvJOpJo/xJZnKgCAbCgqAIBsKCoAgGwoKgCAbCgqAIBsKCoAgGxaD5Rcc2bhWHrBbz9b8sOriueEgZL+eVgQABm1ebr7C9rquoLPvK/O99tGu4IWRC9QsjavRKBkBxhf3KtNf3R68eBb/vecO1BybPHsBUpasLsU/SoXdZUf7ARKjvo/xha0p3aC8YW92rSmOIw0+zUnuK5E16Pozw7cxzP4ee4eDa45ZUNsneOFIbYllw7PVAAA2VBUAADZUFQAANlQVAAA2VBUAADZUFQAANm0nlK8ev+nFCtqnYsSQx1RYmjqDVr4opTiqKXUa0GM2oa9Q3VACG1l64gGrvyXwrGX71jhzvv+2f9QuL3/gcvcOWnKf0x6N/spxZPzg5Rip03dTZuWVFvg9I1LsiCNOjlBxJKUdjg/E0EbdRoLFncHqGwb1sDvfrdwbMfa6JrzT4Xbl224xJ1T9VKgpTilOGgB9tLFo7byqG04TEYPUoW965FNRn+eUa6nmGcqAIBsKCoAgGwoKgCAbCgqAIBsKCoAgGwoKgCAbFpMKe7Vpk+fUTz4vD/PTQw9KEgMDZKDo8TQVCalOGjxtFF/rNoXtP5FKcVjxfuM9ue2/rX+7c668cW92vSZs4oHf+x/A0vvubL1gwVtlSP9QUpxkFDrtW2b0y4qSRYs0tQd/C4XnEb3e4rPfypohbWeDo63ljS+sE+b1rR+zRl4ZmXh9jClODqR4G8EakELcJmfz+jaEV7fouuYc4rR/qJrcIRnKgCAbCgqAIBsKCoAgGwoKgCAbCgqAIBsKCoAgGxaTCke0cDKxwvHdt63xJ3nJYaeECSGRm2SUUpxV+aU4mqQUhy2/kUpxU4bX7g/rxWyU1KKr3qycOy1O/rded8765bC7UsevNidUwvWRs9WP6W46iVHS6o5932tErQN9/qtvBa0L6egY7T6Y+f8gzTZFKypTlAZHFb/qscKx16/e5k77ztnFCdcr1h3hTunNuxfDqExx6gAAAH9SURBVOfsiCKpgx9C5+6PEoVrwVq0iWDtBO3S7gUpaEMO/3Yj0NkrDgDQVigqAIBsKCoAgGwoKgCAbCgqAIBsWgyU7NPm684pHEsv+J0CpcLdgo6KqCkh6shxw92CY3UHnz9dtjPMC4eM9ueGXnZMoOTpxYNv+t/AcfdcXjwQddsEnVBji4NAyYj7Ad8lum0Ud/5E3Xx2yETx/kb9H2OrlPuc8XYRXnNe8u/HFc+tLp4TXXOizrue4H6MfjUv8fMZfW58FMIbHctdjkEoalk8UwEAZENRAQBkQ1EBAGRDUQEAZENRAQBkQ1EBAGTTYqDksPqveaJwbOjexe68R06+vXB7//qL3DlTQ0GgZNCu2+V8/rskt10zBaW1Gnz+dOlASaft2YIwRBEo+bbSgZJbgkDJeUHAn9PG6W2XpDQ/+Gz4qMs3apff6fxMROcxFqSldoDK4LD6VxeH2L5xt792/t/pNxduP2HdZe6cKFCye6c/1hU81N61pXSgZBCYW6pdOvgc+rD1PcAzFQBANhQVAEA2FBUAQDYUFQBANhQVAEA2FBUAQDaWog/F3vPGZq9L2jpzp4MSFqeUjtzfJxFh3bQt1g7KctdOS0UFAIAIL38BALKhqAAAsqGoAACyoagAALKhqAAAsqGoAACyoagAALKhqAAAsqGoAACy+f/vdywTD1nJSwAAAABJRU5ErkJggg==\n", + "text/plain": [ + "
" + ] + }, + "metadata": {} + } + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "oVSa9KkH1u7E" + }, + "source": [ + "import torch\n", + "import torch.nn as nn\n", + "import torchvision\n", + "import torchvision.transforms as transforms\n", + "\n", + "import random" + ], + "execution_count": 91, + "outputs": [] + }, + { + "cell_type": "code", + "metadata": { + "id": "nNd9tITQ1u-F" + }, + "source": [ + "device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\n", + "\n", + "# Hyper-parameters\n", + "num_epochs = 16\n", + "learning_rate = 0.001\n", + "\n", + "torch.manual_seed(0)\n", + "random.seed(0)" + ], + "execution_count": 111, + "outputs": [] + }, + { + "cell_type": "code", + "metadata": { + "id": "ait2YKMs1vEZ" + }, + "source": [ + "Half_width =60\n", + "layer_width = 20" + ], + "execution_count": 112, + "outputs": [] + }, + { + "cell_type": "code", + "metadata": { + "id": "JXPg_kE-1vHB" + }, + "source": [ + "class SpinalCNN(nn.Module):\n", + " \"\"\"CNN.\"\"\"\n", + "\n", + " def __init__(self):\n", + " \"\"\"CNN Builder.\"\"\"\n", + " super(SpinalCNN, self).__init__()\n", + "\n", + " self.conv_layer = nn.Sequential(\n", + "\n", + " # Conv Layer block 1\n", + " nn.Conv2d(in_channels=30, out_channels=15, kernel_size=3, padding=1),\n", + " nn.BatchNorm2d(15),\n", + " nn.ReLU(inplace=True),\n", + " nn.Conv2d(in_channels=15, out_channels=30, kernel_size=3, padding=1),\n", + " nn.ReLU(inplace=True),\n", + " nn.MaxPool2d(kernel_size=2, stride=2),\n", + "\n", + " # Conv Layer block 2\n", + " nn.Conv2d(in_channels=30, out_channels=60, kernel_size=3, padding=1),\n", + " nn.BatchNorm2d(60),\n", + " nn.ReLU(inplace=True),\n", + " nn.Conv2d(in_channels=60, out_channels=60, kernel_size=3, padding=1),\n", + " nn.ReLU(inplace=True),\n", + " nn.MaxPool2d(kernel_size=2, stride=2),\n", + " nn.Dropout2d(p=0.05),\n", + "\n", + " # Conv Layer block 3\n", + " nn.Conv2d(in_channels=60, out_channels=120, kernel_size=3, padding=1),\n", + " nn.BatchNorm2d(120),\n", + " nn.ReLU(inplace=True),\n", + " nn.Conv2d(in_channels=120, out_channels=120, kernel_size=3, padding=1),\n", + " nn.ReLU(inplace=True),\n", + " nn.MaxPool2d(kernel_size=2, stride=2),\n", + " )\n", + " \n", + " self.fc_spinal_layer1 = nn.Sequential(\n", + " nn.Dropout(p=0.1), nn.Linear(Half_width, layer_width),\n", + " nn.ReLU(inplace=True),\n", + " )\n", + " self.fc_spinal_layer2 = nn.Sequential(\n", + " nn.Dropout(p=0.1), nn.Linear(Half_width + layer_width, layer_width),\n", + " nn.ReLU(inplace=True),\n", + " )\n", + " self.fc_spinal_layer3 = nn.Sequential(\n", + " nn.Dropout(p=0.1), nn.Linear(Half_width + layer_width, layer_width),\n", + " nn.ReLU(inplace=True),\n", + " )\n", + " self.fc_spinal_layer4 = nn.Sequential(\n", + " nn.Dropout(p=0.1), nn.Linear(Half_width + layer_width, layer_width),\n", + " nn.ReLU(inplace=True),\n", + " )\n", + " self.fc_out = nn.Sequential(\n", + " nn.Dropout(p=0.1), nn.Linear(layer_width*4, 16) \n", + " )\n", + "\n", + "\n", + " def forward(self, x):\n", + " \"\"\"Perform forward.\"\"\"\n", + " \n", + " # conv layers\n", + " x = self.conv_layer(x)\n", + " \n", + " # flatten\n", + " x = x.view(x.size(0), -1)\n", + " \n", + " x1 = self.fc_spinal_layer1(x[:, 0:Half_width])\n", + " x2 = self.fc_spinal_layer2(torch.cat([ x[:,Half_width:2*Half_width], x1], dim=1))\n", + " x3 = self.fc_spinal_layer3(torch.cat([ x[:,0:Half_width], x2], dim=1))\n", + " x4 = self.fc_spinal_layer4(torch.cat([ x[:,Half_width:2*Half_width], x3], dim=1))\n", + " \n", + " x = torch.cat([x1, x2], dim=1)\n", + " x = torch.cat([x, x3], dim=1)\n", + " x = torch.cat([x, x4], dim=1)\n", + "\n", + " x = self.fc_out(x)\n", + "\n", + " return x" + ], + "execution_count": 113, + "outputs": [] + }, + { + "cell_type": "code", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "WQzxIEJp2LKl", + "outputId": "dff13971-7d7e-460d-8679-7eb412d50632" + }, + "source": [ + "from tensorflow.keras.optimizers import Adam\n", + "model = SpinalCNN().to(device)\n", + "# defining the optimizer\n", + "optimizer = Adam(model.parameters(), lr=0.07)\n", + "# defining the loss function\n", + "criterion = nn.CrossEntropyLoss()\n", + "# checking if GPU is available\n", + "if torch.cuda.is_available():\n", + " model = model.cuda()\n", + " criterion = criterion.cuda()\n", + " \n", + "print(model)" + ], + "execution_count": 114, + "outputs": [ + { + "output_type": "stream", + "text": [ + "SpinalCNN(\n", + " (conv_layer): Sequential(\n", + " (0): Conv2d(30, 15, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n", + " (1): BatchNorm2d(15, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (2): ReLU(inplace=True)\n", + " (3): Conv2d(15, 30, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n", + " (4): ReLU(inplace=True)\n", + " (5): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)\n", + " (6): Conv2d(30, 60, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n", + " (7): BatchNorm2d(60, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (8): ReLU(inplace=True)\n", + " (9): Conv2d(60, 60, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n", + " (10): ReLU(inplace=True)\n", + " (11): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)\n", + " (12): Dropout2d(p=0.05, inplace=False)\n", + " (13): Conv2d(60, 120, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n", + " (14): BatchNorm2d(120, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n", + " (15): ReLU(inplace=True)\n", + " (16): Conv2d(120, 120, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n", + " (17): ReLU(inplace=True)\n", + " (18): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)\n", + " )\n", + " (fc_spinal_layer1): Sequential(\n", + " (0): Dropout(p=0.1, inplace=False)\n", + " (1): Linear(in_features=60, out_features=20, bias=True)\n", + " (2): ReLU(inplace=True)\n", + " )\n", + " (fc_spinal_layer2): Sequential(\n", + " (0): Dropout(p=0.1, inplace=False)\n", + " (1): Linear(in_features=80, out_features=20, bias=True)\n", + " (2): ReLU(inplace=True)\n", + " )\n", + " (fc_spinal_layer3): Sequential(\n", + " (0): Dropout(p=0.1, inplace=False)\n", + " (1): Linear(in_features=80, out_features=20, bias=True)\n", + " (2): ReLU(inplace=True)\n", + " )\n", + " (fc_spinal_layer4): Sequential(\n", + " (0): Dropout(p=0.1, inplace=False)\n", + " (1): Linear(in_features=80, out_features=20, bias=True)\n", + " (2): ReLU(inplace=True)\n", + " )\n", + " (fc_out): Sequential(\n", + " (0): Dropout(p=0.1, inplace=False)\n", + " (1): Linear(in_features=80, out_features=16, bias=True)\n", + " )\n", + ")\n" + ], + "name": "stdout" + }, + { + "output_type": "stream", + "text": [ + "/usr/local/lib/python3.7/dist-packages/keras/optimizer_v2/optimizer_v2.py:356: UserWarning: The `lr` argument is deprecated, use `learning_rate` instead.\n", + " \"The `lr` argument is deprecated, use `learning_rate` instead.\")\n" + ], + "name": "stderr" + } + ] + }, + { + "cell_type": "code", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "Bajf_H5MII9l", + "outputId": "8235c2cc-1963-433c-84e3-502a13342051" + }, + "source": [ + "from torchvision import models\n", + "from torchsummary import summary\n", + "model = SpinalCNN().to(device)\n", + "summary(model, (30, 15, 15))" + ], + "execution_count": 115, + "outputs": [ + { + "output_type": "stream", + "text": [ + "----------------------------------------------------------------\n", + " Layer (type) Output Shape Param #\n", + "================================================================\n", + " Conv2d-1 [-1, 15, 15, 15] 4,065\n", + " BatchNorm2d-2 [-1, 15, 15, 15] 30\n", + " ReLU-3 [-1, 15, 15, 15] 0\n", + " Conv2d-4 [-1, 30, 15, 15] 4,080\n", + " ReLU-5 [-1, 30, 15, 15] 0\n", + " MaxPool2d-6 [-1, 30, 7, 7] 0\n", + " Conv2d-7 [-1, 60, 7, 7] 16,260\n", + " BatchNorm2d-8 [-1, 60, 7, 7] 120\n", + " ReLU-9 [-1, 60, 7, 7] 0\n", + " Conv2d-10 [-1, 60, 7, 7] 32,460\n", + " ReLU-11 [-1, 60, 7, 7] 0\n", + " MaxPool2d-12 [-1, 60, 3, 3] 0\n", + " Dropout2d-13 [-1, 60, 3, 3] 0\n", + " Conv2d-14 [-1, 120, 3, 3] 64,920\n", + " BatchNorm2d-15 [-1, 120, 3, 3] 240\n", + " ReLU-16 [-1, 120, 3, 3] 0\n", + " Conv2d-17 [-1, 120, 3, 3] 129,720\n", + " ReLU-18 [-1, 120, 3, 3] 0\n", + " MaxPool2d-19 [-1, 120, 1, 1] 0\n", + " Dropout-20 [-1, 60] 0\n", + " Linear-21 [-1, 20] 1,220\n", + " ReLU-22 [-1, 20] 0\n", + " Dropout-23 [-1, 80] 0\n", + " Linear-24 [-1, 20] 1,620\n", + " ReLU-25 [-1, 20] 0\n", + " Dropout-26 [-1, 80] 0\n", + " Linear-27 [-1, 20] 1,620\n", + " ReLU-28 [-1, 20] 0\n", + " Dropout-29 [-1, 80] 0\n", + " Linear-30 [-1, 20] 1,620\n", + " ReLU-31 [-1, 20] 0\n", + " Dropout-32 [-1, 80] 0\n", + " Linear-33 [-1, 16] 1,296\n", + "================================================================\n", + "Total params: 259,271\n", + "Trainable params: 259,271\n", + "Non-trainable params: 0\n", + "----------------------------------------------------------------\n", + "Input size (MB): 0.03\n", + "Forward/backward pass size (MB): 0.36\n", + "Params size (MB): 0.99\n", + "Estimated Total Size (MB): 1.37\n", + "----------------------------------------------------------------\n" + ], + "name": "stdout" + } + ] + }, + { + "cell_type": "code", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "SXlcqkeD2OhL", + "outputId": "2c08ecdb-04fd-4485-93a7-5ff1cd99ee9e" + }, + "source": [ + "model = SpinalCNN().to(device)\n", + "\n", + "\n", + "\n", + "# Loss and optimizer\n", + "criterion = nn.CrossEntropyLoss()\n", + "optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate)\n", + "\n", + "# For updating learning rate\n", + "def update_lr(optimizer, lr): \n", + " for param_group in optimizer.param_groups:\n", + " param_group['lr'] = lr\n", + "\n", + "# Train the model\n", + "total_step = len(train_loader)\n", + "curr_lr = learning_rate\n", + "for epoch in range(num_epochs):\n", + " for i, (images, labels) in enumerate(train_loader):\n", + " images = images.to(device)\n", + " labels = labels.to(device)\n", + " \n", + " #print(images.shape)\n", + " # Forward pass\n", + " outputs = model(images)\n", + " labels = torch.tensor(labels, dtype=torch.long, device=device)\n", + " loss = criterion(outputs, labels)\n", + "\n", + " # Backward and optimize\n", + " optimizer.zero_grad()\n", + " loss.backward()\n", + " optimizer.step()\n", + "\n", + "\n", + " if (i+1) % 500 == 0:\n", + " print(\"Epoch [{}/{}], Step [{}/{}] Loss: {:.4f}\"\n", + " .format(epoch+1, num_epochs, i+1, total_step, loss.item()))\n", + " \n", + "\n", + " # Decay learning rate\n", + " if (epoch) == 1 or epoch>20:\n", + " curr_lr /= 3\n", + " update_lr(optimizer, curr_lr)\n", + " \n", + " # Test the model\n", + " model.eval()\n", + " with torch.no_grad():\n", + " correct = 0\n", + " total = 0\n", + " predicted_numpy=[]\n", + " for images, labels in test_loader:\n", + "\n", + " images = images.to(device)\n", + " labels = labels.to(device)\n", + " outputs = model(images)\n", + " _, predicted = torch.max(outputs.data, 1)\n", + " predicted_numpy.append(predicted.cpu().numpy())\n", + "\n", + " total += labels.size(0)\n", + " correct += (predicted == labels).sum().item()\n", + " \n", + " print('Accuracy of the model on the test images: {} %'.format(100 * correct / total))\n", + " \n", + " model.train()" + ], + "execution_count": 116, + "outputs": [ + { + "output_type": "stream", + "text": [ + "/usr/local/lib/python3.7/dist-packages/ipykernel_launcher.py:25: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n" + ], + "name": "stderr" + }, + { + "output_type": "stream", + "text": [ + "Accuracy of the model on the test images: 65.29242569511025 %\n", + "Accuracy of the model on the test images: 74.97603068072867 %\n", + "Accuracy of the model on the test images: 85.0431447746884 %\n", + "Accuracy of the model on the test images: 74.59252157238734 %\n", + "Accuracy of the model on the test images: 88.68648130393098 %\n", + "Accuracy of the model on the test images: 87.91946308724832 %\n", + "Accuracy of the model on the test images: 90.12464046021093 %\n", + "Accuracy of the model on the test images: 88.20709491850431 %\n", + "Accuracy of the model on the test images: 89.83700862895493 %\n", + "Accuracy of the model on the test images: 89.74113135186961 %\n", + "Accuracy of the model on the test images: 91.46692233940556 %\n", + "Accuracy of the model on the test images: 89.26174496644295 %\n", + "Accuracy of the model on the test images: 93.28859060402685 %\n", + "Accuracy of the model on the test images: 93.7679769894535 %\n", + "Accuracy of the model on the test images: 95.11025886864813 %\n", + "Accuracy of the model on the test images: 95.39789069990412 %\n" + ], + "name": "stdout" + } + ] + }, + { + "cell_type": "code", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "An_Q8lsCIwND", + "outputId": "056998bd-4f87-40be-c432-32a08f327793" + }, + "source": [ + "len(predicted_numpy)" + ], + "execution_count": 117, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "105" + ] + }, + "metadata": {}, + "execution_count": 117 + } + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "euZAgKaHIxCz" + }, + "source": [ + "predicted_numpy = np.concatenate(predicted_numpy)" + ], + "execution_count": 118, + "outputs": [] + }, + { + "cell_type": "code", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "mycKQHlLIzxD", + "outputId": "d23d2b85-659c-479d-9436-40beaef57e01" + }, + "source": [ + "predicted_numpy.shape" + ], + "execution_count": 119, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "(1043,)" + ] + }, + "metadata": {}, + "execution_count": 119 + } + ] + }, + { + "cell_type": "code", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "NTOvc63PI2QV", + "outputId": "7332a4f9-aac7-43dc-b96c-540458e9dcd4" + }, + "source": [ + "from sklearn.metrics import confusion_matrix\n", + "from sklearn.metrics import plot_confusion_matrix\n", + "y_true = y_test\n", + "y_pred = predicted_numpy\n", + "print(confusion_matrix(y_true, y_pred), end='\\n')" + ], + "execution_count": 120, + "outputs": [ + { + "output_type": "stream", + "text": [ + "[[150 0 0 1 1 0 0 0 0 0 0 0 0]\n", + " [ 0 42 0 4 2 1 0 0 0 0 0 0 0]\n", + " [ 0 0 48 2 1 0 0 0 0 0 0 0 0]\n", + " [ 1 0 7 42 0 0 0 0 0 0 0 0 0]\n", + " [ 0 0 6 1 25 0 0 0 0 0 0 0 0]\n", + " [ 0 1 0 0 0 45 0 0 0 0 0 0 0]\n", + " [ 0 0 0 0 0 0 21 0 0 0 0 0 0]\n", + " [ 0 0 0 1 0 0 0 80 5 0 0 0 0]\n", + " [ 0 0 0 0 0 0 0 8 96 0 0 0 0]\n", + " [ 0 0 0 0 0 0 0 2 0 78 0 1 0]\n", + " [ 0 0 0 0 0 0 0 0 0 0 84 0 0]\n", + " [ 0 0 1 0 1 0 0 0 0 0 0 99 0]\n", + " [ 0 0 0 0 0 0 0 1 0 0 0 0 185]]\n" + ], + "name": "stdout" + } + ] + }, + { + "cell_type": "code", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 445 + }, + "id": "vyFGKcL2DqtK", + "outputId": "6ad145a1-059f-4a91-bb21-d340ae4e44cd" + }, + "source": [ + "from sklearn.metrics import confusion_matrix\n", + "from sklearn.metrics import plot_confusion_matrix\n", + "import seaborn as sn\n", + "y_true = y_test\n", + "y_pred = predicted_numpy\n", + "plt.figure(figsize = (10,7))\n", + "print(sn.heatmap(confusion_matrix(y_true, y_pred),annot=True,cmap=\"OrRd\",fmt='d'))\n", + "plt.savefig('KSC_pca_Confusion Matrix')" + ], + "execution_count": 135, + "outputs": [ + { + "output_type": "stream", + "text": [ + "AxesSubplot(0.125,0.125;0.62x0.755)\n" + ], + "name": "stdout" + }, + { + "output_type": "display_data", + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + } + } + ] + }, + { + "cell_type": "code", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "YN04vF05da6u", + "outputId": "1b7e24c3-a9bc-4cee-c0f5-e47341d851b4" + }, + "source": [ + "from sklearn.metrics import cohen_kappa_score\n", + "print(cohen_kappa_score(y_true, y_pred, labels=None, weights=None, sample_weight=None))" + ], + "execution_count": 122, + "outputs": [ + { + "output_type": "stream", + "text": [ + "0.9487756139835698\n" + ], + "name": "stdout" + } + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "Q-Jm_Xf3I6Gb" + }, + "source": [ + "from sklearn.metrics import classification_report" + ], + "execution_count": 123, + "outputs": [] + }, + { + "cell_type": "code", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "uza4cL23I8sU", + "outputId": "45326f14-5d91-4a03-997f-2812045a21de" + }, + "source": [ + "from sklearn.metrics import confusion_matrix\n", + "from sklearn.metrics import plot_confusion_matrix\n", + "y_true = y_test\n", + "y_pred = predicted_numpy\n", + "target_names = ['class 0', 'class 1', 'class 2', 'class 3', 'class 4', 'class 5', 'class 6', 'class 7', 'class 8', 'class 9', 'class 10', 'class 11', 'class 12']\n", + "print(classification_report(y_true, y_pred, target_names=target_names, digits=4))" + ], + "execution_count": 124, + "outputs": [ + { + "output_type": "stream", + "text": [ + " precision recall f1-score support\n", + "\n", + " class 0 0.9934 0.9868 0.9901 152\n", + " class 1 0.9767 0.8571 0.9130 49\n", + " class 2 0.7742 0.9412 0.8496 51\n", + " class 3 0.8235 0.8400 0.8317 50\n", + " class 4 0.8333 0.7812 0.8065 32\n", + " class 5 0.9783 0.9783 0.9783 46\n", + " class 6 1.0000 1.0000 1.0000 21\n", + " class 7 0.8791 0.9302 0.9040 86\n", + " class 8 0.9505 0.9231 0.9366 104\n", + " class 9 1.0000 0.9630 0.9811 81\n", + " class 10 1.0000 1.0000 1.0000 84\n", + " class 11 0.9900 0.9802 0.9851 101\n", + " class 12 1.0000 0.9946 0.9973 186\n", + "\n", + " accuracy 0.9540 1043\n", + " macro avg 0.9384 0.9366 0.9364 1043\n", + "weighted avg 0.9565 0.9540 0.9545 1043\n", + "\n" + ], + "name": "stdout" + } + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "2x7ipmMdaM41" + }, + "source": [ + "f = sio.loadmat('KSC.mat')['KSC']\n", + "g = sio.loadmat('KSC_gt.mat')['KSC_gt']" + ], + "execution_count": 125, + "outputs": [] + }, + { + "cell_type": "code", + "metadata": { + "id": "px_1RS-CaYMm" + }, + "source": [ + "F,pca = applyPCA(f,30)" + ], + "execution_count": 126, + "outputs": [] + }, + { + "cell_type": "code", + "metadata": { + "id": "eSKEsR-aagxO" + }, + "source": [ + "FPatches, gPatches = createPatches(F,g, windowSize=15)" + ], + "execution_count": 127, + "outputs": [] + }, + { + "cell_type": "code", + "metadata": { + "id": "ZlOxKP8Ga3-e" + }, + "source": [ + "import itertools" + ], + "execution_count": 130, + "outputs": [] + }, + { + "cell_type": "code", + "metadata": { + "id": "ms0BA6OBah21" + }, + "source": [ + "def classified_pixels(FPatches,gPatches,g):\n", + " FPatches=np.reshape(FPatches,(FPatches.shape[0],FPatches.shape[3],FPatches.shape[1],FPatches.shape[2]))\n", + " data_test=MyDataset(FPatches, gPatches)\n", + " test_loader = torch.utils.data.DataLoader(data_test,batch_size=10,shuffle=False, num_workers=2)\n", + " with torch.no_grad():\n", + " correct = 0\n", + " total = 0\n", + " predicted_numpy=[]\n", + " for images, labels in test_loader:\n", + " images = images.to(device)\n", + " labels = labels.to(device)\n", + " outputs = model(images)\n", + " _, predicted = torch.max(outputs.data, 1)\n", + " predicted_numpy.append(predicted.cpu().numpy())\n", + " total += labels.size(0)\n", + " correct += (predicted == labels).sum().item()\n", + " classification_map=np.array(predicted_numpy)\n", + " cm=[]\n", + " for arr in classification_map:\n", + " cm.append(arr.tolist())\n", + " cm=list(itertools.chain.from_iterable(cm))\n", + " classification_map=np.array(cm)\n", + "\n", + " height=g.shape[0]\n", + " width=g.shape[1]\n", + " outputs = np.zeros((height,width))\n", + " k=0\n", + " for i in range(height):\n", + " for j in range(width):\n", + " target = g[i][j]\n", + " if target == 0 :\n", + " continue\n", + " else :\n", + " outputs[i][j]=classification_map[k]\n", + " k=k+1\n", + " return classification_map,outputs" + ], + "execution_count": 131, + "outputs": [] + }, + { + "cell_type": "code", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "UWG5PNKCap0H", + "outputId": "ddb78580-ac25-4849-cf03-4ee7dbd81528" + }, + "source": [ + "cma,out=classified_pixels(FPatches,gPatches,g)" + ], + "execution_count": 132, + "outputs": [ + { + "output_type": "stream", + "text": [ + "/usr/local/lib/python3.7/dist-packages/ipykernel_launcher.py:17: VisibleDeprecationWarning: Creating an ndarray from ragged nested sequences (which is a list-or-tuple of lists-or-tuples-or ndarrays with different lengths or shapes) is deprecated. If you meant to do this, you must specify 'dtype=object' when creating the ndarray\n" + ], + "name": "stderr" + } + ] + }, + { + "cell_type": "code", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 377 + }, + "id": "FY7dB4Jiatq7", + "outputId": "f1939d53-a171-4cb0-f406-69b173ce3cf7" + }, + "source": [ + "plt.figure(figsize=(7,7))\n", + "a=plt.imshow(out)\n", + "plt.savefig('KSC_pca_cmap')" + ], + "execution_count": 133, + "outputs": [ + { + "output_type": "display_data", + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + } + } + ] + }, + { + "cell_type": "code", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 377 + }, + "id": "IU2Z8ja6bEBM", + "outputId": "bd2a1e56-ce2b-4153-f260-b4e209cfe458" + }, + "source": [ + "plt.figure(figsize=(7,7))\n", + "plt.imshow(g)\n", + "plt.savefig('KSC_pca_gt')" + ], + "execution_count": 134, + "outputs": [ + { + "output_type": "display_data", + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + } + } + ] + } + ] +} \ No newline at end of file diff --git a/Hyperspectral Image Classification/HSI.py b/Hyperspectral Image Classification/HSI.py new file mode 100644 index 0000000..f612d6a --- /dev/null +++ b/Hyperspectral Image Classification/HSI.py @@ -0,0 +1,502 @@ +# -*- coding: utf-8 -*- +"""KSC_PCA.ipynb + +Automatically generated by Colaboratory. + +Original file is located at + https://colab.research.google.com/drive/13bVyVdv-yBFo30C4Ce_Q4eKjwpb5Sm8y +""" + +# Commented out IPython magic to ensure Python compatibility. +import numpy as np +from sklearn.decomposition import PCA +import scipy.io as sio +from sklearn.model_selection import train_test_split +from sklearn import preprocessing +import os +import random +from random import shuffle +from skimage.transform import rotate +import scipy.ndimage +import matplotlib.pyplot as plt +# %matplotlib inline + +import torch + +from torch.utils.data import Dataset, DataLoader + +hsi_data= sio.loadmat('KSC.mat')['KSC'] +labels = sio.loadmat('KSC_gt.mat')['KSC_gt'] + +[height,width,depth]=hsi_data.shape + +def splitTrainTestSet(X, y, testRatio=0.10): + X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=testRatio, random_state=345, + stratify=y) + return X_train, X_test, y_train, y_test + +def oversampleWeakClasses(X, y): + uniqueLabels, labelCounts = np.unique(y, return_counts=True) + maxCount = np.max(labelCounts) + labelInverseRatios = maxCount / labelCounts + # repeat for every label and concat + newX = X[y == uniqueLabels[0], :, :, :].repeat(round(labelInverseRatios[0]), axis=0) + newY = y[y == uniqueLabels[0]].repeat(round(labelInverseRatios[0]), axis=0) + for label, labelInverseRatio in zip(uniqueLabels[1:], labelInverseRatios[1:]): + cX = X[y== label,:,:,:].repeat(round(labelInverseRatio), axis=0) + cY = y[y == label].repeat(round(labelInverseRatio), axis=0) + newX = np.concatenate((newX, cX)) + newY = np.concatenate((newY, cY)) + np.random.seed(seed=42) + rand_perm = np.random.permutation(newY.shape[0]) + newX = newX[rand_perm, :, :, :] + newY = newY[rand_perm] + return newX, newY + +def standartizeData(X): + newX = np.reshape(X, (-1, X.shape[2])) + scaler = preprocessing.StandardScaler().fit(newX) + newX = scaler.transform(newX) + newX = np.reshape(newX, (X.shape[0],X.shape[1],X.shape[2])) + return newX, scaler + +def applyPCA(X, numComponents=75): + newX = np.reshape(X, (-1, X.shape[2])) + pca = PCA(n_components=numComponents, whiten=True) + newX = pca.fit_transform(newX) + newX = np.reshape(newX, (X.shape[0],X.shape[1], numComponents)) + return newX, pca + +def padWithZeros(X, margin=2): + newX = np.zeros((X.shape[0] + 2 * margin, X.shape[1] + 2* margin, X.shape[2])) + x_offset = margin + y_offset = margin + newX[x_offset:X.shape[0] + x_offset, y_offset:X.shape[1] + y_offset, :] = X + return newX + +def createPatches(X, y, windowSize=11, removeZeroLabels = True): + margin = int((windowSize - 1) / 2) + zeroPaddedX = padWithZeros(X, margin=margin) + # split patches + patchesData = np.zeros((X.shape[0] * X.shape[1], windowSize, windowSize, X.shape[2])) + patchesLabels = np.zeros((X.shape[0] * X.shape[1])) + patchIndex = 0 + for r in range(margin, zeroPaddedX.shape[0] - margin): + for c in range(margin, zeroPaddedX.shape[1] - margin): + patch = zeroPaddedX[r - margin:r + margin + 1, c - margin:c + margin + 1] + patchesData[patchIndex, :, :, :] = patch + patchesLabels[patchIndex] = y[r-margin, c-margin] + patchIndex = patchIndex + 1 + if removeZeroLabels: + patchesData = patchesData[patchesLabels>0,:,:,:] + patchesLabels = patchesLabels[patchesLabels>0] + patchesLabels -= 1 + return patchesData, patchesLabels + + +def AugmentData(X_train): + for i in range(int(X_train.shape[0]/2)): + patch = X_train[i,:,:,:] + num = random.randint(0,2) + if (num == 0): + + flipped_patch = np.flipud(patch) + if (num == 1): + + flipped_patch = np.fliplr(patch) + if (num == 2): + + no = random.randrange(-180,180,30) + flipped_patch = scipy.ndimage.interpolation.rotate(patch, no,axes=(1, 0), + reshape=False, output=None, order=3, mode='constant', cval=0.0, prefilter=False) + + + patch2 = flipped_patch + X_train[i,:,:,:] = patch2 + + return X_train + +import numpy as np +import scipy +import os +from keras.models import Sequential +from keras.layers import Dense, Dropout, Flatten +from keras.layers import Conv2D, MaxPooling2D,BatchNormalization +from tensorflow.keras.callbacks import EarlyStopping +from tensorflow.keras.optimizers import SGD +from keras import backend as K +from keras.utils import np_utils +from keras.utils.vis_utils import plot_model + +weight_of_size=10 + +X=hsi_data +y=labels + +X,pca = applyPCA(X,30) + +X.shape + +XPatches, yPatches = createPatches(X, y, windowSize=15) + +XPatches.shape + +X_train, X_test, y_train, y_test = splitTrainTestSet(XPatches, yPatches, testRatio=0.2) + +X_train=np.reshape(X_train,(X_train.shape[0],X_train.shape[3],X_train.shape[1],X_train.shape[1])) + +X_test=np.reshape(X_test,(X_test.shape[0],X_test.shape[3],X_test.shape[1],X_test.shape[1])) + +X_train.shape + +X_train.shape + +y_train + +class MyDataset(Dataset): + def __init__(self, data, target, transform=None): + self.data = torch.from_numpy(data).float() + self.target = torch.from_numpy(target).int() + self.transform = transform + + def __getitem__(self, index): + x = self.data[index] + y = self.target[index] + + if self.transform: + x = self.transform(x) + + return x, y + + def __len__(self): + return len(self.data) + +data_train = MyDataset(X_train, y_train) + +input_shape= X_train[0].shape +print(input_shape) + +data_train.__getitem__(0)[0].shape + +n_epochs = 8 +batch_size_train = 16 +batch_size_test = 10 +learning_rate = 0.01 +momentum = 0.5 +log_interval = 100 +first_HL = 8 + +import torch +import torchvision + +## Call the Dataset Class +#data_train = torchvision.datasets.IndianPines('./data',download=True,PATCH_LENGTH=2) + +## Check the shapes +print(data_train.__getitem__(0)[0].shape) +print(data_train.__len__()) + + +## Wrap it around a Torch Dataloader +train_loader = torch.utils.data.DataLoader(data_train,batch_size=16,shuffle=True, num_workers=2) + +len(data_train) + +data_test=MyDataset(X_test, y_test) + +import torch +import torchvision + +## Call the Dataset Class +#data_test = torchvision.datasets.IndianPines('./data',download=True,PATCH_LENGTH=2) + +## Check the shapes +print(data_test.__getitem__(0)[0].shape) +print(data_test.__len__()) + +test_loader = torch.utils.data.DataLoader(data_test,batch_size=10,shuffle=False, num_workers=2) + +examples = enumerate(test_loader) +batch_idx, (example_data, example_targets) = next(examples) + +print(example_data.shape) + +import matplotlib.pyplot as plt + +fig = plt.figure() +for i in range(6): + plt.subplot(2,3,i+1) + plt.tight_layout() + plt.imshow(example_data[i][0], interpolation='none') + plt.title("Ground Truth: {}".format(example_targets[i])) + plt.xticks([]) + plt.yticks([]) +fig + +import torch +import torch.nn as nn +import torchvision +import torchvision.transforms as transforms + +import random + +device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') + +# Hyper-parameters +num_epochs = 16 +learning_rate = 0.001 + +torch.manual_seed(0) +random.seed(0) + +Half_width =60 +layer_width = 20 + +class SpinalCNN(nn.Module): + """CNN.""" + + def __init__(self): + """CNN Builder.""" + super(SpinalCNN, self).__init__() + + self.conv_layer = nn.Sequential( + + # Conv Layer block 1 + nn.Conv2d(in_channels=30, out_channels=15, kernel_size=3, padding=1), + nn.BatchNorm2d(15), + nn.ReLU(inplace=True), + nn.Conv2d(in_channels=15, out_channels=30, kernel_size=3, padding=1), + nn.ReLU(inplace=True), + nn.MaxPool2d(kernel_size=2, stride=2), + + # Conv Layer block 2 + nn.Conv2d(in_channels=30, out_channels=60, kernel_size=3, padding=1), + nn.BatchNorm2d(60), + nn.ReLU(inplace=True), + nn.Conv2d(in_channels=60, out_channels=60, kernel_size=3, padding=1), + nn.ReLU(inplace=True), + nn.MaxPool2d(kernel_size=2, stride=2), + nn.Dropout2d(p=0.05), + + # Conv Layer block 3 + nn.Conv2d(in_channels=60, out_channels=120, kernel_size=3, padding=1), + nn.BatchNorm2d(120), + nn.ReLU(inplace=True), + nn.Conv2d(in_channels=120, out_channels=120, kernel_size=3, padding=1), + nn.ReLU(inplace=True), + nn.MaxPool2d(kernel_size=2, stride=2), + ) + + self.fc_spinal_layer1 = nn.Sequential( + nn.Dropout(p=0.1), nn.Linear(Half_width, layer_width), + nn.ReLU(inplace=True), + ) + self.fc_spinal_layer2 = nn.Sequential( + nn.Dropout(p=0.1), nn.Linear(Half_width + layer_width, layer_width), + nn.ReLU(inplace=True), + ) + self.fc_spinal_layer3 = nn.Sequential( + nn.Dropout(p=0.1), nn.Linear(Half_width + layer_width, layer_width), + nn.ReLU(inplace=True), + ) + self.fc_spinal_layer4 = nn.Sequential( + nn.Dropout(p=0.1), nn.Linear(Half_width + layer_width, layer_width), + nn.ReLU(inplace=True), + ) + self.fc_out = nn.Sequential( + nn.Dropout(p=0.1), nn.Linear(layer_width*4, 16) + ) + + + def forward(self, x): + """Perform forward.""" + + # conv layers + x = self.conv_layer(x) + + # flatten + x = x.view(x.size(0), -1) + + x1 = self.fc_spinal_layer1(x[:, 0:Half_width]) + x2 = self.fc_spinal_layer2(torch.cat([ x[:,Half_width:2*Half_width], x1], dim=1)) + x3 = self.fc_spinal_layer3(torch.cat([ x[:,0:Half_width], x2], dim=1)) + x4 = self.fc_spinal_layer4(torch.cat([ x[:,Half_width:2*Half_width], x3], dim=1)) + + x = torch.cat([x1, x2], dim=1) + x = torch.cat([x, x3], dim=1) + x = torch.cat([x, x4], dim=1) + + x = self.fc_out(x) + + return x + +from tensorflow.keras.optimizers import Adam +model = SpinalCNN().to(device) +# defining the optimizer +optimizer = Adam(model.parameters(), lr=0.07) +# defining the loss function +criterion = nn.CrossEntropyLoss() +# checking if GPU is available +if torch.cuda.is_available(): + model = model.cuda() + criterion = criterion.cuda() + +print(model) + +from torchvision import models +from torchsummary import summary +model = SpinalCNN().to(device) +summary(model, (30, 15, 15)) + +model = SpinalCNN().to(device) + + + +# Loss and optimizer +criterion = nn.CrossEntropyLoss() +optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate) + +# For updating learning rate +def update_lr(optimizer, lr): + for param_group in optimizer.param_groups: + param_group['lr'] = lr + +# Train the model +total_step = len(train_loader) +curr_lr = learning_rate +for epoch in range(num_epochs): + for i, (images, labels) in enumerate(train_loader): + images = images.to(device) + labels = labels.to(device) + + #print(images.shape) + # Forward pass + outputs = model(images) + labels = torch.tensor(labels, dtype=torch.long, device=device) + loss = criterion(outputs, labels) + + # Backward and optimize + optimizer.zero_grad() + loss.backward() + optimizer.step() + + + if (i+1) % 500 == 0: + print("Epoch [{}/{}], Step [{}/{}] Loss: {:.4f}" + .format(epoch+1, num_epochs, i+1, total_step, loss.item())) + + + # Decay learning rate + if (epoch) == 1 or epoch>20: + curr_lr /= 3 + update_lr(optimizer, curr_lr) + + # Test the model + model.eval() + with torch.no_grad(): + correct = 0 + total = 0 + predicted_numpy=[] + for images, labels in test_loader: + + images = images.to(device) + labels = labels.to(device) + outputs = model(images) + _, predicted = torch.max(outputs.data, 1) + predicted_numpy.append(predicted.cpu().numpy()) + + total += labels.size(0) + correct += (predicted == labels).sum().item() + + print('Accuracy of the model on the test images: {} %'.format(100 * correct / total)) + + model.train() + +len(predicted_numpy) + +predicted_numpy = np.concatenate(predicted_numpy) + +predicted_numpy.shape + +from sklearn.metrics import confusion_matrix +from sklearn.metrics import plot_confusion_matrix +y_true = y_test +y_pred = predicted_numpy +print(confusion_matrix(y_true, y_pred), end='\n') + +from sklearn.metrics import confusion_matrix +from sklearn.metrics import plot_confusion_matrix +import seaborn as sn +y_true = y_test +y_pred = predicted_numpy +plt.figure(figsize = (10,7)) +print(sn.heatmap(confusion_matrix(y_true, y_pred),annot=True,cmap="OrRd",fmt='d')) +plt.savefig('KSC_pca_Confusion Matrix') + +from sklearn.metrics import cohen_kappa_score +print(cohen_kappa_score(y_true, y_pred, labels=None, weights=None, sample_weight=None)) + +from sklearn.metrics import classification_report + +from sklearn.metrics import confusion_matrix +from sklearn.metrics import plot_confusion_matrix +y_true = y_test +y_pred = predicted_numpy +target_names = ['class 0', 'class 1', 'class 2', 'class 3', 'class 4', 'class 5', 'class 6', 'class 7', 'class 8', 'class 9', 'class 10', 'class 11', 'class 12'] +print(classification_report(y_true, y_pred, target_names=target_names, digits=4)) + +f = sio.loadmat('KSC.mat')['KSC'] +g = sio.loadmat('KSC_gt.mat')['KSC_gt'] + +F,pca = applyPCA(f,30) + +FPatches, gPatches = createPatches(F,g, windowSize=15) + +import itertools + +def classified_pixels(FPatches,gPatches,g): + FPatches=np.reshape(FPatches,(FPatches.shape[0],FPatches.shape[3],FPatches.shape[1],FPatches.shape[2])) + data_test=MyDataset(FPatches, gPatches) + test_loader = torch.utils.data.DataLoader(data_test,batch_size=10,shuffle=False, num_workers=2) + with torch.no_grad(): + correct = 0 + total = 0 + predicted_numpy=[] + for images, labels in test_loader: + images = images.to(device) + labels = labels.to(device) + outputs = model(images) + _, predicted = torch.max(outputs.data, 1) + predicted_numpy.append(predicted.cpu().numpy()) + total += labels.size(0) + correct += (predicted == labels).sum().item() + classification_map=np.array(predicted_numpy) + cm=[] + for arr in classification_map: + cm.append(arr.tolist()) + cm=list(itertools.chain.from_iterable(cm)) + classification_map=np.array(cm) + + height=g.shape[0] + width=g.shape[1] + outputs = np.zeros((height,width)) + k=0 + for i in range(height): + for j in range(width): + target = g[i][j] + if target == 0 : + continue + else : + outputs[i][j]=classification_map[k] + k=k+1 + return classification_map,outputs + +cma,out=classified_pixels(FPatches,gPatches,g) + +plt.figure(figsize=(7,7)) +a=plt.imshow(out) +plt.savefig('KSC_pca_cmap') + +plt.figure(figsize=(7,7)) +plt.imshow(g) +plt.savefig('KSC_pca_gt') \ No newline at end of file diff --git a/Hyperspectral Image Classification/HSI_Confusion Matrix.png b/Hyperspectral Image Classification/HSI_Confusion Matrix.png new file mode 100644 index 0000000..9137ce4 Binary files /dev/null and b/Hyperspectral Image Classification/HSI_Confusion Matrix.png differ diff --git a/Hyperspectral Image Classification/HSI_cmap.png b/Hyperspectral Image Classification/HSI_cmap.png new file mode 100644 index 0000000..d36b7e3 Binary files /dev/null and b/Hyperspectral Image Classification/HSI_cmap.png differ diff --git a/Hyperspectral Image Classification/HSI_gt.png b/Hyperspectral Image Classification/HSI_gt.png new file mode 100644 index 0000000..bab40ac Binary files /dev/null and b/Hyperspectral Image Classification/HSI_gt.png differ