packages feed

goal-probability-0.1: Goal/Probability/Graphical/NeuralNetwork.hs

-- | Multilayer perceptrons and backpropagation.
module Goal.Probability.Graphical.NeuralNetwork where


--- Imports ---


-- Goal --

import Goal.Geometry
import Goal.Probability.ExponentialFamily
import Goal.Probability.Graphical

import qualified Data.Vector.Storable as C


--- Neural Networks ---


-- | A mutlilayer perceptron with three layers.
data NeuralNetwork m n o = NeuralNetwork m n o deriving (Eq, Read, Show)


--- Functions ---

splitNeuralNetwork
    :: (Manifold m, Manifold n, Manifold o)
    => Function Mixture Mixture :#: NeuralNetwork m n o
    -> (Natural :#: m, NaturalFunction :#: Tensor m n, Natural :#: n, NaturalFunction :#: Tensor n o)
-- | Splits the 'NeuralNetwork' into its component affine transformations.
splitNeuralNetwork nnp =
    let (NeuralNetwork m n o) = manifold nnp
        tns1 = Tensor m n
        tns2 = Tensor n o
        css = coordinates nnp
        (mcs,css') = C.splitAt (dimension m) css
        (mtx1cs,css'') = C.splitAt (dimension tns1) css'
        (ncs,mtx2cs) = C.splitAt (dimension n) css''
        mp = fromCoordinates m mcs
        mtx1 = fromCoordinates tns1 mtx1cs
        np = fromCoordinates n ncs
        mtx2 = fromCoordinates tns2 mtx2cs
     in (mp,mtx1,np,mtx2)

joinNeuralNetwork
    :: (Manifold m, Manifold n, Manifold o)
    => Natural :#: m
    -> NaturalFunction :#: Tensor m n
    -> Natural :#: n
    -> NaturalFunction :#: Tensor n o
    -> Function Mixture Mixture :#: NeuralNetwork m n o
-- | Construct a 'NeuralNetwork' from component affine transformations.
joinNeuralNetwork mp mtx1 np mtx2 =
    let (Tensor m n) = manifold mtx1
        (Tensor _ o) = manifold mtx2
     in fromCoordinates (NeuralNetwork m n o) $ coordinates mp C.++ coordinates mtx1 C.++ coordinates np C.++ coordinates mtx2

feedForward
    :: (ExponentialFamily m, ExponentialFamily n, Manifold o)
    => Function Mixture Mixture :#: NeuralNetwork m n o
    -> [Mixture :#: o]
    -> ([Natural :#: n], [Mixture :#: n], [Natural :#: m], [Mixture :#: m])
-- | Feeds an input forward through the network, and returns every step of
-- the computation.
feedForward nnp xps =
    let (mp,mtx1,np,mtx2) = splitNeuralNetwork nnp
        nyps = map (<+> np) $ mtx2 >$> xps
        yps = potentialMapping <$> nyps
        nzps = map (<+> mp) $ mtx1 >$> yps
        zps = potentialMapping <$> nzps
     in (nyps,yps,nzps,zps)

feedBackward
    :: (Legendre Natural m, Legendre Natural n, Riemannian Natural m, Riemannian Natural n, Manifold o)
    => Function Mixture Mixture :#: NeuralNetwork m n o
    -> [Mixture :#: o]
    -> [Natural :#: n]
    -> [Mixture :#: n]
    -> [Natural :#: m]
    -> [Natural :#: m]
    -> Differentials :#: Tangent (Function Mixture Mixture) (NeuralNetwork m n o)
-- | Given the results of a feed forward application, back propagates a
-- given error (last input) through the network.
feedBackward nnp xps nyps yps nzps errs1 =
    let (_,mtx1,_,_) = splitNeuralNetwork nnp
        dmps = zipWith legendreFlat nzps errs1
        dmtx1s = [ dmp >.< yp | (dmp,yp) <- zip dmps yps ]
        errs2 = matrixTranspose mtx1 >$> dmps
        dnps = zipWith legendreFlat nyps errs2
        dmtx2s = [ dnp >.< xp | (dnp,xp) <- zip dnps xps ]
     in fromCoordinates (Tangent nnp) $ coordinates (meanPoint dmps) C.++ coordinates (meanPoint dmtx1s)
            C.++ coordinates (meanPoint dnps) C.++ coordinates (meanPoint dmtx2s)

meanSquaredBackpropagation
    :: (Riemannian Natural m, Riemannian Natural n, ExponentialFamily m, ExponentialFamily n, Manifold o)
    => Function Mixture Mixture :#: NeuralNetwork m n o
    -> [Mixture :#: o]
    -> [Mixture :#: m]
    -> Differentials :#: Tangent (Function Mixture Mixture) (NeuralNetwork m n o)
-- | Backpropagation algorithm with the mean squared error function.
meanSquaredBackpropagation nnp xps tps =
    let (nyps,yps,nzps,zps) = feedForward nnp xps
        errs1 = [ alterChart Natural $ zp <-> tp | (tp,zp) <- zip tps zps ]
     in feedBackward nnp xps nyps yps nzps errs1


--- Instances ---


instance (Manifold m, Manifold n, Manifold o) => Manifold (NeuralNetwork m n o) where
    dimension (NeuralNetwork m n o) =  dimension m + dimension m * dimension n + dimension n + dimension n * dimension o

instance (ExponentialFamily m, ExponentialFamily n, Manifold o) => Map (NeuralNetwork m n o) where
    type Domain (NeuralNetwork m n o) = o
    domain (NeuralNetwork _ _ o) = o
    type Codomain (NeuralNetwork m n o) = m
    codomain (NeuralNetwork m _ _) = m

instance (ExponentialFamily m, ExponentialFamily n, Manifold o) => Apply Mixture Mixture (NeuralNetwork m n o) where
    (>$>) nnp xps =
        let (_,_,_,zps) = feedForward nnp xps
         in zps


--- Backprop ---


{-
--backpropagation :: NeuralNetwork (m ': ms) -> (Mixture :#: m -> Mixture :#: m) -> Differential :#:
backpropagate :: NeuralNetwork (m ': ms) -> Mixture :#: m -> Differential :#: NeuralNetwork (m ': ms)
backpropagate nnp dp =



--- Internal ---


popManifold :: NeuralNetwork (m ': ms) -> (m, NeuralNetwork ms)
popManifold (Layer m ms) = (m,ms)

popNeuralNetwork
    :: (Manifold m, Manifold n, Manifold (NeuralNetwork (n ': ms)))
    => Function Mixture Mixture :#: NeuralNetwork (m ': n ': ms)
    -> (Natural :#: m, NaturalFunction :#: Tensor m n, Function Mixture Mixture :#: NeuralNetwork (n ': ms))
popNeuralNetwork nnp =
    let (m,nn') = popManifold $ manifold nnp
        (n,_) = popManifold nn'
        tns = Tensor m n
        css = coordinates nnp
        (mcs,css') = C.splitAt (dimension m) css
        (mtxcs,nncs') = C.splitAt (dimension tns) css'
        mp = fromCoordinates m mcs
        mtx = fromCoordinates tns mtxcs
        nnp' = fromCoordinates nn' nncs'
     in (mp,mtx,nnp')

feedForward
    :: Function Mixture Mixture :#: NeuralNetwork ms
    -> [Mixture :#: Domain (NeuralNetwork ms)]
    -> [Mixture :#: Responses ms]
feedForward nnp0 xps0 =
    recurse nnp0 xps0 [ chart Mixture . fromCoordinates (Responses $ Layer (manifold xp) Nub) | xp <- xps ]
        where recurse nnp xps rss =
                  let (b,mtx,nnp') = popNeuralNetwork nnp
                      yps = nnp' >$> xps
                   in map (potentialMapping . (<+> b)) $ mtx >$> ys


feedBackward
    :: [Mixture :#: Codomain (NeuralNetwork ms)]
    -> [Mixture :#: Responses ms]
    -> Differential :#: Tangent (Function Mixture Mixture) (NeuralNetwork ms)
feedBackward = undefined

--- Instances ---


-- Responses --

instance Eq (Responses '[]) where
    (==) _ _ = True

instance (Eq m, Eq (NeuralNetwork ms)) => Eq (Responses (m ': ms)) where
    (==) (Responses (Layer m ms)) (Responses (Layer m' ms'))
        | m == m' = ms == ms'
        | otherwise = False

instance Manifold (Responses '[]) where
    dimension _ = 0


instance (Manifold m, Manifold (NeuralNetwork ms)) => Manifold (Responses (m ': ms)) where
    dimension (Responses (Layer m ms)) =  dimension m + dimension ms


-- NeuralNetwork --

instance Eq (NeuralNetwork '[]) where
    (==) _ _ = True

instance (Eq m, Eq (NeuralNetwork ms)) => Eq (NeuralNetwork (m ': ms)) where
    (==) (Layer m ms) (Layer m' ms')
        | m == m' = ms == ms'
        | otherwise = False

instance Manifold (NeuralNetwork '[]) where
    dimension _ = 0

instance Manifold m => Manifold (NeuralNetwork '[m]) where
    dimension _ = 0

instance (Manifold m, Manifold n, Manifold (NeuralNetwork (n ': ms))) => Manifold (NeuralNetwork (m ': n ': ms)) where
    dimension (Layer m (Layer n ms)) =  dimension m + dimension m * dimension n + dimension (Layer n ms)

instance Manifold m => Map (NeuralNetwork '[m]) where
    type Domain (NeuralNetwork '[m]) = m
    domain (Layer m _) = m
    type Codomain (NeuralNetwork '[m]) = m
    codomain (Layer m _) = m

instance (ExponentialFamily m, Manifold n) => Apply Mixture Mixture (NeuralNetwork '[m,n]) where
    (>$>) p xs =
        let (b,mtx,_) = popNeuralNetwork p
         in map (potentialMapping . (<+> b)) $ mtx >$> xs

instance (ExponentialFamily m, Manifold n, Map (NeuralNetwork (n ': ms)))
    => Map (NeuralNetwork (m ': n ': ms)) where
    type Domain (NeuralNetwork (m ': n ': ms)) = Domain (NeuralNetwork (n ': ms))
    domain (Layer _ nn) = domain nn
    type Codomain (NeuralNetwork (m ': n ': ms)) = m
    codomain (Layer m _) = m

instance (ExponentialFamily m, Manifold n, Apply Mixture Mixture (NeuralNetwork (n ': o ': ms)))
    => Apply Mixture Mixture (NeuralNetwork (m ': n ': o ': ms)) where
    (>$>) p xs =
        let (b,mtx,p') = popNeuralNetwork p
            ys = p' >$> xs
         in map (potentialMapping . (<+> b)) $ mtx >$> ys
    -}