'''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))