sgd 0.8.0.0 → 0.8.0.1
raw patch · 3 files changed
+3/−298 lines, 3 files
Files
- sgd.cabal +3/−5
- src/Numeric/SGD/Pipe.hs +0/−169
- src/Numeric/SGD/Sparse/Dataset.hs +0/−124
sgd.cabal view
@@ -4,12 +4,12 @@ -- -- see: https://github.com/sol/hpack ----- hash: c76dc64057b0ad62fdd40b4496b01d9bdbf827e48db5996639347df6c4209224+-- hash: 2f29d911949ea62e385d514bbbce417c880285e7f8cd576492e8444fb45ae1c8 name: sgd-version: 0.8.0.0+version: 0.8.0.1 synopsis: Stochastic gradient descent library-description: Please see the README on GitHub at <https://github.com/kawu/sgd#readme>+description: Stochastic gradient descent library. . Import "Numeric.SGD" to use the library. category: Math homepage: https://github.com/kawu/sgd#readme bug-reports: https://github.com/kawu/sgd/issues@@ -35,9 +35,7 @@ Numeric.SGD.DataSet Numeric.SGD.Momentum Numeric.SGD.ParamSet- Numeric.SGD.Pipe Numeric.SGD.Sparse- Numeric.SGD.Sparse.Dataset Numeric.SGD.Sparse.Grad Numeric.SGD.Sparse.LogSigned Numeric.SGD.Sparse.Momentum
− src/Numeric/SGD/Pipe.hs
@@ -1,169 +0,0 @@-{-# LANGUAGE RecordWildCards #-}-{-# LANGUAGE DeriveGeneric #-}----- | Pipe-based interface---module Numeric.SGD.Pipe where--- ( Config(..)--- , Method(..)--- , sgd--- , result--- , every--- , pipeSeq--- , pipeRan--- ) where----- import GHC.Generics (Generic)--- import Numeric.Natural (Natural)--- --- import Control.Monad (when, forM_)--- --- import qualified System.Random as R--- --- import qualified Data.IORef as IO--- --- import qualified Pipes as P--- import qualified Pipes.Prelude as P--- import Pipes ((>->))--- --- import Numeric.SGD.DataSet--- import Numeric.SGD.ParamSet--- import qualified Numeric.SGD.AdaDelta as Ada--- import qualified Numeric.SGD.Momentum as Mom--- --- --- ------------------------------- --- -- Data--- ---------------------------------- --- --- -- | Top-level SGD configuration--- data Config = Config--- { iterNum :: Natural--- -- ^ Number of iteration over the entire training dataset--- , batchRandom :: Bool--- -- ^ Should the mini-batch be selected at random? If not, the subsequent--- -- training elements will be picked sequentially. Random selection gives--- -- no guarantee of seeing each training sample in every epoch. Use `False`--- -- if unsure.--- , method :: Method--- -- ^ Selected SGD method--- , reportPeriod :: Double--- -- ^ How often the quality should be reported (with `1` meaning once per--- -- pass over the training data)--- } deriving (Show, Eq, Ord, Generic)--- --- --- -- | Different SGD methods, together with the corresponding configurations--- data Method--- = AdaDelta {adaDeltaCfg :: Ada.Config}--- | Momentum {momentumCfg :: Mom.Config}--- deriving (Show, Eq, Ord, Generic)--- --- --- ------------------------------- --- -- SGD--- ---------------------------------- --- --- -- | Pipe-based SGD.--- sgd--- :: (ParamSet p)--- => Config--- -> DataSet e--- -> (e -> p -> p)--- -- ^ Network gradient on a sample element--- -> (e -> p -> Double)--- -- ^ Value of the objective function on a sample element--- -> p--- -- ^ Initial parameter values--- -> IO p--- sgd Config{..} dataSet grad0 quality0 net0 = do--- let sgdPipe =--- case method of--- Momentum cfg -> Mom.momentum cfg--- -- (cfg {Mom.tau = iterScale (Mom.tau cfg)})--- grad0 net0--- AdaDelta cfg -> Ada.adaDelta cfg grad0 net0--- report net0--- result net0 $ pipeSeq dataSet--- >-> sgdPipe--- >-> P.take realIterNum--- >-> every realReportPeriod report--- where--- -- Iteration scaling--- iterScale x = fromIntegral (size dataSet) * x--- -- Number of iterations and reporting period--- realIterNum = ceiling $ iterScale (fromIntegral iterNum :: Double)--- realReportPeriod = ceiling $ iterScale reportPeriod--- -- Network quality over the entire training dataset--- report net = do--- putStr . show =<< quality net--- putStrLn $ " (norm_2 = " ++ show (norm_2 net) ++ ")"--- quality net = do--- res <- IO.newIORef 0.0--- forM_ [0 .. size dataSet - 1] $ \ix -> do--- x <- elemAt dataSet ix--- IO.modifyIORef' res (+ quality0 x net)--- IO.readIORef res--- --- --- ------------------------------- --- -- Dataset producers--- ---------------------------------- --- --- -- | Pipe the dataset sequentially in a loop.--- pipeSeq :: DataSet e -> P.Producer e IO ()--- pipeSeq dataSet = do--- go (0 :: Int)--- where--- go k--- | k >= size dataSet = go 0--- | otherwise = do--- x <- P.lift $ elemAt dataSet k--- P.yield x--- go (k+1)--- --- --- -- | Pipe the dataset randomly in a loop.--- pipeRan :: DataSet e -> P.Producer e IO ()--- pipeRan dataSet = do--- x <- P.lift $ do--- ix <- R.randomRIO (0, size dataSet - 1)--- elemAt dataSet ix--- P.yield x--- pipeRan dataSet--- --- --- ------------------------------- --- -- Utils--- ---------------------------------- --- --- -- | Extract the result of the SGD calculation (the last parameter--- -- set flowing downstream).--- result--- :: (Monad m)--- => p --- -- ^ Default value (in case the stream is empty)--- -> P.Producer p m ()--- -- ^ Stream of parameter sets--- -> m p--- result pDef = fmap (maybe pDef id) . P.last--- --- --- -- | Report every `k`-th parameter set flowing downstream.--- every :: (Monad m) => Int -> (p -> m ()) -> P.Pipe p p m x--- every k report = do--- go (1 `mod` k)--- where--- go i = do--- paramSet <- P.await--- when (i == 0) $ do--- P.lift $ report paramSet--- P.yield paramSet--- go $ (i+1) `mod` k
− src/Numeric/SGD/Sparse/Dataset.hs
@@ -1,124 +0,0 @@-{-# LANGUAGE RecordWildCards #-}----- | Dataset abstraction.---module Numeric.SGD.Sparse.Dataset-( --- * Dataset- Dataset (..)--- * Reading-, loadData-, sample--- * Construction-, withVect-, withDisk-, withData-) where---import Control.Monad (forM_)-import Data.Binary (Binary, encodeFile, decode)-import qualified Data.ByteString as B-import qualified Data.ByteString.Lazy as BL-import System.IO.Temp (withTempDirectory)-import System.IO.Unsafe (unsafeInterleaveIO)-import System.FilePath ((</>))-import qualified System.Random as R-import qualified Data.Vector as V-import qualified Control.Monad.State.Strict as S----- | A dataset with elements of type @a@.-data Dataset a = Dataset {- -- | A size of the dataset.- size :: Int- -- | Get dataset element with a given index. The set of indices- -- is of a {0, 1, .., size - 1} form.- , elemAt :: Int -> IO a - }------------------------------------------------- Reading------------------------------------------------- | Lazily load dataset from a disk.-loadData :: Dataset a -> IO [a]-loadData Dataset{..} = lazyMapM elemAt [0 .. size - 1]----- | A dataset sample of the given size.-sample :: R.RandomGen g => g -> Int -> Dataset a -> IO ([a], g)-sample g 0 _ = return ([], g)-sample g n dataset = do- (xs, g') <- sample g (n-1) dataset- let (i, g'') = R.next g'- x <- dataset `elemAt` (i `mod` size dataset)- return (x:xs, g'')------------------------------------------------- Construction------------------------------------------------- | Construct dataset from a vector of elements and run the--- given handler.-withVect :: [a] -> (Dataset a -> IO b) -> IO b-withVect xs handler =- handler dataset- where- v = V.fromList xs- dataset = Dataset- { size = V.length v- , elemAt = \k -> return (v V.! k) }----- | Construct dataset from a list of elements, store it on a disk--- and run the given handler.-withDisk :: Binary a => [a] -> (Dataset a -> IO b) -> IO b-withDisk xs handler = withTempDirectory "." ".sgd" $ \tmpDir -> do- -- We use state monad to compute the number of dataset elements. - n <- flip S.execStateT 0 $ forM_ (zip xs [0 :: Int ..]) $ \(x, ix) -> do- S.lift $ encodeFile (tmpDir </> show ix) x- S.modify (+1)-- -- We need to avoid decodeFile laziness when using some older- -- versions of the binary library.- let at ix = do- cs <- B.readFile (tmpDir </> show ix)- return . decode $ BL.fromChunks [cs]-- handler $ Dataset {size = n, elemAt = at}----- | Use disk or vector dataset representation depending on--- the first argument: when `True`, use `withDisk`, otherwise--- use `withVect`.-withData :: Binary a => Bool -> [a] -> (Dataset a -> IO b) -> IO b-withData x = case x of- True -> withDisk- False -> withVect------------------------------------------------- Lazy IO Utils------------------------------------------------- | Lazily evaluate each action in the sequence from left to right,--- and collect the results.-lazySequence :: [IO a] -> IO [a]-lazySequence (mx:mxs) = do- x <- mx- xs <- unsafeInterleaveIO (lazySequence mxs)- return (x : xs)-lazySequence [] = return []----- | `lazyMapM` f is equivalent to `lazySequence` . `map` f.-lazyMapM :: (a -> IO b) -> [a] -> IO [b]-lazyMapM f = lazySequence . map f