on master help sign in
Split back propagation function into two.

We now have one function which gives a Tape and the output, and another which takes the Tape and a loss gradient. This has a great benefit, in that people can build their own losses, which makes things like GANs really easy.

commitc8cfa399e78d4a182e7ffe267e19fb61d83da293 authorHuw Campbell <huw.campbell@ambiata.com> dateFri 3 Feb 2017, 22:44 (10 years ago) parent7d476bf
4 files changed+133−91
src/Grenade/Core/Layer.hs+18−7 src/Grenade/Core/Network.hs+91−48 src/Grenade/Core/Runner.hs+21−35 src/Grenade/Core/Shape.hs+3−1
src/Grenade/Core/Layer.hs+18−7
@@ -12,6 +12,9 @@ Description : Core definition a simple neural etwork
1212Copyright : (c) Huw Campbell, 2016-2017
1313License : BSD2
1414Stability : experimental
15+
16+This module defines what a Layer is in a Grenade
17+neural network.
1518-}
1619module Grenade.Core.Layer (
1720 Layer (..)
@@ -25,12 +28,14 @@ import Data.List ( foldl' )
2528import Grenade.Core.Shape
2629import Grenade.Core.LearningParameters
2730
28−-- | Class for updating a layer. All layers implement this, and it is
29−-- shape independent.
31+-- | Class for updating a layer. All layers implement this, as it
32+-- describes how to create and update the layer.
33+--
3034class UpdateLayer x where
3135 -- | The type for the gradient for this layer.
3236 -- Unit if there isn't a gradient to pass back.
3337 type Gradient x :: *
38+
3439 -- | Update a layer with its gradient and learning parameters
3540 runUpdate :: LearningParameters -> x -> Gradient x -> x
3641
@@ -44,17 +49,23 @@ class UpdateLayer x where
4449 {-# MINIMAL runUpdate, createRandom #-}
4550
4651-- | Class for a layer. All layers implement this, however, they don't
47−-- need to implement it for all shapes, only ones which are appropriate.
52+-- need to implement it for all shapes, only ones which are
53+-- appropriate.
54+--
4855class UpdateLayer x => Layer x (i :: Shape) (o :: Shape) where
4956 -- | The Wengert tape for this layer. Includes all that is required
5057 -- to generate the back propagated gradients efficiently. As a
5158 -- default, `S i` is fine.
5259 type Tape x i o :: *
60+
5361 -- | Used in training and scoring. Take the input from the previous
5462 -- layer, and give the output from this layer.
5563 runForwards :: x -> S i -> (Tape x i o, S o)
56− -- | Back propagate a step. Takes the current layer, the input that the
57− -- layer gave from the input and the back propagated derivatives from
58− -- the layer above.
59− -- Returns the gradient layer and the derivatives to push back further.
64+
65+ -- | Back propagate a step. Takes the current layer, the input that
66+ -- the layer gave from the input and the back propagated derivatives
67+ -- from the layer above.
68+ --
69+ -- Returns the gradient layer and the derivatives to push back
70+ -- further.
6071 runBackwards :: x -> Tape x i o -> S o -> (Gradient x, S i)
src/Grenade/Core/Network.hs+91−48
@@ -15,9 +15,8 @@ Copyright : (c) Huw Campbell, 2016-2017
1515License : BSD2
1616Stability : experimental
1717
18−This module defines the core data type for the simplest
19−Neural network we support.
20−
18+This module defines the core data types and functions
19+for non-recurrent neural networks.
2120-}
2221
2322#if __GLASGOW_HASKELL__ < 800
@@ -28,9 +27,12 @@ module Grenade.Core.Network (
2827 Network (..)
2928 , Gradients (..)
3029 , Tapes (..)
31− , CreatableNetwork (..)
3230
31+ , runNetwork
32+ , runGradient
3333 , applyUpdate
34+
35+ , randomNetwork
3436 ) where
3537
3638import Control.Monad.Random ( MonadRandom )
@@ -66,27 +68,94 @@ instance Show (Network '[] '[i]) where
6668instance (Show x, Show (Network xs rs)) => Show (Network (x ': xs) (i ': rs)) where
6769 show (x :~> xs) = show x ++ "\n~>\n" ++ show xs
6870
69−
70−-- | Apply one step of stochastic gradient decent across the network.
71−applyUpdate :: LearningParameters -> Network ls ss -> Gradients ls -> Network ls ss
72−applyUpdate _ NNil GNil
73− = NNil
74−applyUpdate rate (layer :~> rest) (gradient :/> grest)
75− = runUpdate rate layer gradient :~> applyUpdate rate rest grest
76−
77−
78−
79−-- | Gradients of a network.
71+-- | Gradient of a network.
72+--
8073-- Parameterised on the layers of the network.
8174data Gradients :: [*] -> * where
8275 GNil :: Gradients '[]
83− (:/>) :: UpdateLayer x => Gradient x -> Gradients xs -> Gradients (x ': xs)
8476
85−-- | Wegnert Tapes of a network.
77+ (:/>) :: UpdateLayer x
78+ => Gradient x
79+ -> Gradients xs
80+ -> Gradients (x ': xs)
81+
82+-- | Wegnert Tape of a network.
83+--
8684-- Parameterised on the layers and shapes of the network.
8785data Tapes :: [*] -> [Shape] -> * where
88− TNil :: SingI i => Tapes '[] '[i]
89− (:\>) :: (SingI i, SingI h, Layer x i h) => !(Tape x i h) -> !(Tapes xs (h ': hs)) -> Tapes (x ': xs) (i ': h ': hs)
86+ TNil :: SingI i
87+ => Tapes '[] '[i]
88+
89+ (:\>) :: (SingI i, SingI h, Layer x i h)
90+ => !(Tape x i h)
91+ -> !(Tapes xs (h ': hs))
92+ -> Tapes (x ': xs) (i ': h ': hs)
93+
94+
95+-- | Running a network forwards with some input data.
96+--
97+-- This gives the output, and the Wengert tape required for back
98+-- propagation.
99+--
100+runNetwork :: forall layers shapes.
101+ Network layers shapes
102+ -> S (Head shapes)
103+ -> (Tapes layers shapes, S (Last shapes))
104+runNetwork =
105+ go
106+ where
107+ go :: forall js ss. (Last js ~ Last shapes)
108+ => Network ss js
109+ -> S (Head js)
110+ -> (Tapes ss js, S (Last js))
111+ go (layer :~> n) !x =
112+ let (tape, forward) = runForwards layer x
113+ (tapes, answer) = go n forward
114+ in (tape :\> tapes, answer)
115+
116+ go NNil !x
117+ = (TNil, x)
118+
119+
120+-- | Running a loss gradient back through the network.
121+--
122+-- This requires a Wengert tape, generated with the appropriate input
123+-- for the loss.
124+--
125+-- Gives the gradients for the layer, and the gradient across the
126+-- input (which may not be required).
127+--
128+runGradient :: forall layers shapes. Network layers shapes
129+ -> Tapes layers shapes
130+ -> S (Last shapes)
131+ -> (Gradients layers, S (Head shapes))
132+runGradient net tapes o =
133+ go net tapes
134+ where
135+ go :: forall js ss. (Last js ~ Last shapes)
136+ => Network ss js
137+ -> Tapes ss js
138+ -> (Gradients ss, S (Head js))
139+ go (layer :~> n) (tape :\> nt) =
140+ let (gradients, feed) = go n nt
141+ (layer', backGrad) = runBackwards layer tape feed
142+ in (layer' :/> gradients, backGrad)
143+
144+ go NNil TNil
145+ = (GNil, o)
146+
147+
148+-- | Apply one step of stochastic gradient decent across the network.
149+applyUpdate :: LearningParameters
150+ -> Network layers shapes
151+ -> Gradients layers
152+ -> Network layers shapes
153+applyUpdate rate (layer :~> rest) (gradient :/> grest)
154+ = runUpdate rate layer gradient :~> applyUpdate rate rest grest
155+
156+applyUpdate _ NNil GNil
157+ = NNil
158+
90159
91160-- | A network can easily be created by hand with (:~>), but an easy way to initialise a random
92161-- network is with the randomNetwork.
@@ -115,36 +184,10 @@ instance (SingI i, SingI o, Layer x i o, Serialize x, Serialize (Network xs (o '
115184-- This allows a complete network to be treated as a layer in a bigger network.
116185instance CreatableNetwork sublayers subshapes => UpdateLayer (Network sublayers subshapes) where
117186 type Gradient (Network sublayers subshapes) = Gradients sublayers
118− runUpdate = applyUpdate
187+ runUpdate = applyUpdate
119188 createRandom = randomNetwork
120189
121190instance (CreatableNetwork sublayers subshapes, i ~ (Head subshapes), o ~ (Last subshapes)) => Layer (Network sublayers subshapes) i o where
122191 type Tape (Network sublayers subshapes) i o = Tapes sublayers subshapes
123−
124− runForwards net i =
125− go i net
126− where
127− go :: forall js ss. (Last js ~ Last subshapes)
128− => S (Head js) -- ^ input vector
129− -> Network ss js -- ^ network to train
130− -> (Tapes ss js, S (Last js))
131− go !x (layer :~> n) =
132− let (tape, forward) = runForwards layer x
133− (tapes, answer) = go forward n
134− in (tape :\> tapes, answer)
135− go !x NNil
136− = (TNil, x)
137−
138− runBackwards net tapes o =
139− go net tapes
140− where
141− go :: forall js ss. (Last js ~ Last subshapes)
142− => Network ss js -- ^ network to train
143− -> Tapes ss js -- ^ network to train
144− -> (Gradients ss, S (Head js))
145− go (layer :~> n) (tape :\> nt) =
146− let (gradients, feed) = go n nt
147− (layer', backGrad) = runBackwards layer tape feed
148− in (layer' :/> gradients, backGrad)
149− go NNil TNil
150− = (GNil, o)
192+ runForwards = runNetwork
193+ runBackwards = runGradient
src/Grenade/Core/Runner.hs+21−35
@@ -1,9 +1,5 @@
1−{-# LANGUAGE BangPatterns #-}
2−{-# LANGUAGE GADTs #-}
31{-# LANGUAGE DataKinds #-}
4−{-# LANGUAGE ScopedTypeVariables #-}
5−{-# LANGUAGE TypeOperators #-}
6−{-# LANGUAGE TypeFamilies #-}
2+{-# LANGUAGE FlexibleContexts #-}
73{-|
84Module : Grenade.Core.Shape
95Description : Core definition of the Shapes of data we understand
@@ -14,19 +10,14 @@ Stability : experimental
1410This module defines simple back propagation and training functions
1511for a network.
1612-}
17−
18−-- GHC 7.10 doesn't think that go is complete
19−{-# OPTIONS_GHC -fno-warn-incomplete-patterns #-}
2013module Grenade.Core.Runner (
2114 train
2215 , backPropagate
2316 , runNet
24− , applyUpdate
2517 ) where
2618
2719import Data.Singletons.Prelude
2820
29−import Grenade.Core.Layer
3021import Grenade.Core.LearningParameters
3122import Grenade.Core.Network
3223import Grenade.Core.Shape
@@ -36,37 +27,32 @@ import Grenade.Core.Shape
3627--
3728-- /Note:/ The loss function pushed backwards is appropriate
3829-- for both regression and classification as a squared loss
39−-- or log-loss respectively. Other loss functions are not yet
40−-- implemented.
41−backPropagate :: forall shapes layers.
42− Network layers shapes -> S (Head shapes) -> S (Last shapes) -> Gradients layers
30+-- or log-loss respectively.
31+--
32+-- For other loss functions, use runNetwork and runGradient
33+-- with the back propagated gradient of your loss.
34+--
35+backPropagate :: SingI (Last shapes)
36+ => Network layers shapes
37+ -> S (Head shapes)
38+ -> S (Last shapes)
39+ -> Gradients layers
4340backPropagate network input target =
44− fst $ go input network
45− where
46− go :: forall js sublayers. (Last js ~ Last shapes)
47− => S (Head js) -- ^ input vector
48− -> Network sublayers js -- ^ network to train
49− -> (Gradients sublayers, S (Head js))
50− -- handle input from the beginning, feeding upwards.
51− go !x (layer :~> n)
52− = let (tape, y) = runForwards layer x
53− -- recursively run the rest of the network, and get the gradients from above.
54− (n', dWs') = go y n
55− -- calculate the gradient for this layer to pass down,
56− (layer', dWs) = runBackwards layer tape dWs'
57− in (layer' :/> n', dWs)
58−
59− -- Bouncing the derivatives back down.
60− go !x NNil
61− = (GNil, x - target)
41+ let (tapes, output) = runNetwork network input
42+ (grads, _) = runGradient network tapes (output - target)
43+ in grads
6244
6345-- | Update a network with new weights after training with an instance.
64−train :: LearningParameters -> Network layers shapes -> S (Head shapes) -> S (Last shapes) -> Network layers shapes
46+train :: SingI (Last shapes)
47+ => LearningParameters
48+ -> Network layers shapes
49+ -> S (Head shapes)
50+ -> S (Last shapes)
51+ -> Network layers shapes
6552train rate network input output =
6653 let grads = backPropagate network input output
6754 in applyUpdate rate network grads
6855
6956-- | Run the network with input and return the given output.
7057runNet :: Network layers shapes -> S (Head shapes) -> S (Last shapes)
71−runNet (layer :~> n) !x = let (_, y) = runForwards layer x in runNet n y
72−runNet NNil !x = x
58+runNet net = snd . runNetwork net
src/Grenade/Core/Shape.hs+3−1
@@ -27,6 +27,7 @@ module Grenade.Core.Shape (
2727 Shape (..)
2828 , S (..)
2929 , Sing (..)
30+
3031 , randomOfShape
3132 , fromStorable
3233 ) where
@@ -59,7 +60,8 @@ data Shape
5960 | D3 Nat Nat Nat
6061 -- ^ Three dimensional matrix. Row, Column, Channels.
6162
62−-- | Given a Shape n, these are the possible data structures with that shape.
63+-- | Concrete data structures for a Shape.
64+--
6365-- All shapes are held in contiguous memory.
6466-- 3D is held in a matrix (usually row oriented) which has height depth * rows.
6567data S (n :: Shape) where