
Fast SpamAssassin Score Learning Tool

Henry Stern
Faculty of Computer Science
Dalhousie University
6050 University Avenue
Halifax, NS  Canada
B3H 1W5
henry@stern.ca

January 8, 2004

1.  WHAT IS IT?

This program is used to compute scores for SpamAssassin rules.  It makes
use of data files generated by the suite of scripts in
spamassassin/masses.  The program outputs the generated scores in a file 
titled 'perceptron.scores'.

The advantage of this program over that of the genetic algorithm (GA)
implementation in spamassassin/masses/craig_evolve.c is that while the GA 
requires several hours to run on high-end machines, the perceptron 
requires only about 15 seconds of CPU time on an Athlon XP 1700+ system.

This makes incremental updates and score personalization practical for the
end-user and gives developers a better idea just how useful a new rule is.

2.  OPTIONS

There are four options that can be passed to the perceptron program.

  -p ham_preference

	This increases the number of non-spam messages in the training 
	set.  It does this by adding 1 + (number of tests hit) * 
	ham_preference instances of non-spam messages to the training set.
	This is intended to reduce false positives by encouraging the
	training program to look at the harder-to-clasify ham messages 
	more often.  By default, this parameter is 2.0.

  -e num_epochs

	This parameter sets how many passes the perceptron will make 
	through the training set before terminating.  On each pass, the 
	training set is shuffled and then iterated through.  By default, 
	it will make 15 passes.

  -l learning_rate

	This parameter modifies the learning rate of the perceptron.  The 
	error gradient is computed for each instance.  This program uses a 
	logsig activation function y = (1/(1+exp(-x))), so the error 
	gradient is computed as E(x) = y*(1-y)*(is_spam - y).  For 
	each instance and score hit in the training set, the scores are 
	modified by adding E(x) / (number of tests hit + 1) * 
	learning_rate.  The default value for this is 2.0, but it can be 
	whatever you want.

  -w weight_decay

	To prevent the scores from getting too high (or even forcing them 
	to if you want), before each epoch, the scores and network bias
	are multiplied by the weight decay.  This is off by default (1.0), 
	but it can be useful.

3.  HOW DOES IT WORK?

This program implements the "Stochastic Gradient Descent" method of 
training a neural network.  It uses a single perceptron with a logsig 
activation function and maps the weights to SpamAssassin score space.

The perceptron is the simplest form of neural network.  It consists of a 
transfer function and an activation function.  Together, they simulate the 
average firing rate of a biological neuron over time.

The transfer function is the sum of the product of weights and inputs.  
It simulates the membrane potential of a neuron.  When the accumulated
charge on the membrane exceeds a certain threshold, the neuron fires,
sending an electrical impulse down the axon. This implementation uses a
linear transfer function:

[1]	f(x) = bias + sum_{i=1}^{n} w_i * x_i

This is quite similar to how the SpamAssasin score system works.  If you 
set the bias to be 0, w_i to be the score for rule i and x_i to be whether 
or not rule i is activated by a given message, the transfer function will 
return the score of the message.  

The activation function simulates the electrical spike travelling down the 
axon.  It takes the output of the transfer function as input and applies 
some sort of transformation to it.  This implementation uses a logsig 
activation function:

[2]	y(x) = 1 / (1 + exp(-f(x)))

This non-linear function constrains the output of the transfer function 
between 0 and 1.  When plotted, it looks somewhat S-shaped with vertical 
asymptotes at 0 as x approaches -infinity and 1 as x approaches infinity.  
The slope of the function is greatest at x=0 and tapers off as it 
approaches the asymptotes.

Lastly, the performance of the perceptron needs to be measured using an 
error function.  Two error functions are commonly used:  mean squared 
error and entropic error.  By default, this implementation uses mean 
squared error but entropic error may be substituted by adding a compiler 
directive.

The most common method of training neural networks is called gradient
descent.  It involves iteratively tuning the parameters of the network so
that the mean error rate always decreases.  This is done by finding the
direction of steepest descent down the "error gradient," reducing the
value of the error function for the next iteration of the algorithm.

If the transfer function and activation function are both differentiable,
the error gradient of a neural network can be calculated with respect to
the weights and bias.  Without getting into calculus, the error gradient 
for a perceptron with a linear transfer function, logsig activation 
function and mean squared error function is:

[3]	E(x) = y(x) * (1-y(x)) * (y_expected - y(x))

The weights are updated using the function:

[4]	w_i = w_i + E(x) * x_i * learning_rate

Since the SpamAssassin rule hits are sparse, the basic gradient descent 
algorithm is impractical.  This implementation uses a variation called 
"Stochastic gradient descent."  Instead of doing one batch update per 
epoch, the training set is randomly walked through, doing incremental 
updates.  In addition, in this implementation, the learning rate is 
modified by the number of rule hits for a given training instance.  
Together, these allow good weights to be computed for 
infrequently-occurring rules.

Once the perceptron has finished running, the weights are converted to 
scores and exported using the familiar file format.  Weights are converted 
to scores using this function:

[5]	score(weight) = -threshold * weight / bias

4.  ACKNOWLEDGEMENTS

I would like to thank my PhD supervisor, Michael Shepherd, for not getting 
mad at me while I worked on this.  I'd also like to thank Thomas 
Trappenberg for his invaluable assistance while I was tweaking the 
performance of the learning algorithm.  I would like to thank Daniel 
Quinlan, Justin Mason and Theo Van Dinter for their valuable input and 
constructive criticism.

--
hs
8/1/2004
