The Backprop Typeclass
Most of the functions in this module require a Backprop
constraint on values
you wish to backpropagate. Even if you manage to get around it for the most
part, gradBP
(the actual function to compute gradients) requires it on both
the inputs and outputs. Let's dig deeper into what it is, and how to define
instances.
The Class
The typeclass contains three methods: zero
, add
, and one
:
class Backprop a where
zero :: a -> a
add :: a -> a -> a
one :: a -> a
zero
is "zero" in the verb sense -- it takes a value and "zeroes out" all
components. For a vector, this means returning a zero vector of the same
shape. For a list, this means replacing all of the items with zero and
returning a list of the same length.
one
does the same thing but with one; the point of it is to be one = gradBP
id
--- the gradient of the identity function for your type.
add
is used to add together contributions in gradients, and is usually a
component-wise addition.
Instances are provided for most common data types where it makes sense.
Custom Instances
Generics
When defining your own custom types, if your custom type is has a single
constructor where all fields are instances of Backprop
, then GHC.Generics
can be used to write your instances automatically:
data MyType = MkMyType Double [Float] (R 10) (L 20 10) (V.Vector Double)
deriving Generic
Nice type. Since it has a single constructor and all of its fields are already
Backprop
instances, we can just write:
instance Backprop MyType
and now your type can be backpropagated!
Common Patterns
For writing "primitive" Backprop
instances (types that aren't product types),
you can use the provided "helpers" from the Numeric.Backprop.Class module.
If your type is a Num
instance, you can use zeroNum
, addNum
, and
oneNum
:
instance Backprop Double where
zero = zeroNum
add = addNum
one = oneNum
If your type is made using a Functor
instance, you can use zeroFunctor
and
oneFunctor
:
instance Backprop a => Backprop (V.Vector a) where
zero = zeroFunctor
add = undefined -- ??
one = oneFunctor
And if your type has an IsList
instance, you can use addIsList
:
instance Backprop a => Backprop (V.Vector a) where
zero = zeroFunctor
add = addIsList
one = oneFunctor
Completely Custom
Completely custom instances are also possible; you just need to implement
zero
, add
, and one
as they make sense for your type. Just make sure that
you obey the laws for sane behavior!
Moving On
At this point, feel free to jump into the haddocks, or read on further for a list of applications and resources.