som 6.5.1 → 7.0.0
raw patch · 4 files changed
+137/−121 lines, 4 filesdep −base-unicode-symbols
Dependencies removed: base-unicode-symbols
Files
- som.cabal +1/−3
- src/Data/Datamining/Clustering/DSOM.hs +3/−0
- src/Data/Datamining/Clustering/SOM.hs +5/−10
- src/Data/Datamining/Clustering/SOMInternal.hs +128/−108
som.cabal view
@@ -1,5 +1,5 @@ name: som-version: 6.5.1+version: 7.0.0 synopsis: Self-Organising Maps description: A Kohonen Self-organising Map (SOM) maps input patterns onto a regular grid (usually two-dimensional) where each@@ -28,7 +28,6 @@ library hs-source-dirs: src build-depends: base ==4.*,- base-unicode-symbols ==0.2.*, binary ==0.7.*, containers ==0.5.*, grid ==7.*,@@ -49,7 +48,6 @@ test-framework ==0.8.*, som, grid ==7.*,- base-unicode-symbols ==0.2.*, MonadRandom ==0.1.*, random ==1.0.* hs-source-dirs: test
src/Data/Datamining/Clustering/DSOM.hs view
@@ -15,6 +15,9 @@ -- -- * Rougier, N. & Boniface, Y. (2011). Dynamic self-organising map. -- Neurocomputing, 74 (11), 1840-1847. +--+-- * Kohonen, T. (1982). Self-organized formation of topologically +-- correct feature maps. Biological Cybernetics, 43 (1), 59–69. ------------------------------------------------------------------------ module Data.Datamining.Clustering.DSOM
src/Data/Datamining/Clustering/SOM.hs view
@@ -34,18 +34,13 @@ -- -- * Kohonen, T. (1982). Self-organized formation of topologically -- correct feature maps. Biological Cybernetics, 43 (1), 59–69.------ * Rougier, N. & Boniface, Y. (2011). Dynamic self-organising map.--- Neurocomputing, 74 (11), 1840-1847. ------------------------------------------------------------------------ module Data.Datamining.Clustering.SOM ( -- * Construction- SOM,- defaultSOM,- customSOM,- decayingGaussian,+ SOM(..),+ DecayingGaussian(..), -- * Deconstruction toGridMap, -- * Advanced control@@ -55,7 +50,7 @@ setCounter ) where -import Data.Datamining.Clustering.SOMInternal (SOM, defaultSOM,- customSOM, decayingGaussian, toGridMap, trainNeighbourhood,- incrementCounter, counter, setCounter)+import Data.Datamining.Clustering.SOMInternal (SOM(..),+ DecayingGaussian(..), toGridMap, trainNeighbourhood, incrementCounter,+ counter, setCounter)
src/Data/Datamining/Clustering/SOMInternal.hs view
@@ -26,6 +26,57 @@ import GHC.Generics (Generic) import Prelude hiding (lookup) +class LearningFunction f where+ type LearningRate f+ rate :: f -> Int -> Int -> (LearningRate f)++-- | A typical learning function for classifiers.+-- @'DecayingGaussian' r0 rf w0 wf tf@ returns a bell curve-shaped+-- function. At time zero, the maximum learning rate (applied to the+-- BMU) is @r0@, and the neighbourhood width is @w0@. Over time the+-- bell curve shrinks and the learning rate tapers off, until at time+-- @tf@, the maximum learning rate (applied to the BMU) is @rf@,+-- and the neighbourhood width is @wf@. Normally the parameters+-- should be chosen such that:+--+-- * 0 < rf << r0 < 1+--+-- * 0 < wf << w0+--+-- * 0 < tf+--+-- where << means "is much smaller than" (not the Haskell @<<@+-- operator!)+data DecayingGaussian a = DecayingGaussian a a a a Int+ deriving (Eq, Show, Generic)++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'+ 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+ type LearningRate (StepFunction a) = a+ rate (StepFunction r) _ d = if d == 0 then r else 0.0++-- | A learning function that updates all nodes with the same, constant+-- learning rate. This can be useful for testing.+data ConstantFunction a = ConstantFunction a deriving (Eq, Show, Generic)++instance (Fractional a) => LearningFunction (ConstantFunction a) where+ type LearningRate (ConstantFunction a) = a+ rate (ConstantFunction r) _ _ = r+ -- | A Self-Organising Map (SOM). -- -- Although @SOM@ implements @GridMap@, most users will only need the@@ -39,19 +90,19 @@ -- 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 gm k p = SOM+data SOM f gm k p = SOM { sGridMap :: gm p,- sLearningFunction :: Int -> Int -> Metric p,+ sLearningFunction :: f, sCounter :: Int- } deriving Generic+ } deriving (Eq, Show, Generic) -instance (F.Foldable gm) => F.Foldable (SOM gm k) where+instance (F.Foldable gm) => F.Foldable (SOM f gm k) where foldr f x g = F.foldr f x (sGridMap g) -instance (G.Grid (gm p)) => G.Grid (SOM gm k p) where- type Index (SOM gm k p) = G.Index (gm p)- type Direction (SOM gm k p) = G.Direction (gm p)+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@@ -62,8 +113,8 @@ null = G.null . sGridMap nonNull = G.nonNull . sGridMap -instance (F.Foldable gm, GM.GridMap gm p, G.Grid (GM.BaseGrid gm p)) => GM.GridMap (SOM gm k) p where- type BaseGrid (SOM gm k) p = GM.BaseGrid gm p+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 mapWithKey = error "Not implemented"@@ -71,11 +122,13 @@ where gm = sGridMap s gm' = GM.adjustWithKey f k gm -currentLearningFunction :: SOM gm k p -> (Int -> Metric p)-currentLearningFunction s = (sLearningFunction s) (sCounter s)+currentLearningFunction+ :: (LearningFunction f, LearningRate f ~ Metric p)+ => SOM f gm k p -> (Int -> Metric p)+currentLearningFunction s = (rate . sLearningFunction $ s) (sCounter s) -- | Extracts the grid and current models from the SOM.-toGridMap :: GM.GridMap gm p => SOM gm k p -> gm p+toGridMap :: GM.GridMap gm p => SOM f gm k p -> gm p toGridMap = sGridMap adjustNode@@ -90,28 +143,30 @@ -- the BMU and trains it and its neighbourhood. trainNeighbourhood :: (Pattern p, G.Grid (gm p), GM.GridMap gm p,- G.Index (GM.BaseGrid gm p) ~ G.Index (gm p)) =>- SOM gm k p -> G.Index (gm p) -> p -> SOM gm k 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 gm' = GM.mapWithKey (adjustNode gm f target bmu) gm f = currentLearningFunction s -incrementCounter :: SOM gm k p -> SOM gm k p+incrementCounter :: SOM f gm k p -> SOM f gm k p incrementCounter s = setCounter (sCounter s + 1) s -counter :: SOM gm k p -> Int+counter :: SOM f gm k p -> Int counter = sCounter -setCounter :: Int -> SOM gm k p -> SOM gm k p+setCounter :: Int -> SOM f gm k p -> SOM f gm k p setCounter k s = s { sCounter = k } 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)) =>- SOM gm k p -> p -> SOM gm k 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 justTrain s p = trainNeighbourhood s bmu p where ds = GM.toList . GM.map (p `difference`) . sGridMap $ s bmu = fst . minimumBy (comparing snd) $ ds@@ -119,8 +174,9 @@ 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)) =>- Classifier (SOM gm) k p where+ 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@@ -132,92 +188,56 @@ 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 gm k p-defaultSOM gm r0 rf w0 wf tf =- SOM {- sGridMap=gm,- sLearningFunction=decayingGaussian2 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 :: gm p -> (Int -> Int -> Metric p) -> SOM gm k p-customSOM gm f =- SOM {- sGridMap=gm,- sLearningFunction=f,- sCounter=0- }+-- -- | 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+-- } --- | Configures one possible learning function for classifiers.--- @'decayingGaussian' r0 w0 tMax@ returns a bell curve-shaped--- function. At time zero, the maximum learning rate (applied to the--- BMU) is @r0@, and the neighbourhood width is @w0@. Over time the--- neighbourhood width shrinks and the learning rate tapers off.-decayingGaussian- :: Floating a => a -> a -> Int -> (Int -> Int -> a)-decayingGaussian r w0 tMax t d = r * s * exp (-(d'*d')/(2*w0*w0*s*s))- where s = exp (-t'/tMax')- t' = fromIntegral t- tMax' = fromIntegral tMax- d' = fromIntegral d+-- -- | 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+-- } --- | Configures a typical learning function for classifiers.--- @'decayingGaussian' r0 rf w0 wf tf@ returns a bell curve-shaped--- function. At time zero, the maximum learning rate (applied to the--- BMU) is @r0@, and the neighbourhood width is @w0@. Over time the--- bell curve shrinks and the learning rate tapers off, until at time--- @tf@, the maximum learning rate (applied to the BMU) is @rf@,--- and the neighbourhood width is @wf@. Normally the parameters--- should be chosen such that:------ * 0 < rf << r0 < 1------ * 0 < wf << w0------ * 0 < tf------ where << means "is much smaller than" (not the Haskell @<<@--- operator!) -decayingGaussian2 :: Floating a => a -> a -> a -> a -> Int -> (Int -> Int -> a)-decayingGaussian2 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