'''PerceptronDemo.py ''' # Simple example: the INCLUSIVE OR function: TS1 = [\ [0, 0, 0, 'Q'], [1, 0, 1, 'P'], [1, 1, 0, 'P'], [1, 1, 1, 'P'] ] # Another example: TS2 = [\ [0, 184 , 142 , 'P'], [1, 290 , 176 , 'P'], [2, 133 , 213 , 'P'], [3, 467 , 268 , 'Q'], [4, 564 , 308 , 'Q'], [5, 545 , 363 , 'Q'], [6, 410 , 408 , 'P'] ] # Select your example by uncommenting the one desired: #TRAINING_SET = TS1; SCALE=400; BIAS=0.8; PLOTMARGINS = 50 TRAINING_SET = TS2; SCALE=1; BIAS=500; PLOTMARGINS = 0 NEXAMPLES = len(TRAINING_SET) def isPositive(example): return example[3]=='P' def isNegative(example): return example[3]=='Q' ''' Now let's train a perceptron to detect P's.''' W = [1, 0.0, 0.0] # Initial weights # The parameter c is the "training rate". It must be small enough that # fine tuning of the weights is possible, but large enough that it doesn't # take millions of iterations for training to converge. # Do not change it here, because it is reset in the training procedure. # It is gradually reduced during the training. C = 1.0 def perceptron_classify(X, W): '''Classify the input vector X using the weight vector W and threshold 0.''' sum = 0.0 for i in range(len(X)): sum += X[i]*W[i] if sum > 0: return 1 else: return 0 def updateWeights(W, f, X): '''Apply the perceptron learning rule. The fraction f is either c or -c, where c is the learning rate.''' for i in range(len(W)): W[i] += f*X[i] #print "Weights are now: "+str(W) # Convert the training set into format required by the algorithm. # Each X vector uses the meaningful features of an example. # Also, we "prepend" a negative BIAS term onto each X vector. # The BIAS should be of the same order of magnitude as the features. XVECTORS = map(lambda e: [-BIAS]+e[1:-1], TRAINING_SET) WIN = None # User interface window K = 0 # Number of training iterations so far NUM_SUCCESSES_IN_A_ROW = 0 BEST_SO_FAR = 0 # Max number of training examples correctly # classified by one weight vector so far. CONVERGED = False def initialize(): '''Set or reset the program to the completely untrained state.''' global W, K, NUM_SUCCESSES_IN_A_ROW, BEST_SO_FAR, CONVERGED W = [1, 0.0, 0.0] K = 0 NUM_SUCCESSES_IN_A_ROW = 0 BEST_SO_FAR = 0 CONVERGED = False import time def train(max_iterations, verbose=True): global NEXAMPLES, XVECTORS, W, THETA, C, PP, win, K global NUM_SUCCESSES_IN_A_ROW, BEST_SO_FAR, CONVERGED printlimit = 10 # A limit on the amount of printing during training. i = 0 # For counting how many iterations have been done # during THIS CALL to 'train'. while i BEST_SO_FAR: BEST_SO_FAR = NUM_SUCCESSES_IN_A_ROW if NUM_SUCCESSES_IN_A_ROW==NEXAMPLES: print "After "+ str(K)+" iterations, training was successful. " CONVERGED = True return True else: # This example was misclassified NUM_SUCCESSES_IN_A_ROW = 0 # Back to "square one." if trial_result==1 and isNegative(example): # It was a false positive, so lower the weights. updateWeights(W, -C, X) else: # It was a false negative, so raise the weights. updateWeights(W, C, X) # Give some information about how the training is going. if verbose: print "Misclassified example: "+str(example) global WIN; WIN.repaint() # Ask for the display to be updated. time.sleep(0.2) # Wait a moment so the UI can be updated. if (K % 100) == 99: print ".", # Show some progress during long runs. C = 1000.0 / (1000 + K) # Lower the learning rate slowly. if (K % 1000) == 999: print "" if not CONVERGED and verbose: print "Training did not converge after using "+str(K)+" iterations." print "Best it could do was "+str(BEST_SO_FAR)+" out of "+str(NEXAMPLES) return False '''Now for defining the user interface...''' from javax.swing import JFrame, JPanel, JButton from java.awt import BorderLayout, Dimension, Color, Font from java.lang import Runnable from java.util.concurrent import ExecutionException from javax.swing import SwingWorker, SwingUtilities from java.awt import GridLayout as awtGridLayout WIDTH = HEIGHT = 600 DARK_GREEN = Color(0, 192, 50) DARK_RED = Color(192, 0, 50) PLOT_FONT = Font("Helvetica", Font.PLAIN, 24) class PlotPanel(JPanel): def __init__(self): print "Creating instance of PlotPanel" self.win = JFrame("PerceptronDemo Plotting Window") # Create a window. global WIN; WIN = self.win # Make it available globally. self.win.setSize(Dimension(WIDTH, HEIGHT)) # Next add the new PlotPanel to the center of the window. self.win.getContentPane().add(self, BorderLayout.CENTER) self.trainButton1 = JButton("Take One Step of Training") self.win.getContentPane().add(self.trainButton1, BorderLayout.NORTH) self.trainButton1.actionPerformed = lambda e: self.doCompute1() self.trainButton2 = JButton("Toggle Continuous Training") self.win.getContentPane().add(self.trainButton2, BorderLayout.SOUTH) self.trainButton2.actionPerformed = lambda e: self.doCompute2() self.win.setVisible(True) self.bgTraining=None def doCompute1(self): '''Callback for the top button.''' cs = OneStepOfTraining() cs.execute() def doCompute2(self): '''Callback for the bottom button.''' if self.bgTraining==None: self.startTraining() else: self.stopTraining() cs.execute() def startTraining(self): self.bgTraining = BackgroundTraining() self.trainButton2.setText("Stop training") self.bgTraining.execute() # start up this thread def stopTraining(self): print "Stopping training" self.trainButton2.setText("Resume continuous training") self.bgTraining.cancel(True) self.bgTraining = None def paintComponent(self, g): global TRAINING_SET, NEXAMPLES, SCALE, W, T, HEIGHT global PLOTMARGINS, K, DARK_GREEN, DARK_RED, PLOT_FONT m=PLOTMARGINS message = str(K)+" training steps have been taken." g.setFont(PLOT_FONT) g.drawString(message, 50, 50) # Show all the points in the training set: for i in range(NEXAMPLES): letter = TRAINING_SET[i][3] if letter=='P': g.setColor(DARK_GREEN) else: g.setColor(DARK_RED) x = int(SCALE * TRAINING_SET[i][1])+m y = int(SCALE * TRAINING_SET[i][2])+m r = 5 # radius of ovals g.fillOval(x-r, y-r, 2*r, 2*r) g.drawString(letter, x+2*r, y+2*r) # Now plot the line implied by the current weight vector... g.setColor(Color.blue) points = clip(W, -m, -m, WIDTH-m, HEIGHT-m) if len(points)==2: (x1, y1) = points[0] (x2, y2) = points[1] g.drawLine(int(x1)+m,int(y1)+m,int(x2)+m,int(y2)+m) def clip(L, left, bottom, right, top): '''Compute the portion of the line L that lies within the rectangle bounded by left, bottom, right, and top.''' global SCALE # magnification desired before clipping. global BIAS # needed in computing the intercept of the line. # L represents a line: # a x + b y + c = 0; Let's let a = L1 and b = L2 # and c = -L0 * blow_up_factor: a = L[1]; b = L[2]; c = -L[0]*BIAS*SCALE # Show the line in two forms of equations: # First, the abc form. print "Line: a,b,c = "+str([a, b, c]) # Second, the slope-and-intercept form. if b != 0: slope = - a / b else: slope = "undefined" if b != 0: intercept = - c / b else: intercept = "undefined" print "Line: y = "+str(slope)+" x + "+str(intercept) # Our job is to find the intersections of this line with the sides of a box. # There will usually be either no intersections or 2 intersections, but in theory # there could be one intersection (at a corner) or an infinity of intersections (along a side). # We will handle the last two cases as Zero intersections, taking liberty with the inequalities involved. goodPoints = [] # Left side y value: if b!=0: y = (a*left + c) / (-b) if y > bottom and y < top: goodPoints.append((left, y)) y = (a*right + c) / (-b) if y > bottom and y < top: goodPoints.append((right, y)) if a!=0: x = (b*bottom + c) / (-a) if x > left and x < right: goodPoints.append((x, bottom)) x = (b*top + c) / (-a) if x > left and x < right: goodPoints.append((x, top)) #print "in clip: goodPoints = "+str(goodPoints) return goodPoints def testClip(): '''This is only used during development but is included for whatever didactic value it might have.''' W = [-1, 1, 1] print clip(W, -2, -2, 2, 2) W = [-10, 0, 1] print clip(W, -2, -2, 2, 2) W = [0, 1, 0] print clip(W, -2, -2, 2, 2) W = [-200, 0, 1] print clip(W, 0, 0, 500, 500) #testClip() class OneStepOfTraining(SwingWorker): def __init__(self): print "Creating the Training Run instance" SwingWorker.__init__(self) def doInBackground(self): global W, K train(1) print "K="+str(K)+", W = "+str(W) return def done(self): global WIN WIN.repaint() try: self.get() #close frame if abnormal completion except ExecutionException, e: hide(0) print e.getCause() class BackgroundTraining(SwingWorker): def __init__(self): print "Creating the Training Run instance" SwingWorker.__init__(self) def doInBackground(self): global W, K, CONVERGED print "Beginning Training Run" while not self.isCancelled() and not CONVERGED: try: NS = 1000 train(NS, verbose=False) print str(NS)+" training steps requested. Now finished or converged." print "Total number of steps so far: K="+str(K)+"; Current weights: W = "+str(W) except e: print "There was an exception during doInBackground. " + str(e) time.sleep(1.0) def done(self): try: self.get() #close frame if abnormal completion except ExecutionException, e: hide(0) print e.getCause() class Runnable(Runnable): def __init__(self, runMethod): self._runMethod = runMethod def run(self): self._runMethod() initialize() SwingUtilities.invokeLater(Runnable(PlotPanel))