on master help sign in
Merge pull request #15 from HuwCampbell/topic/cereal

Add very basic model saving and loading

commit2369ec3407a82d93e8ffcc6be17e855c411d607a authorHuw Campbell <huw.campbell@gmail.com> dateThu 19 Jan 2017, 19:06 (10 years ago) parent48263cd7c49164
21 files changed+329−80
README.md+12−4 grenade.cabal+5 main/feedforward.hs+38−18 main/shakespeare.hs+47−20 src/Grenade/Core/Network.hs+13 src/Grenade/Core/Shape.hs+25−27 src/Grenade/Layers/Convolution.hs+17 src/Grenade/Layers/Flatten.hs+7 src/Grenade/Layers/FullyConnected.hs+17 src/Grenade/Layers/Logit.hs+6 src/Grenade/Layers/Pad.hs+5 src/Grenade/Layers/Pooling.hs+5 src/Grenade/Layers/Relu.hs+6 src/Grenade/Layers/Tanh.hs+6 src/Grenade/Recurrent/Core/Network.hs+40 src/Grenade/Recurrent/Core/Runner.hs+7−1 src/Grenade/Recurrent/Layers/LSTM.hs+48−1 src/Grenade/Recurrent/Layers/Trivial.hs+6 src/Grenade/Utils/OneHot.hs+11−2 test/Test/Grenade/Layers/Convolution.hs+7−6 test/Test/Grenade/Recurrent/Layers/LSTM.hs+1−1
README.md+12−4
@@ -23,9 +23,18 @@ randomMnist :: MonadRandom m => m MNIST
2323randomMnist = randomNetwork
2424```
2525
26−And that's it. Because the types are rich, there's no specific term level code
27−required; although it is of course possible and easy to construct one explicitly
28−oneself.
26+And that's it. Because the types are so rich, there's no specific term level code
27+required to construct this network; although it is of course possible and
28+easy to construct and deconstruct the networks and layers explicitly oneself.
29+
30+If recurrent neural networks are more your style, you can try defining something
31+["unreasonably effective"](http://karpathy.github.io/2015/05/21/rnn-effectiveness/)
32+with
33+```haskell
34+type Shakespeare = RecurrentNetwork '[ R (LSTM 40 80), R (LSTM 80 40), F (FullyConnected 40 40), F Logit]
35+ '[ 'D1 40, 'D1 80, 'D1 40, 'D1 40, 'D1 40 ]
36+```
37+
2938
3039The network can be thought of as a heterogeneous list of layers, where its type
3140includes not only the layers of the network, but also the shapes of data that
@@ -89,7 +98,6 @@ and the tests run using:
8998
9099Grenade is currently known to build with ghc 7.10 and 8.0.
91100
92−
93101Thanks
94102------
95103Writing a library like this has been on my mind for a while now, but a big shout
grenade.cabal+5
@@ -22,6 +22,7 @@ library
2222 , containers
2323 , deepseq
2424 , either == 4.4.*
25+ , cereal
2526 , exceptions == 0.8.*
2627 , hmatrix
2728 , MonadRandom
@@ -85,6 +86,8 @@ executable feedforward
8586 build-depends: base
8687 , grenade
8788 , attoparsec
89+ , bytestring
90+ , cereal
8891 , either
8992 , optparse-applicative == 0.12.*
9093 , text == 1.2.*
@@ -132,6 +135,8 @@ executable shakespeare
132135 build-depends: base
133136 , grenade
134137 , attoparsec
138+ , bytestring
139+ , cereal
135140 , either
136141 , optparse-applicative == 0.12.*
137142 , text == 1.2.*
main/feedforward.hs+38−18
@@ -8,6 +8,9 @@ import Control.Monad
88import Control.Monad.Random
99import Data.List ( foldl' )
1010
11+import qualified Data.ByteString as B
12+import Data.Serialize
13+
1114import GHC.TypeLits
1215
1316import qualified Numeric.LinearAlgebra.Static as SA
@@ -16,13 +19,12 @@ import Options.Applicative
1619
1720import Grenade
1821
19−-- The defininition for our simple feed forward network.
20−-- The type level list represents the shapes passed through the layers. One can see that for this demonstration
21−-- we are using relu, tanh and logit non-linear units, which can be easily subsituted for each other in and out.
22−
23−-- It's important to keep the type signatures, as there's many layers which can "squeeze" into the gaps
24−-- between the shapes, so inference can't do it all for us.
2522
23+-- The defininition for our simple feed forward network.
24+-- The type level lists represents the layers and the shapes passed through the layers.
25+-- One can see that for this demonstration we are using relu, tanh and logit non-linear
26+-- units, which can be easily subsituted for each other in and out.
27+--
2628-- With around 100000 examples, this should show two clear circles which have been learned by the network.
2729type FFNet = Network '[ FullyConnected 2 40, Tanh, FullyConnected 40 10, Relu, FullyConnected 10 1, Logit ]
2830 '[ 'D1 2, 'D1 40, 'D1 40, 'D1 10, 'D1 10, 'D1 1, 'D1 1]
@@ -30,8 +32,8 @@ type FFNet = Network '[ FullyConnected 2 40, Tanh, FullyConnected 40 10, Relu, F
3032randomNet :: MonadRandom m => m FFNet
3133randomNet = randomNetwork
3234
33−netTest :: MonadRandom m => LearningParameters -> Int -> m String
34−netTest rate n = do
35+netTrain :: FFNet -> LearningParameters -> Int -> IO FFNet
36+netTrain net0 rate n = do
3537 inps <- replicateM n $ do
3638 s <- getRandom
3739 return $ S1D $ SA.randomVector s SA.Uniform * 2 - 1
@@ -39,20 +41,28 @@ netTest rate n = do
3941 if v `inCircle` (fromRational 0.33, 0.33) || v `inCircle` (fromRational (-0.33), 0.33)
4042 then S1D $ fromRational 1
4143 else S1D $ fromRational 0
42− net0 <- randomNet
4344
4445 let trained = foldl' trainEach net0 (zip inps outs)
45− let testIns = [ [ (x,y) | x <- [0..50] ]
46− | y <- [0..20] ]
47−
48− let outMat = fmap (fmap (\(x,y) -> (render . normx) $ runNet trained (S1D $ SA.vector [x / 25 - 1,y / 10 - 1]))) testIns
49− return $ unlines outMat
46+ return trained
5047
5148 where
5249 inCircle :: KnownNat n => SA.R n -> (SA.R n, Double) -> Bool
5350 v `inCircle` (o, r) = SA.norm_2 (v - o) <= r
5451 trainEach !network (i,o) = train rate network i o
5552
53+netLoad :: FilePath -> IO FFNet
54+netLoad modelPath = do
55+ modelData <- B.readFile modelPath
56+ either fail return $ runGet (get :: Get FFNet) modelData
57+
58+netScore :: FFNet -> IO ()
59+netScore network = do
60+ let testIns = [ [ (x,y) | x <- [0..50] ]
61+ | y <- [0..20] ]
62+ outMat = fmap (fmap (\(x,y) -> (render . normx) $ runNet network (S1D $ SA.vector [x / 25 - 1,y / 10 - 1]))) testIns
63+ putStrLn $ unlines outMat
64+
65+ where
5666 render n' | n' <= 0.2 = ' '
5767 | n' <= 0.4 = '.'
5868 | n' <= 0.6 = '-'
@@ -62,7 +72,7 @@ netTest rate n = do
6272 normx :: S ('D1 1) -> Double
6373 normx (S1D r) = SA.mean r
6474
65−data FeedForwardOpts = FeedForwardOpts Int LearningParameters
75+data FeedForwardOpts = FeedForwardOpts Int LearningParameters (Maybe FilePath) (Maybe FilePath)
6676
6777feedForward' :: Parser FeedForwardOpts
6878feedForward' =
@@ -72,9 +82,19 @@ feedForward' =
7282 <*> option auto (long "momentum" <> value 0.9)
7383 <*> option auto (long "l2" <> value 0.0005)
7484 )
85+ <*> optional (strOption (long "load"))
86+ <*> optional (strOption (long "save"))
7587
7688main :: IO ()
7789main = do
78− FeedForwardOpts examples rate <- execParser (info (feedForward' <**> helper) idm)
79− putStrLn "Training network..."
80− putStrLn =<< evalRandIO (netTest rate examples)
90+ FeedForwardOpts examples rate load save <- execParser (info (feedForward' <**> helper) idm)
91+ net0 <- case load of
92+ Just loadFile -> netLoad loadFile
93+ Nothing -> randomNet
94+
95+ net <- netTrain net0 rate examples
96+ netScore net
97+
98+ case save of
99+ Just saveFile -> B.writeFile saveFile $ runPut (put net)
100+ Nothing -> return ()
main/shakespeare.hs+47−20
@@ -11,7 +11,7 @@ import Control.Monad.Random
1111import Control.Monad.Trans.Except
1212
1313import Data.Char ( isUpper, toUpper, toLower )
14−import Data.List ( unfoldr, foldl' )
14+import Data.List ( foldl' )
1515import Data.Maybe ( fromMaybe )
1616
1717import qualified Data.Vector as V
@@ -20,6 +20,8 @@ import Data.Vector ( Vector )
2020import qualified Data.Map as M
2121import Data.Proxy ( Proxy (..) )
2222
23+import qualified Data.ByteString as B
24+import Data.Serialize
2325
2426import Data.Singletons.Prelude
2527import GHC.TypeLits
@@ -32,29 +34,31 @@ import Grenade
3234import Grenade.Recurrent
3335import Grenade.Utils.OneHot
3436
37+import System.IO.Unsafe ( unsafeInterleaveIO )
38+
3539-- The defininition for our natural language recurrent network.
3640-- This network is able to learn and generate simple words in
3741-- about an hour.
3842--
39−-- This is a first class recurrent net, although it's similar to
40−-- an unrolled graph.
43+-- This is a first class recurrent net.
4144--
4245-- The F and R types are tagging types to ensure that the runner and
4346-- creation function know how to treat the layers.
4447--
4548-- As an example, here's a short sequence generated.
4649--
47−-- > the see and and the sir, and and the make and the make and go the make and go the make and the
48−--
50+-- > KING RICHARD III:
51+-- > And as the heaven her his words, we the son, I show sand stape but the lament to shall were the sons with a strend
52+
4953type F = FeedForward
5054type R = Recurrent
5155
5256-- The definition of our network
53−type Shakespeare = RecurrentNetwork '[ R (LSTM 40 40), F (FullyConnected 40 40), F Logit]
54− '[ 'D1 40, 'D1 40, 'D1 40, 'D1 40 ]
57+type Shakespeare = RecurrentNetwork '[ R (LSTM 40 80), R (LSTM 80 40), F (FullyConnected 40 40), F Logit]
58+ '[ 'D1 40, 'D1 80, 'D1 40, 'D1 40, 'D1 40 ]
5559
5660-- The definition of the "sideways" input, which the network if fed recurrently.
57−type Shakespearian = RecurrentInputs '[ R (LSTM 40 40), F (FullyConnected 40 40), F Logit]
61+type Shakespearian = RecurrentInputs '[ R (LSTM 40 80), R (LSTM 80 40), F (FullyConnected 40 40), F Logit]
5862
5963randomNet :: MonadRandom m => m (Shakespeare, Shakespearian)
6064randomNet = randomRecurrent
@@ -82,36 +86,50 @@ trainSlice !rate !net !recIns input offset size =
8286runShakespeare :: ShakespeareOpts -> ExceptT String IO ()
8387runShakespeare ShakespeareOpts {..} = do
8488 (shakespeare, oneHotMap, oneHotDictionary) <- loadShakespeare trainingFile
85− (net0, i0) <- lift randomNet
86− lift $ foldM_ (\(!net, !io) size -> do
87− xs <- take (iterations `div` 15) <$> getRandomRs (0, length shakespeare - size - 1)
89+ (net0, i0) <- lift $
90+ case loadPath of
91+ Just loadFile -> netLoad loadFile
92+ Nothing -> randomNet
93+
94+ (trained, bestInput) <- lift $ foldM (\(!net, !io) size -> do
95+ xs <- take (iterations `div` 10) <$> getRandomRs (0, length shakespeare - size - 1)
8896 let (!trained, !bestInput) = foldl' (\(!n, !i) offset -> trainSlice rate n i shakespeare offset size) (net, io) xs
89− let results = take 100 $ generateParagraph trained bestInput oneHotMap oneHotDictionary ( S1D $ konst 0)
97+ results <- take 1000 <$> generateParagraph trained bestInput temperature oneHotMap oneHotDictionary ( S1D $ konst 0)
9098 putStrLn ("TRAINING STEP WITH SIZE: " ++ show size)
9199 putStrLn (unAnnotateCapitals results)
92100 return (trained, bestInput)
93− ) (net0, i0) [10,10,15,15,20,20,25,25,30,30,35,35,40,40,50 :: Int]
101+ ) (net0, i0) $ replicate 10 sequenceSize
102+
103+ case savePath of
104+ Just saveFile -> lift . B.writeFile saveFile $ runPut (put (trained, bestInput))
105+ Nothing -> return ()
94106
95107generateParagraph :: forall layers shapes n a. (Last shapes ~ 'D1 n, Head shapes ~ 'D1 n, KnownNat n, Ord a)
96108 => RecurrentNetwork layers shapes
97109 -> RecurrentInputs layers
110+ -> Double
98111 -> M.Map a Int
99112 -> Vector a
100113 -> S ('D1 n)
101− -> [a]
102−generateParagraph n s hotmap hotdict i =
103− unfoldr go (s, i)
114+ -> IO [a]
115+generateParagraph n s temperature hotmap hotdict =
116+ go s
104117 where
105− go (x, y) =
118+ go x y =
106119 do let (ns, o) = runRecurrent n x y
107− un <- unHot hotdict o
108− re <- makeHot hotmap un
109− Just (un, (ns, re))
120+ un <- sample temperature hotdict o
121+ Just re <- return $ makeHot hotmap un
122+ rest <- unsafeInterleaveIO $ go ns re
123+ return (un : rest)
110124
111125data ShakespeareOpts = ShakespeareOpts {
112126 trainingFile :: FilePath
113127 , iterations :: Int
114128 , rate :: LearningParameters
129+ , sequenceSize :: Int
130+ , temperature :: Double
131+ , loadPath :: Maybe FilePath
132+ , savePath :: Maybe FilePath
115133 }
116134
117135shakespeare' :: Parser ShakespeareOpts
@@ -122,6 +140,10 @@ shakespeare' = ShakespeareOpts <$> argument str (metavar "TRAIN")
122140 <*> option auto (long "momentum" <> value 0.95)
123141 <*> option auto (long "l2" <> value 0.000001)
124142 )
143+ <*> option auto (long "sequence-length" <> short 's' <> value 50)
144+ <*> option auto (long "temperature" <> short 't' <> value 0.4)
145+ <*> optional (strOption (long "load"))
146+ <*> optional (strOption (long "save"))
125147
126148main :: IO ()
127149main = do
@@ -132,6 +154,11 @@ main = do
132154 Left err -> putStrLn err
133155
134156
157+netLoad :: FilePath -> IO (Shakespeare, Shakespearian)
158+netLoad modelPath = do
159+ modelData <- B.readFile modelPath
160+ either fail return $ runGet get modelData
161+
135162-- Replace capitals with an annotation and the lower case letter
136163-- http://fastml.com/one-weird-trick-for-training-char-rnns/
137164annotateCapitals :: String -> String
src/Grenade/Core/Network.hs+13
@@ -29,6 +29,8 @@ import Control.Monad.Random (MonadRandom)
2929import Data.List ( foldl' )
3030import Data.Singletons
3131
32+import Data.Serialize
33+
3234import Grenade.Core.Shape
3335
3436-- | Learning parameters for stochastic gradient descent.
@@ -101,3 +103,14 @@ instance (SingI i, SingI o, Layer x i o) => CreatableNetwork (x ': '[]) (i ': o
101103
102104instance (SingI i, SingI o, Layer x i o, CreatableNetwork xs (o ': r ': rs)) => CreatableNetwork (x ': xs) (i ': o ': r ': rs) where
103105 randomNetwork = (:~>) <$> createRandom <*> randomNetwork
106+
107+
108+-- | Add very simple serialisation to the network
109+instance (SingI i, SingI o, Layer x i o, Serialize x) => Serialize (Network '[x] '[i, o]) where
110+ put (O x) = put x
111+ put _ = error "impossible"
112+ get = O <$> get
113+
114+instance (SingI i, SingI o, Layer x i o, Serialize x, Serialize (Network xs (o ': r ': rs))) => Serialize (Network (x ': xs) (i ': o ': r ': rs)) where
115+ put (x :~> r) = put x >> put r
116+ get = (:~>) <$> get <*> get
src/Grenade/Core/Shape.hs+25−27
@@ -65,30 +65,36 @@ data S (n :: Shape) where
6565
6666deriving instance Show (S n)
6767
68+-- Singletons
69+-- These could probably be derived with template haskell, but this seems
70+-- clear and makes adding the KnownNat constraints simple.
71+data instance Sing (n :: Shape) where
72+ D1Sing :: KnownNat a => Sing ('D1 a)
73+ D2Sing :: (KnownNat a, KnownNat b) => Sing ('D2 a b)
74+ D3Sing :: (KnownNat a, KnownNat b, KnownNat c, KnownNat (a * c)) => Sing ('D3 a b c)
75+
76+instance KnownNat a => SingI ('D1 a) where
77+ sing = D1Sing
78+instance (KnownNat a, KnownNat b) => SingI ('D2 a b) where
79+ sing = D2Sing
80+instance (KnownNat a, KnownNat b, KnownNat c, KnownNat (a * c)) => SingI ('D3 a b c) where
81+ sing = D3Sing
82+
6883instance SingI x => Num (S x) where
6984 (+) = n2 (+)
7085 (-) = n2 (-)
7186 (*) = n2 (*)
7287 abs = n1 abs
7388 signum = n1 signum
74− fromInteger x = case (sing :: Sing x) of
75− D1Sing -> S1D (konst $ fromInteger x)
76− D2Sing -> S2D (konst $ fromInteger x)
77− D3Sing -> S3D (konst $ fromInteger x)
89+ fromInteger x = nk (fromInteger x)
7890
7991instance SingI x => Fractional (S x) where
8092 (/) = n2 (/)
8193 recip = n1 recip
82− fromRational x = case (sing :: Sing x) of
83− D1Sing -> S1D (konst $ fromRational x)
84− D2Sing -> S2D (konst $ fromRational x)
85− D3Sing -> S3D (konst $ fromRational x)
94+ fromRational x = nk (fromRational x)
8695
8796instance SingI x => Floating (S x) where
88− pi = case (sing :: Sing x) of
89− D1Sing -> S1D (konst pi)
90− D2Sing -> S2D (konst pi)
91− D3Sing -> S3D (konst pi)
97+ pi = nk pi
9298 exp = n1 exp
9399 log = n1 log
94100 sqrt = n1 sqrt
@@ -107,21 +113,6 @@ instance SingI x => Floating (S x) where
107113 acosh = n1 acosh
108114 atanh = n1 atanh
109115
110−-- Singletons
111−-- These could probably be derived with template haskell, but this seems
112−-- clear and makes adding the KnownNat constraints simple.
113−data instance Sing (n :: Shape) where
114− D1Sing :: KnownNat a => Sing ('D1 a)
115− D2Sing :: (KnownNat a, KnownNat b) => Sing ('D2 a b)
116− D3Sing :: (KnownNat a, KnownNat b, KnownNat c, KnownNat (a * c)) => Sing ('D3 a b c)
117−
118−instance KnownNat a => SingI ('D1 a) where
119− sing = D1Sing
120−instance (KnownNat a, KnownNat b) => SingI ('D2 a b) where
121− sing = D2Sing
122−instance (KnownNat a, KnownNat b, KnownNat c, KnownNat (a * c)) => SingI ('D3 a b c) where
123− sing = D3Sing
124−
125116--
126117-- I haven't made shapes strict, as sometimes they're not needed
127118-- (the last input gradient back for instance)
@@ -170,3 +161,10 @@ n2 f (S1D x) (S1D y) = S1D (f x y)
170161n2 f (S2D x) (S2D y) = S2D (f x y)
171162n2 f (S3D x) (S3D y) = S3D (f x y)
172163n2 _ _ _ = error "Impossible to have different constructors for the same shaped network"
164+
165+-- Helper function for creating the number instances
166+nk :: forall x. SingI x => Double -> S x
167+nk x = case (sing :: Sing x) of
168+ D1Sing -> S1D (konst x)
169+ D2Sing -> S2D (konst x)
170+ D3Sing -> S3D (konst x)
src/Grenade/Layers/Convolution.hs+17
@@ -16,7 +16,9 @@ module Grenade.Layers.Convolution (
1616import Control.Monad.Random hiding ( fromList )
1717import Data.Maybe
1818import Data.Proxy
19+import Data.Serialize
1920import Data.Singletons.TypeLits
21+
2022import GHC.TypeLits
2123
2224import Numeric.LinearAlgebra hiding ( uniformSample, konst )
@@ -125,6 +127,21 @@ instance ( KnownNat channels
125127
126128 createRandom = randomConvolution
127129
130+instance ( KnownNat channels
131+ , KnownNat filters
132+ , KnownNat kernelRows
133+ , KnownNat kernelColumns
134+ , KnownNat strideRows
135+ , KnownNat strideColumns
136+ , KnownNat (kernelRows * kernelColumns * channels)
137+ ) => Serialize (Convolution channels filters kernelRows kernelColumns strideRows strideColumns) where
138+ put (Convolution w _) = putListOf put . toList . flatten . extract $ w
139+ get = do
140+ let f = fromIntegral $ natVal (Proxy :: Proxy filters)
141+ wN <- maybe (fail "Vector of incorrect size") return . create . reshape f . LA.fromList =<< getListOf get
142+ let mm = konst 0
143+ return $ Convolution wN mm
144+
128145-- | A two dimentional image may have a convolution filter applied to it
129146instance ( KnownNat kernelRows
130147 , KnownNat kernelCols
src/Grenade/Layers/Flatten.hs+7
@@ -7,6 +7,8 @@ module Grenade.Layers.Flatten (
77 FlattenLayer (..)
88 ) where
99
10+import Data.Serialize
11+
1012import Data.Singletons.TypeLits
1113import GHC.TypeLits
1214
@@ -41,6 +43,11 @@ instance (KnownNat y, KnownNat x, KnownNat z, z ~ 1) => Layer FlattenLayer ('D3
4143 runForwards _ (S3D y) = S2D y
4244 runBackwards _ _ (S2D y) = ((), S3D y)
4345
46+instance Serialize FlattenLayer where
47+ put _ = return ()
48+ get = return FlattenLayer
49+
50+
4451fromJust' :: Maybe x -> x
4552fromJust' (Just x) = x
4653fromJust' Nothing = error $ "FlattenLayer error: data shape couldn't be converted."
src/Grenade/Layers/FullyConnected.hs+17
@@ -3,6 +3,7 @@
33{-# LANGUAGE TypeOperators #-}
44{-# LANGUAGE TypeFamilies #-}
55{-# LANGUAGE MultiParamTypeClasses #-}
6+{-# LANGUAGE ScopedTypeVariables #-}
67module Grenade.Layers.FullyConnected (
78 FullyConnected (..)
89 , randomFullyConnected
@@ -10,8 +11,11 @@ module Grenade.Layers.FullyConnected (
1011
1112import Control.Monad.Random hiding (fromList)
1213
14+import Data.Proxy
15+import Data.Serialize
1316import Data.Singletons.TypeLits
1417
18+import qualified Numeric.LinearAlgebra as LA
1519import Numeric.LinearAlgebra.Static
1620
1721import Grenade.Core.Network
@@ -55,6 +59,19 @@ instance (KnownNat i, KnownNat o) => Layer (FullyConnected i o) ('D1 i) ('D1 o)
5559 dWs = tr wN #> dEdy
5660 in (FullyConnected' wB' mm', S1D dWs)
5761
62+instance (KnownNat i, KnownNat o) => Serialize (FullyConnected i o) where
63+ put (FullyConnected b _ w _) = do
64+ putListOf put . LA.toList . extract $ b
65+ putListOf put . LA.toList . LA.flatten . extract $ w
66+
67+ get = do
68+ let f = fromIntegral $ natVal (Proxy :: Proxy i)
69+ b <- maybe (fail "Vector of incorrect size") return . create . LA.fromList =<< getListOf get
70+ k <- maybe (fail "Vector of incorrect size") return . create . LA.reshape f . LA.fromList =<< getListOf get
71+ let bm = konst 0
72+ let mm = konst 0
73+ return $ FullyConnected b bm k mm
74+
5875randomFullyConnected :: (MonadRandom m, KnownNat i, KnownNat o)
5976 => m (FullyConnected i o)
6077randomFullyConnected = do
src/Grenade/Layers/Logit.hs+6
@@ -7,6 +7,8 @@ module Grenade.Layers.Logit (
77 ) where
88
99
10+import Data.Serialize
11+
1012import Data.Singletons.TypeLits
1113import Grenade.Core.Network
1214import Grenade.Core.Shape
@@ -35,6 +37,10 @@ instance (KnownNat i, KnownNat j, KnownNat k) => Layer Logit ('D3 i j k) ('D3 i
3537 runForwards _ (S3D y) = S3D (logistic y)
3638 runBackwards _ (S3D y) (S3D dEdy) = ((), S3D (logistic' y * dEdy))
3739
40+instance Serialize Logit where
41+ put _ = return ()
42+ get = return Logit
43+
3844logistic :: Floating a => a -> a
3945logistic x = 1 / (1 + exp (-x))
4046
src/Grenade/Layers/Pad.hs+5
@@ -10,6 +10,7 @@ module Grenade.Layers.Pad (
1010
1111import Data.Maybe
1212import Data.Proxy
13+import Data.Serialize
1314import Data.Singletons.TypeLits
1415import GHC.TypeLits
1516
@@ -34,6 +35,10 @@ instance UpdateLayer (Pad l t r b) where
3435 runUpdate _ x _ = x
3536 createRandom = return Pad
3637
38+instance Serialize (Pad l t r b) where
39+ put _ = return ()
40+ get = return Pad
41+
3742-- | A two dimentional image can be padped.
3843instance ( KnownNat padLeft
3944 , KnownNat padTop
src/Grenade/Layers/Pooling.hs+5
@@ -12,6 +12,7 @@ module Grenade.Layers.Pooling (
1212
1313import Data.Maybe
1414import Data.Proxy
15+import Data.Serialize
1516import Data.Singletons.TypeLits
1617import GHC.TypeLits
1718
@@ -39,6 +40,10 @@ instance UpdateLayer (Pooling kernelRows kernelColumns strideRows strideColumns)
3940 runUpdate _ Pooling _ = Pooling
4041 createRandom = return Pooling
4142
43+instance Serialize (Pooling kernelRows kernelColumns strideRows strideColumns) where
44+ put _ = return ()
45+ get = return Pooling
46+
4247-- | A two dimentional image can be pooled.
4348instance ( KnownNat kernelRows
4449 , KnownNat kernelColumns
src/Grenade/Layers/Relu.hs+6
@@ -6,6 +6,8 @@ module Grenade.Layers.Relu (
66 Relu (..)
77 ) where
88
9+import Data.Serialize
10+
911import GHC.TypeLits
1012import Grenade.Core.Network
1113import Grenade.Core.Shape
@@ -23,6 +25,10 @@ instance UpdateLayer Relu where
2325 runUpdate _ _ _ = Relu
2426 createRandom = return Relu
2527
28+instance Serialize Relu where
29+ put _ = return ()
30+ get = return Relu
31+
2632instance ( KnownNat i) => Layer Relu ('D1 i) ('D1 i) where
2733 runForwards _ (S1D y) = S1D (relu y)
2834 where
src/Grenade/Layers/Tanh.hs+6
@@ -6,6 +6,8 @@ module Grenade.Layers.Tanh (
66 Tanh (..)
77 ) where
88
9+import Data.Serialize
10+
911import GHC.TypeLits
1012import Grenade.Core.Network
1113import Grenade.Core.Shape
@@ -20,6 +22,10 @@ instance UpdateLayer Tanh where
2022 runUpdate _ _ _ = Tanh
2123 createRandom = return Tanh
2224
25+instance Serialize Tanh where
26+ put _ = return ()
27+ get = return Tanh
28+
2329instance KnownNat i => Layer Tanh ('D1 i) ('D1 i) where
2430 runForwards _ (S1D y) = S1D (tanh y)
2531 runBackwards _ (S1D y) (S1D dEdy) = ((), S1D (tanh' y * dEdy))
src/Grenade/Recurrent/Core/Network.hs+40
@@ -19,10 +19,15 @@ module Grenade.Recurrent.Core.Network (
1919
2020import Control.Monad.Random ( MonadRandom )
2121import Data.Singletons ( SingI )
22+import Data.Serialize
23+import qualified Data.Vector.Storable as V
2224
2325import Grenade.Core.Shape
2426import Grenade.Core.Network
2527
28+import qualified Numeric.LinearAlgebra as LA
29+import qualified Numeric.LinearAlgebra.Static as LAS
30+
2631
2732-- | Witness type to say indicate we're building up with a normal feed
2833-- forward layer.
@@ -96,3 +101,38 @@ instance (SingI i, RecurrentLayer x i o, CreatableRecurrent xs (o ': r ': rs)) =
96101 (rest, resti) <- randomRecurrent
97102 return (thisLayer :~@> rest, thisShape :~@+> resti)
98103
104+-- | Add very simple serialisation to the recurrent network
105+instance (SingI i, SingI o, Layer x i o, Serialize x) => Serialize (RecurrentNetwork '[FeedForward x] '[i, o]) where
106+ put (OR x) = put x
107+ put _ = error "impossible"
108+ get = OR <$> get
109+
110+instance (SingI i, Layer x i o, Serialize x, Serialize (RecurrentNetwork xs (o ': r ': rs))) => Serialize (RecurrentNetwork (FeedForward x ': xs) (i ': o ': r ': rs)) where
111+ put (x :~~> r) = put x >> put r
112+ get = (:~~>) <$> get <*> get
113+
114+instance (SingI i, RecurrentLayer x i o, Serialize x, Serialize (RecurrentNetwork xs (o ': r ': rs))) => Serialize (RecurrentNetwork (Recurrent x ': xs) (i ': o ': r ': rs)) where
115+ put (x :~@> r) = put x >> put r
116+ get = (:~@>) <$> get <*> get
117+
118+instance (UpdateLayer x) => (Serialize (RecurrentInputs '[FeedForward x])) where
119+ put _ = return ()
120+ get = return (ORS ())
121+
122+instance (UpdateLayer x, Serialize (RecurrentInputs (y ': ys))) => (Serialize (RecurrentInputs (FeedForward x ': y ': ys))) where
123+ put ( () :~~+> rest) = put rest
124+ get = ( () :~~+> ) <$> get
125+
126+instance (SingI (RecurrentShape x), RecurrentUpdateLayer x, Serialize (RecurrentInputs (y ': ys))) => (Serialize (RecurrentInputs (Recurrent x ': y ': ys))) where
127+ put ( i :~@+> rest ) = do
128+ _ <- (case i of
129+ (S1D x) -> putListOf put . LA.toList . LAS.extract $ x
130+ (S2D x) -> putListOf put . LA.toList . LA.flatten . LAS.extract $ x
131+ (S3D x) -> putListOf put . LA.toList . LA.flatten . LAS.extract $ x
132+ ) :: PutM ()
133+ put rest
134+
135+ get = do
136+ Just i <- fromStorable . V.fromList <$> getListOf get
137+ rest <- get
138+ return ( i :~@+> rest)
src/Grenade/Recurrent/Core/Runner.hs+7−1
@@ -19,6 +19,12 @@ import Grenade.Core.Shape
1919import Grenade.Recurrent.Core.Network
2020
2121-- | Drive and network and collect its back propogated gradients.
22+--
23+-- TODO: split this nicely into backpropagate and update.
24+--
25+-- QUESTION: Should we return a list of gradients or the sum of
26+-- the gradients? It's different taking into account
27+-- momentum and L2.
2228trainRecurrent :: forall shapes layers. SingI (Last shapes)
2329 => LearningParameters
2430 -> RecurrentNetwork layers shapes
@@ -94,7 +100,7 @@ trainRecurrent rate network recinputs examples =
94100 = () :~~+> updateRecInputs l xs ys
95101
96102 updateRecInputs l@LearningParameters {..} (x :~@+> xs) (y :~@+> ys)
97− = (x - realToFrac learningRate * y) :~@+> updateRecInputs l xs ys
103+ = (realToFrac (learningRate * learningRegulariser) * x - realToFrac learningRate * y) :~@+> updateRecInputs l xs ys
98104
99105 updateRecInputs _ (ORS ()) (ORS ())
100106 = ORS ()
src/Grenade/Recurrent/Layers/LSTM.hs+48−1
@@ -8,6 +8,7 @@
88{-# LANGUAGE MultiParamTypeClasses #-}
99{-# LANGUAGE FlexibleContexts #-}
1010{-# LANGUAGE ViewPatterns #-}
11+{-# LANGUAGE ScopedTypeVariables #-}
1112
1213-- GHC 7.10 doesn't see recurrent run functions as total.
1314{-# OPTIONS_GHC -fno-warn-incomplete-patterns #-}
@@ -19,9 +20,13 @@ module Grenade.Recurrent.Layers.LSTM (
1920
2021import Control.Monad.Random ( MonadRandom, getRandom )
2122
23+
2224-- import Data.List ( foldl1' )
25+import Data.Proxy
26+import Data.Serialize
2327import Data.Singletons.TypeLits
2428
29+import qualified Numeric.LinearAlgebra as LA
2530import Numeric.LinearAlgebra.Static
2631
2732import Grenade.Core.Network
@@ -118,7 +123,6 @@ instance (KnownNat i, KnownNat o) => UpdateLayer (LSTM i o) where
118123
119124 -- v :: forall x ix. (x -> (R ix)) -> x -> x -> R ix
120125 -- v e (e -> a) (e -> b) = a + b
121−
122126 createRandom = randomLSTM
123127
124128instance (KnownNat i, KnownNat o) => RecurrentUpdateLayer (LSTM i o) where
@@ -245,3 +249,46 @@ sigmoid' x = logix * (1 - logix)
245249
246250tanh' :: (Floating a) => a -> a
247251tanh' t = 1 - s ^ (2 :: Int) where s = tanh t
252+
253+instance (KnownNat i, KnownNat o) => Serialize (LSTM i o) where
254+ put (LSTM LSTMWeights {..} _) = do
255+ u lstmWf
256+ u lstmUf
257+ v lstmBf
258+ u lstmWi
259+ u lstmUi
260+ v lstmBi
261+ u lstmWo
262+ u lstmUo
263+ v lstmBo
264+ u lstmWc
265+ v lstmBc
266+ where
267+ u :: forall a b. (KnownNat a, KnownNat b) => Putter (L b a)
268+ u = putListOf put . LA.toList . LA.flatten . extract
269+ v :: forall a. (KnownNat a) => Putter (R a)
270+ v = putListOf put . LA.toList . extract
271+
272+ get = do
273+ lstmWf <- u
274+ lstmUf <- u
275+ lstmBf <- v
276+ lstmWi <- u
277+ lstmUi <- u
278+ lstmBi <- v
279+ lstmWo <- u
280+ lstmUo <- u
281+ lstmBo <- v
282+ lstmWc <- u
283+ lstmBc <- v
284+ return $ LSTM (LSTMWeights {..}) (LSTMWeights w0 u0 v0 w0 u0 v0 w0 u0 v0 w0 v0)
285+ where
286+ u :: forall a b. (KnownNat a, KnownNat b) => Get (L b a)
287+ u = let f = fromIntegral $ natVal (Proxy :: Proxy a)
288+ in maybe (fail "Vector of incorrect size") return . create . LA.reshape f . LA.fromList =<< getListOf get
289+ v :: forall a. (KnownNat a) => Get (R a)
290+ v = maybe (fail "Vector of incorrect size") return . create . LA.fromList =<< getListOf get
291+
292+ w0 = konst 0
293+ u0 = konst 0
294+ v0 = konst 0
src/Grenade/Recurrent/Layers/Trivial.hs+6
@@ -7,12 +7,18 @@ module Grenade.Recurrent.Layers.Trivial (
77 Trivial (..)
88 ) where
99
10+import Data.Serialize
11+
1012import Grenade.Core.Network
1113
1214-- | A trivial layer.
1315data Trivial = Trivial
1416 deriving Show
1517
18+instance Serialize Trivial where
19+ put _ = return ()
20+ get = return Trivial
21+
1622instance UpdateLayer Trivial where
1723 type Gradient Trivial = ()
1824 runUpdate _ _ _ = Trivial
src/Grenade/Utils/OneHot.hs+11−2
@@ -11,8 +11,11 @@ module Grenade.Utils.OneHot (
1111 , hotMap
1212 , makeHot
1313 , unHot
14+ , sample
1415 ) where
1516
17+import qualified Control.Monad.Random as MR
18+
1619import Data.List ( group, sort )
1720
1821import Data.Map ( Map )
@@ -23,6 +26,7 @@ import Data.Singletons.TypeLits
2326
2427import Data.Vector ( Vector )
2528import qualified Data.Vector as V
29+import qualified Data.Vector.Storable as VS
2630
2731import Numeric.LinearAlgebra ( maxIndex )
2832import Numeric.LinearAlgebra.Devel
@@ -76,9 +80,14 @@ makeHot m x = do
7680 return vec
7781 else Nothing
7882
79−unHot :: forall a n. (KnownNat n)
80− => Vector a -> (S ('D1 n)) -> Maybe a
83+unHot :: forall a n. KnownNat n
84+ => Vector a -> S ('D1 n) -> Maybe a
8185unHot v (S1D xs)
8286 = (V.!?) v
8387 $ maxIndex (extract xs)
8488
89+sample :: forall a n m. (KnownNat n, MR.MonadRandom m)
90+ => Double -> Vector a -> S ('D1 n) -> m a
91+sample temperature v (S1D xs) = do
92+ ix <- MR.fromList . zip [0..] . fmap (toRational . exp . (/ temperature) . log) . VS.toList . extract $ xs
93+ return $ v V.! ix
test/Test/Grenade/Layers/Convolution.hs+7−6
@@ -22,6 +22,7 @@ import Grenade.Layers.Convolution
2222import Disorder.Jack
2323
2424import Test.Jack.Hmatrix
25+import Test.Jack.TypeLits
2526
2627data OpaqueConvolution :: * where
2728 OpaqueConvolution :: Convolution channels filters kernelRows kernelColumns strideRows strideColumns -> OpaqueConvolution
@@ -42,12 +43,12 @@ genConvolution = Convolution <$> uniformSample <*> uniformSample
4243
4344genOpaqueOpaqueConvolution :: Jack OpaqueConvolution
4445genOpaqueOpaqueConvolution = do
45− Just channels <- someNatVal <$> choose (1, 10)
46− Just filters <- someNatVal <$> choose (1, 10)
47− Just kernel_h <- someNatVal <$> choose (2, 20)
48− Just kernel_w <- someNatVal <$> choose (2, 20)
49− Just stride_h <- someNatVal <$> choose (1, 10)
50− Just stride_w <- someNatVal <$> choose (1, 10)
46+ channels <- genNat
47+ filters <- genNat
48+ kernel_h <- genNat
49+ kernel_w <- genNat
50+ stride_h <- genNat
51+ stride_w <- genNat
5152 case (channels, filters, kernel_h, kernel_w, stride_h, stride_w) of
5253 ( SomeNat (pch :: Proxy ch), SomeNat (_ :: Proxy fl),
5354 SomeNat (pkr :: Proxy kr), SomeNat (pkc :: Proxy kc),
test/Test/Grenade/Recurrent/Layers/LSTM.hs+1−1
@@ -90,7 +90,7 @@ prop_lstm_reference_backwards_cell =
9090 refCell = Reference.Vector . H.toList . S.extract $ cell
9191 refInput = Reference.Vector . H.toList . S.extract $ input
9292 refGradients = Reference.runLSTMbackOnCell refInput refNet refCell
93− in toList refGradients ~~~ (H.toList . S.extract $ actualGradients)
93+ in toList refGradients ~~~ H.toList (S.extract actualGradients)
9494
9595
9696(~~~) as bs = all (< 1e-8) (zipWith (-) as bs)