Python Program to Classify Various Iris Plants Assignment Solution.

Instructions

Objective
Write a python assignment to classify various iris plants.

Requirements and Specifications

Source Code

```import numpy as np import pandas as pd from sklearn.preprocessing import OneHotEncoder import matplotlib.pyplot as plt class NeuralNetwork: def __init__(self, layers, X=None, y=None, activation=lambda x: 1 / (1 + np.exp(-x)), deriv_activation=lambda x: np.multiply(x, 1 - x), learning_rate=0.15): self.X = X self.y = y self.weights = list() self.layers = layers self.X_train = None self.y_train = None self.X_test = None self.y_test = None self.act = activation self.dact = deriv_activation self.lr = learning_rate self.losses = list() self.accuracies = list() def train_test_split(self, test_size = 0.3): if self.X is None: raise ValueError('No dataset has been loaded into the Neural Network model') # Compute the number of training samples n_train = len(self.X)-int(test_size * len(self.X)) # Split self.X_train = self.X[:n_train] self.y_train = self.y[:n_train] self.X_test = self.X[n_train:] self.y_test = self.y[n_train:] # print info print(f"There are a total of {len(self.X)} samples and {len(self.y[0])} features in the dataset.") print(f"There are {len(self.X_train)} samples in the train dataset.") print(f"There are {len(self.X_test)} samples in the test dataset.") def initialize_weights(self, layers): # Initialize weights for hidden layers (not taking into account the input and output layers) n_layers = len(layers) for i in range(1, n_layers): w = [ [np.random.uniform(-1, 1) for k in range(layers[i - 1] + 1)] for j in range(layers[i]) ] self.weights.append(np.matrix(w)) def fit(self, epochs=10): # Initialize weights for hidden layers (not taking into account the input and output layers) n_layers = len(self.layers) - 1 self.initialize_weights(self.layers) # Update weights and print acc for each epoch print('-' * 40) print('{:<10s}{:>10s}{:>12s}'.format('Epoch', 'Accuracy (%)', 'Loss')) print('{:<10s}{:>10s}{:>12s}'.format('-----', '------------', '----')) for epoch in range(epochs): # Update weights err = self.train() # Compute current accuracy accuracy = self.accuracy(self.X_train, self.y_train) print('{:<10d}{:<20.2f}{:<20.6f}'.format(epoch + 1, accuracy * 100.0, abs(err.mean()))) self.losses.append(abs(err.mean())) self.accuracies.append(accuracy) # After all training, print test accuracy print('-' * 40) val_acc = self.accuracy(self.X_test, self.y_test) print('Model accuracy: {:.2f}%'.format(val_acc * 100.0)) print('-' * 40) def accuracy(self, X, y): n_good = 0 for i in range(len(X)): y_pred = self.predict(X[i]) if list(y[i]) == y_pred: n_good += 1 return n_good / len(X) def predict(self, X): X = np.append(1, X) # Compute the activations result = self.forward_prop(X) output = result[-1].A1 index = self.find_max_activation(output) y = [0 for i in range(len(output))] y[index] = 1 return y def find_max_activation(self, output): m, index = output[0], 0 for i in range(1, len(output)): if output[i] > m: m, index = output[i], i return index def forward_prop(self, x): activations = [x] layer_input = x for i in range(len(self.weights)): activation = self.act(np.dot(layer_input, self.weights[i].T)) activations.append(activation) layer_input = np.append(1, activation) # Augment with bias return activations def backward_prop(self, y, activations): outputFinal = activations[-1] error = np.matrix(y - outputFinal) # Error at output ; finalErr = None n_layers = len(self.weights) for j in range(n_layers, 0, -1): currActivation = activations[j] if j > 1: # Augment previous activation prevActivation = np.append(1, activations[j - 1]) else: # First hidden layer, prevActivation is input (without bias) prevActivation = activations[0] delta = np.multiply(error, self.dact(currActivation)) self.weights[j - 1] += self.lr * np.multiply(delta.T, prevActivation) # Remove bias from weights to calculate error w = np.delete(self.weights[j - 1], [0], axis=1) error = np.dot(delta, w) # Error at current layer if j == 1: finalErr = error return finalErr def train(self): X = self.X_train Y = self.y_train n_layers = len(self.weights) for i in range(len(X)): x, y = X[i], Y[i] x = np.matrix(np.append(1, x)) # Augment feature vector activations = self.forward_prop(x) err = self.backward_prop(y, activations) return err if __name__ == '__main__': # Load data df = pd.read_csv('ANN - Iris data.txt', header=None, names=['sepal_length', 'sepal_width', 'petal_length', 'petal_width', 'label']) # Extract dependant and independent variables and convert to numpy arrays y = df['label'].to_numpy() X = df.drop(columns=['label']).to_numpy() # Encode the dependent variable y y = y.reshape(-1, 1) encoder = OneHotEncoder(sparse=False) y = encoder.fit_transform(y) layers = [X.shape[1], 8, 16, y.shape[1]] epochs = 100 nn = NeuralNetwork(layers, X, y) nn.train_test_split(0.2) nn.fit(epochs) # Plot accuracy and loss fig, axes = plt.subplots(nrows=1, ncols=2) axes[0].plot(range(epochs), nn.accuracies) axes[0].set_xlabel('Epoch') axes[0].set_ylabel('Accuracy (%)') axes[0].grid(True) axes[1].plot(range(epochs), nn.losses) axes[1].set_xlabel('Epoch') axes[1].set_ylabel('Loss') axes[1].grid(True) plt.show()```