My perceptron algorithm keeps giving me the wrong line for separating 2D linearly separable data

35 views Asked by At

I'm implementing a perceptron model for dummy 2D data. Below is how I generate my data

#numpoint
n = 15
#f(x) = w0 + ax1 + bx2
#then if f(x) = 0
#x2 = (-w0 - ax1)/b 
intercept = 30
a = 4
b = 2
#generate random points from 0 - 20
x1 = np.random.uniform(-20, 20, n) #return a np array
x2 = np.random.uniform(-20, 20, n)
y = []
#plot f(x)
plt.plot(x1, (-intercept - a*x1)/b, 'k-') 
plt.ylabel("x2")
plt.xlabel("x1")

#plot colored points
for i in range(0, len(x1)):
    f = intercept + a * x1[i] + b * x2[i]
    if (f <= 0):
        plt.plot(x1[i], x2[i], 'ro')
        y.append(-1)
    if (f > 0):
        plt.plot(x1[i], x2[i], 'bo')
        y.append(1)
y = np.array(y)
# Add x0 for threshold
x0 = np.ones(n)
stacked_x = np.stack((x0,x1,x2))
stacked_x

This is the visualization of the data

enter image description here

This is my Perceptron Model

class PLA():
    def __init__(self, numPredictors):
        self.w = np.random.rand(1,numPredictors+1) #(1, numPredictors+1)
        self.iter = 0
    def fitModel(self, xData, yData):
        while(True): 
            yhat = np.matmul(self.w, xData).squeeze() #from(1,n) to (,n)
            compare = np.sign(yhat) == yData          
            ind = [i for i in range(0,len(compare)) if compare[i] == False] #misclassified index
            print(len(ind))
            if len(ind) == 0:    
                break
            for i in ind:
                update = yData[i]* xData[:, i] #1d array
                self.w = self.w + np.transpose(update[:,np.newaxis]) #tranpose to match weight's shape
            self.iter += 1

When I visualize the model

pla1 = PLA(2)
pla1.fitModel(stacked_x, y)
#plot colored points
for i in range(0, len(x1)):
    if (y[i] == -1):
        plt.plot(x1[i], x2[i], 'ro')
    if (y[i] == 1):
        plt.plot(x1[i], x2[i], 'bo')
plt.plot(x1, (-pla1.w[0][0] - pla1.w[0][1]*x1)/(pla1.w[0][1]), 'g-', label = "PLA")
plt.plot(x1, (-intercept - a*x1)/b, 'k-', label = "f(x)")
plt.xlabel("x1")
plt.ylabel("x2")
plt.legend()

The line I got from the perceptron algorithm is incorrect

enter image description here

This is another run with different data parameters and sample size (n = 30)

enter image description here

I tried printing out the update at each iteration and it was working as I intended. I'm not sure what is causing my algorithm to stop even though there're still misclassified points. I've been stucked on this for a few day. I really appreciate any input.

1

There are 1 answers

2
Muhammed Yunus On

I've amended the code and it works now. I separated out the coefficients into a weight term and a bias term. I am not sure about the way you were updating the coefficients, so I changed that part to ensure that the updates occur for each instance.

enter image description here

#numpoint
n = 300
#f(x) = w0 + ax1 + bx2
#then if f(x) = 0
#x2 = (-w0 - ax1)/b 
intercept = 30
a = 4
b = 2
#generate random points from 0 - 20
x1 = np.random.uniform(-20, 20, n) #return a np array
x2 = np.random.uniform(-20, 20, n)
y = []
#plot f(x)
plt.plot(x1, (-intercept - a*x1)/b, 'k--', label='ground truth') 
plt.ylabel("x2")
plt.xlabel("x1")

#plot colored points
for i in range(0, len(x1)):
    f = intercept + a * x1[i] + b * x2[i]
    if (f <= 0):
        plt.scatter(x1[i], x2[i], c='tab:red')
        y.append(-1)
    if (f > 0):
        plt.scatter(x1[i], x2[i], c='tab:blue')
        y.append(1)
y = np.array(y)
# Add x0 for threshold
stacked_x = np.row_stack((x1, x2))

class PLA():
    def __init__(self, numPredictors):
        self.w = np.random.rand(numPredictors)
        self.b = 0
        self.numPredictors = numPredictors
        
    def fitModel(self, xData, yData):
        n_errors = np.inf
        while(n_errors):
            n_errors = 0
            for xi, yi in zip(xData.T, yData.reshape(-1, 1)):
                linear = np.dot(xi, self.w) + self.b
                yhat = 1 if (linear > 0) else -1
                
                error = yi - yhat
                self.w = self.w + error * xi
                self.b = self.b + error
                
                if yhat != yi:
                    n_errors += 1 

pla1 = PLA(2)
pla1.fitModel(stacked_x, y)

plt.plot(x1, (-pla1.b - pla1.w[0]*x1)/pla1.w[1], 'g-', alpha=0.5, linewidth=5, label = "PLA")
plt.xlabel("x1")
plt.ylabel("x2")
plt.legend()
plt.gcf().set_size_inches(8, 3)
plt.ylim(-22, 22) #clip y limits