som 7.0.1 → 7.2.0
raw patch · 4 files changed
+87/−131 lines, 4 files
Files
- som.cabal +1/−1
- src/Data/Datamining/Clustering/DSOMInternal.hs +4/−4
- src/Data/Datamining/Clustering/SOM.hs +2/−6
- src/Data/Datamining/Clustering/SOMInternal.hs +80/−120
som.cabal view
@@ -1,5 +1,5 @@ name: som-version: 7.0.1+version: 7.2.0 synopsis: Self-Organising Maps description: A Kohonen Self-organising Map (SOM) maps input patterns onto a regular grid (usually two-dimensional) where each
src/Data/Datamining/Clustering/DSOMInternal.hs view
@@ -114,8 +114,8 @@ G.Index (GM.BaseGrid gm p) ~ G.Index (gm p)) => DSOM gm t p -> p -> DSOM gm (G.Index (gm p)) p justTrain s p = trainNeighbourhood s bmu p- where ds = GM.toList . GM.map (p `difference`) . sGridMap $ s- bmu = fst . minimumBy (comparing snd) $ ds+ where ds = GM.toList . GM.map (p `difference`) $ sGridMap s+ bmu = fst $ minimumBy (comparing snd) ds instance (GM.GridMap gm p, k ~ G.Index (GM.BaseGrid gm p), Pattern p,@@ -126,11 +126,11 @@ toList = GM.toList . sGridMap numModels = G.tileCount . sGridMap models = GM.elems . sGridMap- differences s p = GM.toList . GM.map (p `difference`) . sGridMap $ s+ differences s p = GM.toList . GM.map (p `difference`) $ sGridMap s trainBatch s = foldl' justTrain s reportAndTrain s p = (bmu, ds, s') where ds = differences s p- bmu = fst . minimumBy (comparing snd) $ ds+ bmu = fst $ minimumBy (comparing snd) ds s' = trainNeighbourhood s bmu p
src/Data/Datamining/Clustering/SOM.hs view
@@ -44,13 +44,9 @@ -- * Deconstruction toGridMap, -- * Advanced control- trainNeighbourhood,- incrementCounter,- counter,- setCounter+ trainNeighbourhood ) where import Data.Datamining.Clustering.SOMInternal (SOM(..),- DecayingGaussian(..), toGridMap, trainNeighbourhood, incrementCounter,- counter, setCounter)+ DecayingGaussian(..), toGridMap, trainNeighbourhood)
src/Data/Datamining/Clustering/SOMInternal.hs view
@@ -26,9 +26,20 @@ import GHC.Generics (Generic) import Prelude hiding (lookup) +-- | A function used to adjust the models in a classifier. class LearningFunction f where type LearningRate f- rate :: f -> Int -> Int -> (LearningRate f)+ -- | @'rate' f t d@ returns the learning rate for a node.+ -- The parameter @f@ is the learning function.+ -- The parameter @t@ indicates how many patterns (or pattern+ -- batches) have previously been presented to the classifier.+ -- Typically this is used to make the learning rate decay over time.+ -- The parameter @d@ is the grid distance from the node being+ -- updated to the BMU (Best Matching Unit).+ -- The output is the learning rate for that node (the amount by+ -- which the node's model should be updated to match the target).+ -- The learning rate should be between zero and one.+ rate :: f -> LearningRate f -> LearningRate f -> LearningRate f -- | A typical learning function for classifiers. -- @'DecayingGaussian' r0 rf w0 wf tf@ returns a bell curve-shaped@@ -47,25 +58,23 @@ -- -- where << means "is much smaller than" (not the Haskell @<<@ -- operator!)-data DecayingGaussian a = DecayingGaussian a a a a Int+data DecayingGaussian a = DecayingGaussian a a a a a deriving (Eq, Show, Generic) -instance (Floating a, Fractional a, Num a) =>- LearningFunction (DecayingGaussian a) where+instance (Floating a, Fractional a, Num a)+ => LearningFunction (DecayingGaussian a) where type LearningRate (DecayingGaussian a) = a- rate (DecayingGaussian r0 rf w0 wf tf) t d = r * exp (-(d'*d')/(w*w))- where a = t'/tf'+ rate (DecayingGaussian r0 rf w0 wf tf) t d = r * exp (-(d*d)/(w*w))+ where a = t/tf r = r0 * ((rf/r0)**a) w = w0 * ((wf/w0)**a)- t' = fromIntegral t- tf' = fromIntegral tf- d' = fromIntegral d -- | A learning function that only updates the BMU and has a constant -- learning rate. data StepFunction a = StepFunction a deriving (Eq, Show, Generic) -instance (Fractional a) => LearningFunction (StepFunction a) where+instance (Fractional a, Eq a)+ => LearningFunction (StepFunction a) where type LearningRate (StepFunction a) = a rate (StepFunction r) _ d = if d == 0 then r else 0.0 @@ -90,52 +99,63 @@ -- just return an @error@). It would be problematic to implement -- them because the input SOM and the output SOM would have to have -- the same @Metric@ type.-data SOM f gm k p = SOM+data SOM f t gm k p = SOM {- sGridMap :: gm p,- sLearningFunction :: f,- sCounter :: Int+ -- | Maps patterns to tiles in a regular grid.+ -- In the context of a SOM, the tiles are called "nodes"+ gridMap :: gm p,+ -- | The function used to update the nodes.+ learningFunction :: f,+ -- | A counter used as a "time" parameter.+ -- If you create the SOM with a counter value @0@, and don't+ -- directly modify it, then the counter will represent the number+ -- of patterns that this SOM has classified.+ counter :: t } deriving (Eq, Show, Generic) -instance (F.Foldable gm) => F.Foldable (SOM f gm k) where- foldr f x g = F.foldr f x (sGridMap g)+instance (F.Foldable gm) => F.Foldable (SOM f t gm k) where+ foldr f x g = F.foldr f x (gridMap g) -instance (G.Grid (gm p)) => G.Grid (SOM f gm k p) where- type Index (SOM f gm k p) = G.Index (gm p)- type Direction (SOM f gm k p) = G.Direction (gm p)- indices = G.indices . sGridMap- distance = G.distance . sGridMap- neighbours = G.neighbours . sGridMap- contains = G.contains . sGridMap- viewpoint = G.viewpoint . sGridMap- directionTo = G.directionTo . sGridMap- tileCount = G.tileCount . sGridMap- null = G.null . sGridMap- nonNull = G.nonNull . sGridMap+instance (G.Grid (gm p)) => G.Grid (SOM f t gm k p) where+ type Index (SOM f t gm k p) = G.Index (gm p)+ type Direction (SOM f t gm k p) = G.Direction (gm p)+ indices = G.indices . gridMap+ distance = G.distance . gridMap+ neighbours = G.neighbours . gridMap+ contains = G.contains . gridMap+ viewpoint = G.viewpoint . gridMap+ directionTo = G.directionTo . gridMap+ tileCount = G.tileCount . gridMap+ null = G.null . gridMap+ nonNull = G.nonNull . gridMap -instance (F.Foldable gm, GM.GridMap gm p, G.Grid (GM.BaseGrid gm p)) => GM.GridMap (SOM f gm k) p where- type BaseGrid (SOM f gm k) p = GM.BaseGrid gm p- toGrid = GM.toGrid . sGridMap- toMap = GM.toMap . sGridMap+instance (F.Foldable gm, GM.GridMap gm p, G.Grid (GM.BaseGrid gm p))+ => GM.GridMap (SOM f t gm k) p where+ type BaseGrid (SOM f t gm k) p = GM.BaseGrid gm p+ toGrid = GM.toGrid . gridMap+ toMap = GM.toMap . gridMap mapWithKey = error "Not implemented"- adjustWithKey f k s = s { sGridMap=gm' }- where gm = sGridMap s+ adjustWithKey f k s = s { gridMap=gm' }+ where gm = gridMap s gm' = GM.adjustWithKey f k gm currentLearningFunction- :: (LearningFunction f, LearningRate f ~ Metric p)- => SOM f gm k p -> (Int -> Metric p)-currentLearningFunction s = (rate . sLearningFunction $ s) (sCounter s)+ :: (LearningFunction f, Metric p ~ LearningRate f,+ Num (LearningRate f), Integral t)+ => SOM f t gm k p -> (LearningRate f -> Metric p)+currentLearningFunction s+ = rate (learningFunction s) (fromIntegral $ counter s) -- | Extracts the grid and current models from the SOM.-toGridMap :: GM.GridMap gm p => SOM f gm k p -> gm p-toGridMap = sGridMap+-- A synonym for @'gridMap'@.+toGridMap :: GM.GridMap gm p => SOM f t gm k p -> gm p+toGridMap = gridMap adjustNode- :: (Pattern p, G.Grid g, k ~ G.Index g) =>- g -> (Int -> Metric p) -> p -> k -> k -> p -> p+ :: (Pattern p, G.Grid g, k ~ G.Index g, Num t) =>+ g -> (t -> Metric p) -> p -> k -> k -> p -> p adjustNode g f target bmu k = makeSimilar target (f d)- where d = G.distance g bmu k+ where d = fromIntegral $ G.distance g bmu k -- | Trains the specified node and the neighbourood around it to better -- match a target.@@ -144,100 +164,40 @@ trainNeighbourhood :: (Pattern p, G.Grid (gm p), GM.GridMap gm p, G.Index (GM.BaseGrid gm p) ~ G.Index (gm p), LearningFunction f,- LearningRate f ~ Metric p) =>- SOM f gm k p -> G.Index (gm p) -> p -> SOM f gm k p-trainNeighbourhood s bmu target = s { sGridMap=gm' }- where gm = sGridMap s+ Metric p ~ LearningRate f, Num (LearningRate f), Integral t) =>+ SOM f t gm k p -> G.Index (gm p) -> p -> SOM f t gm k p+trainNeighbourhood s bmu target = s { gridMap=gm' }+ where gm = gridMap s gm' = GM.mapWithKey (adjustNode gm f target bmu) gm f = currentLearningFunction s -incrementCounter :: SOM f gm k p -> SOM f gm k p-incrementCounter s = setCounter (sCounter s + 1) s--counter :: SOM f gm k p -> Int-counter = sCounter--setCounter :: Int -> SOM f gm k p -> SOM f gm k p-setCounter k s = s { sCounter = k }+incrementCounter :: Num t => SOM f t gm k p -> SOM f t gm k p+incrementCounter s = s { counter=counter s + 1} justTrain :: (Ord (Metric p), Pattern p, G.Grid (gm p), GM.GridMap gm (Metric p), GM.GridMap gm p, G.Index (GM.BaseGrid gm (Metric p)) ~ G.Index (gm p), G.Index (GM.BaseGrid gm p) ~ G.Index (gm p), LearningFunction f,- LearningRate f ~ Metric p) =>- SOM f gm k p -> p -> SOM f gm k p+ Metric p ~ LearningRate f, Num (LearningRate f), Integral t) =>+ SOM f t gm k p -> p -> SOM f t gm k p justTrain s p = trainNeighbourhood s bmu p- where ds = GM.toList . GM.map (p `difference`) . sGridMap $ s- bmu = fst . minimumBy (comparing snd) $ ds+ where ds = GM.toList . GM.map (p `difference`) $ gridMap s+ bmu = fst $ minimumBy (comparing snd) ds instance (GM.GridMap gm p, k ~ G.Index (GM.BaseGrid gm p), Pattern p, G.Grid (gm p), GM.GridMap gm (Metric p), k ~ G.Index (gm p), k ~ G.Index (GM.BaseGrid gm (Metric p)), Ord (Metric p),- LearningFunction f, LearningRate f ~ Metric p) =>- Classifier (SOM f gm) k p where- toList = GM.toList . sGridMap- numModels = G.tileCount . sGridMap- models = GM.elems . sGridMap- differences s p = GM.toList . GM.map (p `difference`) . sGridMap $ s+ LearningFunction f, Metric p ~ LearningRate f, Num (LearningRate f),+ Integral t)+ => Classifier (SOM f t gm) k p where+ toList = GM.toList . gridMap+ numModels = G.tileCount . gridMap+ models = GM.elems . gridMap+ differences s p = GM.toList . GM.map (p `difference`) $ gridMap s trainBatch s = incrementCounter . foldl' justTrain s reportAndTrain s p = (bmu, ds, incrementCounter s') where ds = differences s p- bmu = fst . minimumBy (comparing snd) $ ds+ bmu = fst $ minimumBy (comparing snd) ds s' = trainNeighbourhood s bmu p----- -- | Creates a classifier with a default (bell-shaped) learning--- -- function. Usage is @'defaultSOM' gm r0 rf w0 wf tf@, where:--- ----- -- [@gm@] The geometry and initial models for this classifier.--- -- A reasonable choice here is @'lazyGridMap' g ps@, where @g@ is a--- -- @'HexHexGrid'@, and @ps@ is a set of random patterns.--- ----- -- [@r0@] See description in @'decayingGaussian2'@.--- ----- -- [@rf@] See description in @'decayingGaussian2'@.--- ----- -- [@w0@] See description in @'decayingGaussian2'@.--- ----- -- [@wf@] See description in @'decayingGaussian2'@.--- ----- -- [@tf@] See description in @'decayingGaussian2'@.--- defaultSOM--- :: Floating (Metric p) => gm p -> Metric p -> Metric p -> Metric p ->--- Metric p -> Int -> SOM f gm k p--- defaultSOM gm r0 rf w0 wf tf =--- SOM {--- sGridMap=gm,--- sLearningFunction=DecayingGaussian r0 rf w0 wf tf,--- sCounter=0--- }---- -- | Creates a classifier with a custom learning function.--- -- Usage is @'customSOM' gm g@, where:--- ----- -- [@gm@] The geometry and initial models for this classifier.--- -- A reasonable choice here is @'lazyGridMap' g ps@, where @g@ is a--- -- @'HexHexGrid'@, and @ps@ is a set of random patterns.--- ----- -- [@f@] A function used to adjust the models in the classifier.--- -- This function will be invoked with two parameters.--- -- The first parameter will indicate how many patterns (or pattern--- -- batches) have previously been presented to this classifier.--- -- Typically this is used to make the learning rate decay over time.--- -- The second parameter to the function is the grid distance from--- -- the node being updated to the BMU (Best Matching Unit).--- -- The output is the learning rate for that node (the amount by--- -- which the node's model should be updated to match the target).--- -- The learning rate should be between zero and one.--- customSOM--- :: (LearningFunction f, LearningRate f ~ Metric p)--- => gm p -> f -> SOM f gm k p--- customSOM gm f =--- SOM {--- sGridMap=gm,--- sLearningFunction=f,--- sCounter=0--- }-