som 4.0 → 4.1
raw patch · 3 files changed
+109/−67 lines, 3 files
Files
- som.cabal +1/−1
- src/Data/Datamining/Clustering/SOM.hs +9/−12
- src/Data/Datamining/Clustering/SOMInternal.hs +99/−54
som.cabal view
@@ -1,5 +1,5 @@ name: som-version: 4.0+version: 4.1 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/SOM.hs view
@@ -16,7 +16,7 @@ -- -- In layman's terms, a SOM can be useful when you you want to discover -- the underlying structure of some data. A tutorial is available at--- <https://github.com/mhwombat/som/wiki>+-- <https://github.com/mhwombat/som/wiki>. -- -- References: --@@ -28,23 +28,20 @@ {-# LANGUAGE UnicodeSyntax #-} module Data.Datamining.Clustering.SOM (+ -- * Construction SOM, defaultSOM, customSOM, gaussian, decayingGaussian,- toGridMap+ -- * Deconstruction+ toGridMap,+ -- * Advanced control+ trainNeighbourhood,+ incrementCounter ) where import Data.Datamining.Clustering.SOMInternal (SOM, defaultSOM,- customSOM, gaussian, decayingGaussian, toGridMap)--{- $Vector-If you wish to use a SOM with raw numeric vectors, use @no-warn-orphans@-and add the following to your code:--> instance (Floating a, Fractional a, Ord a, Eq a) ⇒ Pattern [a] a where-> difference = euclideanDistanceSquared-> makeSimilar = adjustVector--}+ customSOM, gaussian, decayingGaussian, toGridMap, trainNeighbourhood,+ incrementCounter)
src/Data/Datamining/Clustering/SOMInternal.hs view
@@ -16,14 +16,20 @@ module Data.Datamining.Clustering.SOMInternal (+ -- * Construction SOM(..), defaultSOM, customSOM, gaussian, decayingGaussian,- toGridMap+ -- * Deconstruction+ toGridMap,+ -- * Advanced control+ trainNeighbourhood,+ incrementCounter ) where +import qualified Data.Foldable as F (Foldable, foldr) import Data.List (foldl', minimumBy) import Data.Ord (comparing) import qualified Math.Geometry.Grid as G (Grid(..))@@ -32,6 +38,19 @@ import Data.Datamining.Clustering.Classifier(Classifier(..)) import Prelude hiding (lookup) +-- | A Self-Organising Map (SOM).+--+-- Although @SOM@ implements @GridMap@, most users will only need the+-- interface provided by @Classifier@. If you chose to use the +-- @GridMap@ functions, please note:+--+-- 1. The functions @adjust@, and @adjustWithKey@ do not increment the+-- counter. You can do so manually with @incrementCounter@.+--+-- 2. The functions @map@ and @mapWithKey@ are not implemented (they+-- 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 { sGridMap ∷ gm p,@@ -39,10 +58,33 @@ sCounter ∷ Int } +instance (F.Foldable gm) ⇒ F.Foldable (SOM 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)+ indices = G.indices . sGridMap+ distance = G.distance . sGridMap+ neighbours = G.neighbours . sGridMap+ contains = G.contains . sGridMap+ viewpoint = G.viewpoint . sGridMap+ tileCount = G.tileCount . sGridMap+ 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+ toGrid = GM.toGrid . sGridMap+ toMap = GM.toMap . sGridMap+ mapWithKey = error "Not implemented"+ adjustWithKey f k s = s { sGridMap=gm' }+ where gm = sGridMap s+ gm' = GM.adjustWithKey f k gm+ currentLearningFunction ∷ SOM gm k p → (Int → Metric p) currentLearningFunction s = (sLearningFunction s) (sCounter s) --- | Extract the grid and current models from the SOM.+-- | Extracts the grid and current models from the SOM. toGridMap ∷ GM.GridMap gm p ⇒ SOM gm k p → gm p toGridMap = sGridMap @@ -52,22 +94,29 @@ adjustNode g f target bmu k = makeSimilar target (f d) where d = G.distance g bmu k -trainWithBMU+-- | Trains the specified node and the neighbourood around it to better+-- match a target.+-- Most users should use @train@, which automatically determines+-- 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-trainWithBMU s bmu target = s { sGridMap=gm' }+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 s = s { sCounter=sCounter 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)) ⇒ SOM gm k p → p → SOM gm k p-justTrain s p = trainWithBMU s bmu p+justTrain s p = trainNeighbourhood s bmu p where ds = GM.toList . GM.map (p `difference`) . sGridMap $ s bmu = fst . minimumBy (comparing snd) $ ds @@ -80,35 +129,33 @@ numModels = G.tileCount . sGridMap models = GM.elems . sGridMap differences s p = GM.toList . GM.map (p `difference`) . sGridMap $ s- trainBatch s ps = (foldl' justTrain s ps) {sCounter=sCounter s + 1}- reportAndTrain s p = (bmu, ds, 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- s' = trainWithBMU s bmu p- s'' = s' { sCounter=sCounter s + 1}+ s' = trainNeighbourhood s bmu p --- Creates a classifier with a default (bell-shaped) learning function.+-- | Creates a classifier with a default (bell-shaped) learning+-- function. Usage is @'defaultSOM' gm r w t@, 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.+--+-- [@r@] The learning rate to be applied to the BMU (Best Matching Unit)+-- at "time" zero. The BMU is the model which best matches the+-- current target pattern.+--+-- [@w@] The width of the bell curve at "time" zero.+--+-- [@t@] Controls how rapidly the learning rate decays. After this+-- time, any learning done by the classifier will be negligible.+-- We recommend setting this parameter to the number of patterns+-- (or pattern batches) that will be presented to the classifier. An+-- estimate is fine. defaultSOM - ∷ Floating (Metric p) ⇒- -- | The geometry and initial models for this classifier.- -- A reasonable choice here is 'lazyGridMap g ps', where 'g' is a- -- @'Math.Geometry.Grid.HexHexGrid'@, and 'ps' is a set of- -- random patterns.- gm p →- -- | The learning rate to be applied to the BMU (Best Matching Unit)- -- at "time" zero. The BMU is the model which best matches the- -- current target pattern.- Metric p →- -- | The width of the bell curve at "time" zero.- Metric p →- -- | After this time, any learning done by the classifier will be- -- negligible. Recommend setting this parameter to the number of- -- patterns (or pattern batches) that will be presented to the- -- classifier. An estimate is fine.- Int →- -- | The result- SOM gm k p+ ∷ Floating (Metric p) ⇒ gm p → Metric p → Metric p → Int → SOM gm k p defaultSOM gm r w t = SOM { sGridMap=gm, @@ -116,26 +163,24 @@ sCounter=0 } --- Creates a classifier with a custom learning function.-customSOM ∷ - -- | The geometry and initial models for this classifier.- -- A reasonable choice here is 'lazyGridMap g ps', where 'g' is a- -- @'Math.Geometry.Grid.HexHexGrid'@, and 'ps' is a set of- -- random patterns.- gm p →- -- | 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.- (Int → Int → Metric p) →- -- | The result- SOM gm k p+-- | 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,@@ -158,11 +203,11 @@ where d' = fromIntegral d -- | Configures a typical learning function for classifiers.--- @'decayingGaussian r w0 tMax' returns a bell curve-shaped function.--- At time zero, the maximum learning rate (applied to the BMU) is --- @r@, and the neighbourhood width is @w@. Over time the bell curve--- shrinks and the learning rate tapers off, until at time @tMax@,--- the learning rate is negligible.+-- @'decayingGaussian' r w0 tMax@ returns a bell curve-shaped+-- function. At time zero, the maximum learning rate (applied to the +-- BMU) is @r@, and the neighbourhood width is @w@. Over time the bell+-- curve shrinks and the learning rate tapers off, until at time+-- @tMax@, the learning rate is negligible. decayingGaussian ∷ Floating a ⇒ a → a → Int → (Int → Int → a) decayingGaussian r w0 tMax =