Permalink
Browse files

Updated the source comments to work with Haddock.

  • Loading branch information...
1 parent 10d4679 commit 9e3c250ecdee714fd990a4a8b86a8d647e899004 @andrewdougherty committed Nov 19, 2010
Showing with 43 additions and 27 deletions.
  1. +43 −27 SVM.hs
View
70 SVM.hs
@@ -28,36 +28,42 @@ module SVM (DataSet (..), SVMSolution (..), KernelFunction (..), SVM (..), LSSVM
reciprocalKernelFunction, radialKernelFunction, linearKernelFunction, splineKernelFunction,
polyKernelFunction, mlpKernelFunction) where
- import Data.Array.Unboxed -- ^ Unboxed arrays are used for better performance.
- import Data.List (foldl') -- ^ foldl' gives better performance than sum
+ import Data.Array.Unboxed -- Unboxed arrays are used for better performance.
+ import Data.List (foldl') -- foldl' gives better performance than sum
- -- | Each data set is a list of vectors (the points) and a corresponding list of values.
+ -- |Each data set is a list of vectors and values which are training points of the form f(x) = y forall {x,y}.
data DataSet = DataSet {points::(Array Int [Double]), values::(UArray Int Double)}
- -- | The solution contains the dual weights, the support vectors and the bias.
+ -- |The solution contains the dual weights, the support vectors and the bias.
data SVMSolution = SVMSolution {alpha::(UArray Int Double), sv::(Array Int [Double]), bias::Double}
- -- | The kernel matrix has been implemented as an unboxed array for performance reasons.
+ -- |The kernel matrix has been implemented as an unboxed array for performance reasons.
newtype KernelMatrix = KernelMatrix (UArray Int Double)
- -- | Every kernel function represents an inner product in feature space. The first list is a set of parameters.
+ -- |Every kernel function represents an inner product in feature space. The parameters are:
+ --
+ -- * A list of kernel parameters that can be interpreted differently by each kernel function.
+ --
+ -- * The first point in the inner product.
+ --
+ -- * The second point in the inner product.
newtype KernelFunction = KernelFunction ([Double] -> [Double] -> [Double] -> Double)
-- Some common kernel functions (these are called many times, so they need to be fast):
- -- | The reciprocal kernel is the result of exponential basis functions, exp(-k*(x+a)). The inner product
+ -- |The reciprocal kernel is the result of exponential basis functions, exp(-k*(x+a)). The inner product
-- is an integral over all k >= 0.
reciprocalKernelFunction :: [Double] -> [Double] -> [Double] -> Double
reciprocalKernelFunction (a:as) (x:xs) (y:ys) = (reciprocalKernelFunction as xs ys) / (x + y + 2*a)
reciprocalKernelFunction _ _ _ = 1
- -- | This is the kernel when radial basis functions are used.
+ -- |This is the kernel when radial basis functions are used.
radialKernelFunction :: [Double] -> [Double] -> [Double] -> Double
radialKernelFunction (a:as) x y = exp $ (cpshelp 0 x y) / a
where cpshelp !accum (x:xs) (y:ys) = cpshelp (accum + (x-y)**2) xs ys
cpshelp !accum _ _ = negate accum
- -- | This is a simple dot product between the two data points, corresponding to a featureless space.
+ -- |This is a simple dot product between the two data points, corresponding to a featureless space.
linearKernelFunction :: [Double] -> [Double] -> [Double] -> Double
linearKernelFunction (a:as) (x:xs) (y:ys) = x * y + linearKernelFunction as xs ys
linearKernelFunction _ _ _ = 0
@@ -71,40 +77,47 @@ module SVM (DataSet (..), SVMSolution (..), KernelFunction (..), SVM (..), LSSVM
polyKernelFunction :: [Double] -> [Double] -> [Double] -> Double
polyKernelFunction (a0:a1:as) x y = (a0 + linearKernelFunction as x y)**a1
+ -- |Provides a solution similar to neural net.
mlpKernelFunction :: [Double] -> [Double] -> [Double] -> Double
mlpKernelFunction (a0:a1:as) x y = tanh (a0 * linearKernelFunction as x y - a1)
- {- | A support vector machine (SVM) can estimate a function based upon some training data. Instances of this
+ {- |A support vector machine (SVM) can estimate a function based upon some training data. Instances of this
class need only implement the dual cost and the kernel function. Default implementations are given
for finding the SVM solution, for simulating a function and for creating a kernel matrix from a set of
training points. All SVMs should return a solution which contains a list of the support vectors and their
dual weigths. dcost represents the coefficient of the dual cost function. This term gets added to the
diagonal elements of the kernel matrix and may be different for each type of SVM. -}
class SVM a where
+ {- | Creates a 'KernelMatrix' from the training points in the 'DataSet'. If @kf@ is the 'KernelFunction'
+ then the elements of the kernel matrix are given by @K[i,j] = kf x[i] x[j]@, where the @x[i]@ are taken
+ from the training points. The kernel matrix is symmetric and positive semi-definite.Only the bottom half
+ of the kernel matrix is stored.-}
createKernelMatrix :: a -> (Array Int [Double]) -> KernelMatrix
+ -- | The derivative of the cost function is added to the diagonal elements of the kernel matrix. This
+ -- places a cost on the norm of the solution, which helps prevent overfitting of the training data.
dcost :: a -> Double
+ -- | This function provides access to the 'KernelFunction' used by the 'SVM'.
evalKernel :: a -> [Double] -> [Double] -> Double
+ {- | This function takes an 'SVMSolution' produced by the 'SVM' passed in, and a list of points in the
+ space, and it returns a list of valuues y = f(x), where f is the generating function represented by
+ the support vector solution.-}
simulate :: a -> SVMSolution -> (Array Int [Double]) -> [Double]
+ {- | This function takes a 'DataSet' and feeds it to the 'SVM'. Then it returns the 'SVMSolution' which
+ is the support vector solution for the function which generated the points in the training set. The
+ function also takes values for epsilon and the max iterations, which are used as stopping criteria in the
+ conjugate gradient algorithm.-}
solve :: a -> DataSet -> Double -> Int -> SVMSolution
- {- | The kernel matrix is created by evaluating the kernel function on all of the points in the data set.
- K[i,j] = f x[i] x[j], so K is symmetric and positive semi-definite. Only the bottom half is created.
- The diagonal elements all have gamma added to them as part of solving the problem. -}
createKernelMatrix a x = KernelMatrix matrix
where matrix = listArray (1, dim) [eval i j | j <- indices x, i <- range(1,j)]
dim = ((n+1) * n) `quot` 2
eval i j | (i /= j) = evalKernel a (x!i) (x!j)
| otherwise = evalKernel a (x!i) (x!j) + dcost a
n = snd $ bounds x
- -- | This function takes a set of points and an SVMSolution, representing a function, and evaluates
- -- that function over all of the given points. A list of the values y = f(x) are returned.
simulate a (SVMSolution alpha sv b) points = [(eval p) + b | p <- elems points]
where eval x = mDot alpha $ listArray (bounds sv) [evalKernel a x v | v <- elems sv]
- {- | This function takes a set of points and creates an SVM solution to the problem. The default
- implementation uses a conjugate gradient algorithm to solve for the optimal solution to the
- problem. -}
solve svm (DataSet points values) epsilon maxIter = SVMSolution alpha points b
where b = (mSum v) / (mSum nu)
alpha = mZipWith (\x y -> x - b*y) v nu
@@ -115,17 +128,20 @@ module SVM (DataSet (..), SVMSolution (..), KernelFunction (..), SVM (..), LSSVM
n = snd $ bounds values
kernel = createKernelMatrix svm points
- {- | A least squares support vector machine. The cost represents the relative expense of missing a training
+ {- |A least squares support vector machine. The cost represents the relative expense of missing a training
versus a more complicated generating function. The higher this number the better the fit of the training
set, but at a cost of poorer generalization. The LSSVM uses every training point in the solution and
performs least squares regression on the dual of the problem. -}
- data LSSVM = LSSVM {kf::KernelFunction, cost::Double, params::[Double]}
+ data LSSVM = LSSVM {kf::KernelFunction, -- ^The kernel function defines the feature space.
+ cost::Double, -- ^The cost coefficient in the Lagrangian.
+ params::[Double] -- ^Any parameters needed by the 'KernelFunction'.
+ }
instance SVM LSSVM where
dcost = (0.5 /) . cost
evalKernel (LSSVM (KernelFunction kf) _ params) = kf params
- -- | The conjugate gradient algorithm is used to find the optimal solution. It will run until a cutoff delta
+ -- |The conjugate gradient algorithm is used to find the optimal solution. It will run until a cutoff delta
-- is reached or for a max number of iterations. The type synonym is just there to shorten the type signature.
type CGArray = UArray Int Double
cga :: CGArray -> CGArray -> CGArray -> KernelMatrix -> Double -> Int -> CGArray
@@ -144,7 +160,7 @@ module SVM (DataSet (..), SVMSolution (..), KernelFunction (..), SVM (..), LSSVM
-- The following functions are used internally for all of the linear algebra involving kernel matrices or
-- unboxed arrays of doubles (representing vectors).
- -- | Matrix multiplication between a kernel matrix and a vector is handled by this funciton. Only the bottom
+ -- |Matrix multiplication between a kernel matrix and a vector is handled by this funciton. Only the bottom
-- half of the matrix is stored. This function requires 1 based indices for both of its arguments.
matmult :: KernelMatrix -> (UArray Int Double) -> (UArray Int Double)
matmult (KernelMatrix k) v = listArray (1, d) $ helper 1 1
@@ -155,22 +171,22 @@ module SVM (DataSet (..), SVMSolution (..), KernelFunction (..), SVM (..), LSSVM
| (j < d) = cpsdot (acc + k!n * v!j) (j+1) (n+j)
| otherwise = acc + k!n * v!j
- -- | Scalar multiplication of an unboxed array.
+ -- |Scalar multiplication of an unboxed array.
scalarmult :: Double -> (UArray Int Double) -> (UArray Int Double)
scalarmult = amap . (*)
- -- | A version of zipWith for use with unboxed arrays.
+ -- |A version of zipWith for use with unboxed arrays.
mZipWith :: (Double -> Double -> Double) -> (UArray Int Double) -> (UArray Int Double) -> (UArray Int Double)
mZipWith f v1 v2 = array (bounds v1) [(i, f (v1!i) (v2!i)) | i <- indices v1]
- -- | Sum the elements of an unboxed array.
+ -- |Sum the elements of an unboxed array.
mSum :: (UArray Int Double) -> Double
mSum = foldl' (+) 0 . elems
- -- | Standard dot product of two unboxed arrays.
+ -- |Standard dot product of two unboxed arrays.
mDot :: (UArray Int Double) -> (UArray Int Double) -> Double
mDot = (mSum .) . mZipWith (*)
- -- | Add two unboxed arrays element by element.
+ -- |Add two unboxed arrays element by element.
mAdd :: (UArray Int Double) -> (UArray Int Double) -> (UArray Int Double)
mAdd = mZipWith (+)

0 comments on commit 9e3c250

Please sign in to comment.