1

before I begin I've seen: Perceptron implementation, decision boundary will not plot

and:

Plot a 3D Boundary Decision in Python

and I still can't figure this out!

I've trained a perceptron to classify flower class based on the classic IRIS dataset. I want to iteratively plot a 3d decision boundary over successive epochs so that I can 'see' the model updating. Here is an example of something similar I've done for a polynomial regression: Updating the model parameters over epochs of gradient descent

I understand the mathematical intuition behind plotting a plane as a decision boundary. There are three input dimensions, so the clusters of each class could be separated by a plane. I understand that the equation for such a plane is iteratively updated over successive epochs of training, and it's this updating that I want to visualize!

I'm at the point where the model is trained, and I have a matrix of weights. Here is my code, which you should be able to run immediately as the data is pulled directly from a URL:

#The perceptron has many inputs(features)
#The inputs are fed into a linear unit and produce one binary output
#The perceptron finds linear separation of a dataset, and can classify via decision boundary 


import numpy as np
import pandas as pd
pd.options.mode.chained_assignment = None  # default='warn'
import matplotlib.pyplot as plt


def load_data():
    #read in data
    URL_='https://archive.ics.uci.edu/ml/machine-learning-databases/iris/iris.data'
    raw_data = pd.read_csv(URL_, header = None)
    
    data = raw_data.rename(columns={0:'sepal_length_cm',1:'sepal_width_cm',\
                         2:'petal_length_cm',4:'flower_class'})
    #drop petal width so we can easily visualize
    data = data.drop([3], axis=1)
    #We will only use two of the flowers because they are linearly seperable
    training_data = data.iloc[0:100]
    #label the data according to flower identity (0) is true for Iris-setosa
    training_data['flower_class'] = np.where(training_data\
                                            .iloc[:, -1]=='Iris-setosa', 0, 1)
    #split the dataframe into separate test/train sets
    test_1 = training_data.iloc[0:15,:]
    test_2 = training_data.iloc[50:65,:]
    frames = [test_1,test_2]
    testing_data = pd.concat(frames)
    training_data.drop(testing_data.index, axis=0,inplace=True)
    #return data as a matrix of floats
    training_data = np.asmatrix(training_data, dtype = 'float64')
    
    return training_data,testing_data

def visualize(data):
    #visualize the data features
    plt.figure("Input Features",figsize=(11,7))  
    ax = plt.axes(projection ='3d')  
    ax.zaxis.set_major_formatter('{x:.02f}')
    ax.scatter(np.array(data[:34,0]), np.array(data[:34,1]),\
                np.array(data[:34,2]),label='setosa')
    ax.scatter(np.array(data[35:,0]), np.array(data[35:,1]), \
                np.array(data[:35,2]),marker='x', label='versicolor')
    plt.legend(loc="upper left")
    plt.show()
    ax.set_xlabel('sepal length (cm)', fontweight ='bold') 
    ax.set_ylabel('sepal width (cm)', fontweight ='bold') 
    ax.set_zlabel('petal length (cm)', fontweight ='bold')
    plt.title("Input features of Iris Flowers")

def perceptron(data, num_iter):
    features = data[:, :-1]
    labels = data[:, -1]
    learning_rate = .1
    # set weights to zero
    w = np.zeros(shape=(1, features.shape[1]+1)) #initialize weights/bias to 0
    print('\nInitialized synaptic weights / activation bias: ',w,'\n')
    
    for epoch in range(num_iter):
        for x, label in zip(features, labels): #go through each x value 
            x = np.insert(x,0,1) #insert 1 as a 'bias-weight' for dot product
            y = np.dot(w, x.transpose()) #prediction, here is the linear combination
            p_output = 1.0 if (y > 0) else 0.0 #threshold function 
            error = (label - p_output)
            if error!=0: # misclassified - don't update params if correct output
                w += learning_rate * (error * x)
        print('epoch',epoch+1,' - Updated parameters: ',w)

    return (w)
             
def main():
    global data
    training_data,testing_data = load_data()
    visualize(training_data)
    num_iter = 10
    global w
    w = perceptron(training_data, num_iter)

#solve for plane equation and plot decision boundary plane iteratively


main()


And here is my output: 

Initialized synaptic weights / activation bias:  [[0. 0. 0. 0.]] 

epoch 1  - Updated parameters:  [[0.1  0.67 0.31 0.44]]
epoch 2  - Updated parameters:  [[ 0.    0.23 -0.21  0.6 ]]
epoch 3  - Updated parameters:  [[ 0.    0.33 -0.34  0.89]]
epoch 4  - Updated parameters:  [[ 0.    0.43 -0.47  1.18]]
epoch 5  - Updated parameters:  [[-0.1  -0.14 -0.91  1.03]]
epoch 6  - Updated parameters:  [[-0.1  -0.14 -0.91  1.03]]
epoch 7  - Updated parameters:  [[-0.1  -0.14 -0.91  1.03]]
epoch 8  - Updated parameters:  [[-0.1  -0.14 -0.91  1.03]]
epoch 9  - Updated parameters:  [[-0.1  -0.14 -0.91  1.03]]
epoch 10  - Updated parameters:  [[-0.1  -0.14 -0.91  1.03]]

How would I go about plotting a plane decision-boundary given these weights I've solved for?

desertnaut
  • 57,590
  • 26
  • 140
  • 166
mTeSR
  • 11
  • 2

0 Answers0