import numpy as np import time import os import matplotlib.pyplot as plt import uctc.nn as nn from utils import parameter_data, Dataset use_graphics = False class PerceptronModel(object): def __init__(self, dimensions): """ Initialize a new Perceptron instance. A perceptron classifies data points as either belonging to a particular class (+1) or not (-1). `dimensions` is the dimensionality of the data. For example, dimensions=2 would mean that the perceptron must classify 2D points. """ self.w = nn.Parameter(parameter_data(dimensions, 1)) def get_weights(self): """ Return a Parameter instance with the current weights of the perceptron. """ return self.w.data() def run(self, x): """ Calculates the score assigned by the perceptron to a data point x. Inputs: x: a node with shape (1 x dimensions) Returns: a node containing a single number (the score) """ "*** YOUR CODE HERE ***" out = nn.Linear(x, self.w) return out def get_prediction(self, x): """ Calculates the predicted class for a single data point `x`. Returns: 1 or -1 """ "*** YOUR CODE HERE ***" score = self.run(x).data()[0] # score = np.array(x.data()).dot(np.array(self.w.data())) if score >= 0: return 1 else: return -1 def train(self, dataset): """ Train the perceptron until convergence. """ "*** YOUR CODE HERE ***" batch_size = 1 while True: converged = True for x, y in dataset.iterate_once(batch_size): prediction = self.get_prediction(x) x = np.array(x.data(), dtype=np.float32) y = int(y.data()[0]) # assert 0 if prediction != y: # print(prediction, y) converged = False self.w.update(nn.pyarray_to_tensor(x), -y) # time.sleep(0.01) if converged: break class PerceptronDataset(Dataset): def __init__(self, model: PerceptronModel): points = 500 x = np.hstack([np.random.randn(points, 2), np.ones((points, 1))]) y = np.where(x[:, 0] + 2 * x[:, 1] - 1 >= 0, 1.0, -1.0) super().__init__(x, np.expand_dims(y, axis=1)) self.model = model self.epoch = 0 limits = np.array([-3.0, 3.0]) if use_graphics: fig, ax = plt.subplots(1, 1) ax.set_xlim(limits) ax.set_ylim(limits) positive = ax.scatter(*x[y == 1, :-1].T, color="red", marker="+") negative = ax.scatter(*x[y == -1, :-1].T, color="blue", marker="_") line, = ax.plot([], [], color="black") text = ax.text(0.03, 0.97, "", transform=ax.transAxes, va="top") ax.legend([positive, negative], [1, -1]) plt.show(block=False) self.fig = fig self.line = line self.text = text self.limits = limits self.last_update = time.time() def iterate_once(self, batch_size): self.epoch += 1 for i, (x, y) in enumerate(super().iterate_once(batch_size)): yield x, y if time.time() - self.last_update > 0.001: w = self.model.get_weights() limits = self.limits print(f"epoch: {self.epoch}\npoint: {i * batch_size + 1}/{len(self.x)}\nweights: {w}") if use_graphics: if w[1] != 0: self.line.set_data(limits, (-w[0] * limits - w[2]) / w[1]) elif w[0] != 0: self.line.set_data(np.full(2, -w[2] / w[0]), limits) else: self.line.set_data([], []) self.text.set_text( f"epoch: {self.epoch}\npoint: {i * batch_size + 1}/{len(self.x)}\nweights: {w}") self.fig.canvas.draw_idle() self.fig.canvas.start_event_loop(1e-3) self.last_update = time.time() model = PerceptronModel(3) dataset = PerceptronDataset(model) model.train(dataset)