-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathproblem1.py
158 lines (118 loc) · 6.46 KB
/
problem1.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
"""
This code implements a perceptron algorithm (PLA).
First, we visualise the dataset which contains 2 features. We can see that the dataset can be clearly separated by drawing a straight line between them. The goal is to write an algorithm that finds that line and classifies all of these data points correctly.
The output file (e.g. 'output1_f.csv') contains the values of w1, w2 and b which define the 'threshold' line. The last row will be the most accurate one. Each time it goes through each of the examples in 'input1.csv', it adds a new line to the output file containing a comma-separated list of the weights w_1, w_2, and b (bias) in that order.
Upon convergence, the program stops, and the final values of w_1, w_2, and b are printed to the output file (output1.csv). This defines the decision boundary that your PLA has computed for the given dataset.
Note: When implementing your PLA, in case of tie (sum of w_jx_ij = 0), please follow the lecture note and classify the datapoint as -1.
Ensure this file can be executed as:
$ python3 problem1.py input1.csv output1.csv
The code includes plotting functions. However those are disabled when executing the code from the command line in the format specified immediately above.
"""
# builtin modules
import os
import psutil
import requests
import sys
# 3rd party modules
import pandas as pd
import numpy as np
import plotly.graph_objects as go
class problem1:
def get_data(source_file, names_in:list = ['feature1','feature2','labels']):
# Define input and output filepaths
input_path = os.path.join(os.getcwd(),'datasets','in', source_file)
# Read input data
df = pd.read_csv(input_path, names=names_in)
return df
def perceptron_classify(df, n:int = 200, names_in:list = ['feature1','feature2','labels']):
"""
1. set b = w = 0
2. for N iterations, or until weights do not change
(a) for each training example xᵏ with label yᵏ
i. if yᵏ — f(xᵏ) = 0, continue
ii. else, update wᵢ, △wᵢ = (yᵏ — f(xᵏ)) xᵢ
"""
# transform the dataframe to an array
data = np.asmatrix(df, dtype = 'float64')
# get the first two columns as pairs of values
features = data[:, :-1]
# get the last column
labels = data[:, -1]
# assign zero weight as a starting point to features and labels
w = np.zeros(shape=(1, features.shape[1]+1)) #e.g. array([0., 0., 0.])
w_ = np.empty(shape=[0,3]) # declare w_ as an empty matrix of same shape as w
for iteration in range(0,n):
for x, label in zip(features, labels):
x = np.insert(x, 0, 1) # add a column of 1s to represent w0
f = np.dot(w, x.transpose()) # a scalar
if f * label <= 0:
w += (x * label.item(0,0)).tolist() # because label comes from being a matrix (matrix([[1.]])) and needs to be converted to scalar
else:
iteration = n
w_ = np.vstack((w_, w))
return (w_, w_[-1])
def plot_results(df, weights, names_in:list = ['feature1','feature2','labels']):
"""
Plot the Perceptron classifier, from the following inputs:
- source_file: csv file with the input samples
- weights: from perceptron_classify function
- names_in: a list of the names of the columns (headers) in the input df
returns:
- a plot of the figure in the default browser, and
- a PNG version of the plot to the "images" project directory
"""
# Create the figure for plotting the initial data
fig = go.Figure(data=go.Scatter(x=df[names_in[0]],
y=df[names_in[1]],
mode='markers',
marker=dict(
color=df[names_in[2]],
colorscale='Viridis',
line_width=1,
size = 16),
text=df[names_in[2]], # hover text goes here
showlegend=False)) # turn off legend only for this item
# Create the 1D array for X values from the first feature; this is just to be able to plot a line
# within the space defined by the two features explored
X = np.linspace(0, max(df[names_in[0]].max(),df[names_in[1]].max()))
# Vector Y will calculated from the weights, w1, w2, the bias, b, and the value of X in its 1D linear space
Y = []
for b, w1, w2 in [weights]: #(matrix.tolist()[0] for matrix in weights):
for x in X:
if w2 == 0:
y = 0.0
else:
y = (-(b / w2) / (b / w1))* x + (-b / w2) # per the equation of a line, e.g. C = Ax + By
Y.append(y)
# Add the threshold line to the plot
fig.add_trace(go.Scatter(x=X, y=Y,
mode= 'lines',
name = 'Threshold'))
# Give the figure a title
fig.update_layout(title='Perceptron Algorithm | Problem 1')
# Show the figure, by default will open a browser window
fig.show()
# export plot to png file to images directory
# create an images directory if not already present
if not os.path.exists("images"):
os.mkdir("images")
## write the png file with the plot/figure
return fig.write_image("images/fig1.png")
def write_csv(filename, weights):
# write the outputs csv file
filepath = os.path.join(os.getcwd(),'datasets','out', filename)
dataframe = pd.DataFrame(data=weights, columns=('b','w1','w2'))
# reorder the columns in the dataframe in accordance with assignment
order = [1,2,0] # setting column's order, 'b' goes as first column followed by weights
dataframe = dataframe[[dataframe.columns[i] for i in order]]
dataframe.to_csv(filepath)
return print("New Outputs file saved to: <<", filename, ">>", sep='', end='\n')
def main():
in_data = 'input1.csv'
out_data = 'output1.csv'
df = get_data(in_data)
(w_, w) = perceptron_classify(df)
plot_results(df, w)
write_csv(out_data, w_)
if __name__ == '__main__':
main()