{-# OPTIONS_HADDOCK show-extensions #-} {-# LANGUAGE TypeOperators #-} {-# LANGUAGE ScopedTypeVariables #-} {-| Module : Numeric.Neural.Convolution Description : convolutional layers Copyright : (c) Lars Brünjes, 2016 License : MIT Maintainer : brunjlar@gmail.com Stability : experimental Portability : portable This module defines /convolutional/ layers. -} module Numeric.Neural.Convolution ( focus , focus' , cover , cover' , convolution ) where import Control.Category import Data.FixedSize import Data.Functor.Compose (Compose(..)) import Data.Proxy import Data.Utils import GHC.TypeLits import GHC.TypeLits.Witnesses import Numeric.Neural.Layer import Numeric.Neural.Model import Prelude hiding (id, (.)) -- | Focuses on a specific region of a volume. -- focus :: (KnownNat m, KnownNat n, KnownNat d, KnownNat m', KnownNat n') => a -- ^ the element to use for out-of-bound indices -> Volume m n d a -- ^ the original 'Volume' -> (Int, Int) -- ^ the upper left corner of the focused region -> Volume m' n' d a focus x v (i, j) = generate $ \(i', j', k') -> maybe x id $ v !? (i + i', j + j', k') -- | Specialization of 'focus' to 'Volume's with numeric values, -- padding out-of-bound indices with zero. -- -- >>> :set -XDataKinds -- >>> let v = generate (\(i, j, k) -> i + j + k) :: Volume 2 2 3 Int -- >>> focus' v (0, 1) :: Volume 1 2 3 Int -- Volume (Matrix [[[1,2,3],[0,0,0]]]) -- focus' :: (KnownNat m, KnownNat n, KnownNat d, KnownNat m', KnownNat n', Num a) => Volume m n d a -- ^ the original 'Volume' -> (Int, Int) -- ^ the upper left corner of the focused region -> Volume m' n' d a focus' = focus 0 -- | Covers a 'Volume' with (smaller) regions. -- cover :: forall m n d a s m' n'. (KnownNat m, KnownNat n, KnownNat d, KnownNat s, KnownNat m', KnownNat n') => a -- ^ the element to use for out-of-bound indices -> Proxy s -- ^ a proxy to the region size -> Int -- ^ the stride -> Volume m n d a -- ^ the original 'Volume' -> Matrix m' n' (Vector (s * s * d) a) cover x ps stride v = withNatOp (%*) ps ps $ withNatOp (%*) (Proxy :: Proxy (s * s)) (Proxy :: Proxy d) $ generate $ toVector . volume where volume :: (Int, Int) -> Volume s s d a volume (i, j) = focus x v (stride * i, stride * j) -- | Specialization of 'cover' to 'Volume's with numeric values. -- -- >>> :set -XDataKinds -- >>> let v = generate (\(i, j, _) -> i + j) :: Volume 4 4 1 Int -- >>> cover' (Proxy :: Proxy 3) 1 v :: Matrix 2 2 (Vector 9 Int) -- Matrix [[[0,1,2,1,2,3,2,3,4],[1,2,3,2,3,4,3,4,5]],[[1,2,3,2,3,4,3,4,5],[2,3,4,3,4,5,4,5,6]]] -- cover' :: forall m n d a s m' n'. (KnownNat m, KnownNat n, KnownNat d, KnownNat s, KnownNat m', KnownNat n', Num a) => Proxy s -- ^ a proxy to the region size -> Int -- ^ the stride -> Volume m n d a -- ^ the original 'Volume' -> Matrix m' n' (Vector (s * s * d) a) cover' = cover 0 -- | Convolves a 'Layer' over a 'Volume'. -- convolution :: forall s m n d m' n' d'. (KnownNat s, KnownNat m, KnownNat n, KnownNat d, KnownNat m', KnownNat n') => Proxy s -- ^ a proxy to the region size -> Int -- ^ the stride -> Layer (s * s * d) d' -- ^ the layer to convolve -> Component (Volume m n d) (Volume m' n' d') convolution ps stride l = cArr (Diff $ toVolume . unCompose) . cConvolve l . cArr (Diff $ Compose . cover' ps stride) where unCompose :: Compose f g a -> f (g a) unCompose (Compose x) = x _ = natVal (Proxy :: Proxy d)