Python Forum

Full Version: Help with multiclass classification in perceptron code
You're currently viewing a stripped down version of our content. View the full version with proper formatting.
Hello!
I need help with my python programming where I implemented Multiclass Perceptron. I am having trouble in updating the weight. I don't know where I am going wrong I always end up getting low accuracy value. I have pasted my code below as well as the output. I have used Sklearn module to compare my accuracy.

Thanks

'''
Implementation of Perceptron for multi-class classification
'''
class PerceptronMultiClass(object):

    def __init__(self):
        # Define private variables, weights and number of classes
        self.__weights = None
        self.__n_class = -1

    def __update(self, x, y):
        '''
        Update the weight vector during each training iteration

        Args:
            x : numpy
                d x N feature vector
            y : numpy
                1 x N ground-truth label
        '''
        # TODO: Implement the member update function
        threshold = 0.5 * np.ones([1, x.shape[1]])
        x = np.concatenate([threshold, x], axis = 0)
        for n in range(x.shape[1]):
                x_n = np.expand_dims(x[:, n],axis=-1)
                prediction = np.matmul(self.__weights.T,x_n)
                #print('ARGMAX',np.argmax(prediction))
                prediction = np.argmax(np.amax(prediction))
                if prediction != y[n]:
                    for c in range(self.__n_class):
                        weights_c = np.expand_dims(self.__weights[:,c],axis=0)
                        #print('dimensions before',weights_c.shape)
                        weights_c = weights_c
                        #print('dimensions after',weights_c.shape)
                        #print('shape of x_n',x_n.shape)
                        if c == prediction:
                            weights_c = weights_c - x_n
                            self.__weights[:,c] = weights_c[:,0]
                        elif c == y[n]:
                            weights_c = weights_c + x_n
                            self.__weights[:,c] = weights_c[:,0]
                        else:
                            continue
    def fit(self, x, y, T=100, tol=1e-3):
        '''
        Fits the model to x and y by updating the weight vector
        based on mis-classified examples for t iterations until convergence

        Args:
            x : numpy
                d x N feature vector
            y : numpy
                1 x N ground-truth label
            t : int
                number of iterations to optimize perceptron
            tol : float
                change of loss tolerance, if greater than loss + tolerance, then stop
        '''
        # TODO: Implement the fit function
        #Initialize the weights to zero
        for n in range(y.shape[0]):
            self.__n_class= len(np.unique(y)) #number of classes
        #print(x.shape[0],x.shape[1],self.__n_class) #(d+1,C)
        self.__weights = np.zeros([x.shape[0]+1, self.__n_class])
        for i in range(self.__n_class):
            self.__weights[0] = -1.0
        #print(self.__weights)
        #print(self.__weights.shape[0],self.__weights.shape[1])
        #finding misclassified examples
        # c_hat = h(x^n(t)) , c_star = y^n ---> unique values determine the __n_class
        #Initialize loss and weights
        prev_loss = 2.0
        pre_weights = np.copy(self.__weights)

        for t in range(T):
            predictions = self.predict(x)
            #loss = 1/N sum n^N
            loss = np.mean(np.where(predictions !=y, 1.0, 0.0))
            #stopping convergence
            if loss == 0.0:
                break

            elif loss > prev_loss + tol and t > 2:
                self.__weights = pre_weights
                break
            prev_loss = loss
            pre_weights = np.copy(self.__weights)
            #updating weight vector and class
            self.__update(x,y)
    def predict(self, x):
        '''
        Predicts the label for each feature vector x

        Args:
            x : numpy
                d x N feature vector

        Returns:
            numpy : 1 x N label vector
        '''
        # TODO: Implement the predict function
        #compute weights (d+1,N)
        #threshold shape is (1,N)
        threshold = 0.5 * np.ones((1, x.shape[1]))
        #print('threshold',threshold.shape)
        #x is (d,N), thus concatenate threshold and # X
        x = np.concatenate([threshold,x],axis=0) #--> (d+1,N)
        #print('Size of x',x.shape)
        #predict w^T(d+1,N)^T . (d+1,N) --> (1,N)

        predictions = np.matmul(self.__weights.T,x)
        #print(predictions.shape[0],predictions.shape[1])
        #argmax of predcitions
        #print('prediction of values',predictions)
        predictions = np.argmax(np.amax(predictions,axis=1))
        #print('predictions after argmax',predictions)
        return predictions
    def score(self, x, y):
        '''
        Predicts labels based on feature vector x and computes the mean accuracy
        of the predictions

        Args:
            x : numpy
                d x N feature vector
            y : numpy
                1 x N ground-truth label

        Returns:
            float : mean accuracy
        '''
        # TODO: Implement the score function
        predcitions = self.predict(x)

        #accuracy score
        scores = np.where(predcitions == y, 1.0, 0.0)
        return np.mean(scores)


def split_dataset(x, y, n_sample_train_to_val_test=8):
    '''
    Helper function to splits dataset into training, validation and testing sets

    Args:
        x : numpy
            d x N feature vector
        y : numpy
            1 x N ground-truth label
        n_sample_train_to_val_test : int
            number of training samples for every validation, testing sample

    Returns:
        x_train : numpy
            d x n feature vector
        y_train : numpy
            1 x n ground-truth label
        x_val : numpy
            d x m feature vector
        y_val : numpy
            1 x m ground-truth label
        x_test : numpy
            d x m feature vector
        y_test : numpy
            1 x m ground-truth label
    '''
    n_sample_interval = n_sample_train_to_val_test + 2

    train_idx = []
    val_idx = []
    test_idx = []
    for idx in range(x.shape[0]):
        if idx and idx % n_sample_interval == (n_sample_interval - 1):
            val_idx.append(idx)
        elif idx and idx % n_sample_interval == 0:
            test_idx.append(idx)
        else:
            train_idx.append(idx)

    x_train, x_val, x_test = x[train_idx, :], x[val_idx, :], x[test_idx, :]
    y_train, y_val, y_test = y[train_idx], y[val_idx], y[test_idx]

    return x_train, y_train, x_val, y_val, x_test, y_test


if __name__ == '__main__':

    iris_data = skdata.load_iris()
    wine_data = skdata.load_wine()

    datasets = [iris_data, wine_data]
    tags = ['iris', 'wine']

    # TODO: Experiment with 3 different max training steps (T) for each dataset
    train_steps_iris = [60,100,200]
    train_steps_wine = [60, 80, 100]

    train_steps = [train_steps_iris, train_steps_wine]

    # TODO: Set a tolerance for each dataset
    tol_iris = 1e-2
    tol_wine = 1

    tols = [tol_iris, tol_wine]

    for dataset, steps, tol, tag in zip(datasets, train_steps, tols, tags):
        # Split dataset into 80 training, 10 validation, 10 testing
        x = dataset.data
        y = dataset.target
        x_train, y_train, x_val, y_val, x_test, y_test = split_dataset(
            x=x,
            y=y,
            n_sample_train_to_val_test=8)

        '''
        Trains and tests Perceptron model from scikit-learn
        '''
        model = Perceptron(penalty=None, alpha=0.0, tol=1e-3)
        # Trains scikit-learn Perceptron model
        model.fit(x_train, y_train)

        print('Results on the {} dataset using scikit-learn Perceptron model'.format(tag))

        # Test model on training set
        scores_train = model.score(x_train, y_train)
        print('Training set mean accuracy: {:.4f}'.format(scores_train))

        # Test model on validation set
        scores_val = model.score(x_val, y_val)
        print('Validation set mean accuracy: {:.4f}'.format(scores_val))

        # Test model on testing set
        scores_test = model.score(x_test, y_test)
        print('Testing set mean accuracy: {:.4f}'.format(scores_test))

        '''
        Trains, validates, and tests our Perceptron model for multi-class classification
        '''
        # TODO: obtain dataset in correct shape (d x N)
        x_train = np.transpose(x_train, axes=(1, 0))
        x_val = np.transpose(x_val, axes=(1, 0))
        x_test = np.transpose(x_test, axes=(1, 0))
        # Initialize empty lists to hold models and scores
        models = []
        scores = []
        for T in steps:
            # TODO: Initialize PerceptronMultiClass model
            model = PerceptronMultiClass()
            print('Results on the {} dataset using our Perceptron model trained with {} steps and tolerance of {}'.format(tag, T, tol))
            # TODO: Train model on training set
            model.fit(x_train, y_train)

            # TODO: Test model on training set
            scores_train = model.score(x_train, y_train)
            print('Training set mean accuracy: {:.4f}'.format(scores_train))

            # TODO: Test model on validation set
            scores_val = model.score(x_val, y_val)
            print('Validation set mean accuracy: {:.4f}'.format(scores_val))

            # TODO: Save the model and its score
            models.append(model)
            scores.append(scores_val)

        # TODO: Select the best performing model on the validation set
        #iterate the validation scores
        for i in range(len(scores)):
            best_idx = i
        print('Using best model trained with {} steps and tolerance of {}'.format(steps[best_idx], tol))

        # TODO: Test model on testing set
        scores_test = model.score(x_test, y_test)
        print('Testing set mean accuracy: {:.4f}'.format(scores_test))
Output:
Results on the iris dataset using scikit-learn Perceptron model Training set mean accuracy: 0.8512 Validation set mean accuracy: 0.7333 Testing set mean accuracy: 0.9286 Results on the iris dataset using our Perceptron model trained with 60 steps and tolerance of 0.01 Training set mean accuracy: 0.3306 Validation set mean accuracy: 0.3333 Results on the iris dataset using our Perceptron model trained with 100 steps and tolerance of 0.01 Training set mean accuracy: 0.3306 Validation set mean accuracy: 0.3333 Results on the iris dataset using our Perceptron model trained with 200 steps and tolerance of 0.01 Training set mean accuracy: 0.3306 Validation set mean accuracy: 0.3333 Using best model trained with 200 steps and tolerance of 0.01 Testing set mean accuracy: 0.3571 Results on the wine dataset using scikit-learn Perceptron model Training set mean accuracy: 0.5625 Validation set mean accuracy: 0.4118 Testing set mean accuracy: 0.4706 Results on the wine dataset using our Perceptron model trained with 60 steps and tolerance of 1 Training set mean accuracy: 0.3889 Validation set mean accuracy: 0.4706 Results on the wine dataset using our Perceptron model trained with 80 steps and tolerance of 1 Training set mean accuracy: 0.3889 Validation set mean accuracy: 0.4706 Results on the wine dataset using our Perceptron model trained with 100 steps and tolerance of 1 Training set mean accuracy: 0.3889 Validation set mean accuracy: 0.4706 Using best model trained with 100 steps and tolerance of 1 Testing set mean accuracy: 0.4118