@@ -16,26 +16,22 @@ module Grenade.Layers.Convolution (
16 Convolution (..)
16 Convolution (..)
17 , Convolution' (..)
17 , Convolution' (..)
18 , randomConvolution
18 , randomConvolution
26−import Control.Monad.Random hiding (fromList)
21+import Control.Monad.Random hiding ( fromList )
27import Data.Maybe
22import Data.Maybe
28import Data.Proxy
23import Data.Proxy
29import Data.Singletons.TypeLits
24import Data.Singletons.TypeLits
30import GHC.TypeLits
25import GHC.TypeLits
32−import Numeric.LinearAlgebra hiding (uniformSample, konst)
27+import Numeric.LinearAlgebra hiding ( uniformSample, konst )
33import qualified Numeric.LinearAlgebra as LA
28import qualified Numeric.LinearAlgebra as LA
34import Numeric.LinearAlgebra.Static hiding ((|||), build, toRows)
29import Numeric.LinearAlgebra.Static hiding ((|||), build, toRows)
36import Grenade.Core.Network
31import Grenade.Core.Network
37import Grenade.Core.Shape
32import Grenade.Core.Shape
38import Grenade.Core.Vector
33import Grenade.Core.Vector
34+import Grenade.Layers.Convolution.Internal
40-- | A convolution layer for a neural network.
36-- | A convolution layer for a neural network.
41-- This uses the im2col convolution trick popularised by Caffe, which essentially turns the
37-- This uses the im2col convolution trick popularised by Caffe, which essentially turns the
@@ -153,18 +149,21 @@ instance ( KnownNat kernelRows
153 runForwards (Convolution kernel _) (S2D' input) =
149 runForwards (Convolution kernel _) (S2D' input) =
154 let ex = extract input
150 let ex = extract input
155 ek = extract kernel
151 ek = extract kernel
152+ ix = fromIntegral $ natVal (Proxy :: Proxy inputRows)
153+ iy = fromIntegral $ natVal (Proxy :: Proxy inputCols)
156 kx = fromIntegral $ natVal (Proxy :: Proxy kernelRows)
154 kx = fromIntegral $ natVal (Proxy :: Proxy kernelRows)
157 ky = fromIntegral $ natVal (Proxy :: Proxy kernelCols)
155 ky = fromIntegral $ natVal (Proxy :: Proxy kernelCols)
158 sx = fromIntegral $ natVal (Proxy :: Proxy strideRows)
156 sx = fromIntegral $ natVal (Proxy :: Proxy strideRows)
159 sy = fromIntegral $ natVal (Proxy :: Proxy strideCols)
157 sy = fromIntegral $ natVal (Proxy :: Proxy strideCols)
160 ox = fromIntegral $ natVal (Proxy :: Proxy outputRows)
158 ox = fromIntegral $ natVal (Proxy :: Proxy outputRows)
161 oy = fromIntegral $ natVal (Proxy :: Proxy outputCols)
159 oy = fromIntegral $ natVal (Proxy :: Proxy outputCols)
162− c = im2col kx ky sx sy ex
160+ c = im2colUnsafe kx ky sx sy ix iy ex
163 mt = c LA.<> ek
161 mt = c LA.<> ek
164− r = col2vid 1 1 1 1 ox oy mt
162+ r = col2vidUnsafe 1 1 1 1 ox oy mt
165 rs = fmap (fromJust . create) r
163 rs = fmap (fromJust . create) r
166 in S3D' $ mkVector rs
164 in S3D' $ mkVector rs
167− runBackards (Convolution kernel _) (S2D' input) (S3D' dEdy) =
166+ runBackwards (Convolution kernel _) (S2D' input) (S3D' dEdy) =
168 let ex = extract input
167 let ex = extract input
169 ix = fromIntegral $ natVal (Proxy :: Proxy inputRows)
168 ix = fromIntegral $ natVal (Proxy :: Proxy inputRows)
170 iy = fromIntegral $ natVal (Proxy :: Proxy inputCols)
169 iy = fromIntegral $ natVal (Proxy :: Proxy inputCols)
@@ -174,17 +173,19 @@ instance ( KnownNat kernelRows
174 sy = fromIntegral $ natVal (Proxy :: Proxy strideCols)
173 sy = fromIntegral $ natVal (Proxy :: Proxy strideCols)
175 ox = fromIntegral $ natVal (Proxy :: Proxy outputRows)
174 ox = fromIntegral $ natVal (Proxy :: Proxy outputRows)
176 oy = fromIntegral $ natVal (Proxy :: Proxy outputCols)
175 oy = fromIntegral $ natVal (Proxy :: Proxy outputCols)
176+ fl = fromIntegral $ natVal (Proxy :: Proxy filters)
177− c = im2col kx ky sx sy ex
178+ c = im2colUnsafe kx ky sx sy ix iy ex
179 eo = vecToList $ fmap extract dEdy
180 eo = vecToList $ fmap extract dEdy
180 ek = extract kernel
181 ek = extract kernel
182− vs = vid2col 1 1 1 1 ox oy eo
183+ vs = vid2colUnsafe fl 1 1 1 1 ox oy eo
184 kN = fromJust . create $ tr c LA.<> vs
185 kN = fromJust . create $ tr c LA.<> vs
185 dW = vs LA.<> tr ek
186 dW = vs LA.<> tr ek
187− xW = col2im kx ky sx sy ix iy dW
188+ xW = col2imUnsafe kx ky sx sy ix iy dW
188 in (Convolution' kN, S2D' . fromJust . create $ xW)
189 in (Convolution' kN, S2D' . fromJust . create $ xW)
@@ -215,12 +216,13 @@ instance ( KnownNat kernelRows
215 sy = fromIntegral $ natVal (Proxy :: Proxy strideCols)
216 sy = fromIntegral $ natVal (Proxy :: Proxy strideCols)
216 ox = fromIntegral $ natVal (Proxy :: Proxy outputRows)
217 ox = fromIntegral $ natVal (Proxy :: Proxy outputRows)
217 oy = fromIntegral $ natVal (Proxy :: Proxy outputCols)
218 oy = fromIntegral $ natVal (Proxy :: Proxy outputCols)
219+ ch = fromIntegral $ natVal (Proxy :: Proxy channels)
218− c = vid2col kx ky sx sy ix iy ex
220+ c = vid2colUnsafe ch kx ky sx sy ix iy ex
219 mt = c LA.<> ek
221 mt = c LA.<> ek
220− r = col2vid 1 1 1 1 ox oy mt
222+ r = col2vidUnsafe 1 1 1 1 ox oy mt
221 rs = fmap (fromJust . create) r
223 rs = fmap (fromJust . create) r
222 in S3D' $ mkVector rs
224 in S3D' $ mkVector rs
223− runBackards (Convolution kernel _) (S3D' input) (S3D' dEdy) =
225+ runBackwards (Convolution kernel _) (S3D' input) (S3D' dEdy) =
224 let ex = vecToList $ fmap extract input
226 let ex = vecToList $ fmap extract input
225 ix = fromIntegral $ natVal (Proxy :: Proxy inputRows)
227 ix = fromIntegral $ natVal (Proxy :: Proxy inputRows)
226 iy = fromIntegral $ natVal (Proxy :: Proxy inputCols)
228 iy = fromIntegral $ natVal (Proxy :: Proxy inputCols)
@@ -230,77 +232,18 @@ instance ( KnownNat kernelRows
230 sy = fromIntegral $ natVal (Proxy :: Proxy strideCols)
232 sy = fromIntegral $ natVal (Proxy :: Proxy strideCols)
231 ox = fromIntegral $ natVal (Proxy :: Proxy outputRows)
233 ox = fromIntegral $ natVal (Proxy :: Proxy outputRows)
232 oy = fromIntegral $ natVal (Proxy :: Proxy outputCols)
234 oy = fromIntegral $ natVal (Proxy :: Proxy outputCols)
235+ ch = fromIntegral $ natVal (Proxy :: Proxy channels)
236+ fl = fromIntegral $ natVal (Proxy :: Proxy filters)
233− c = vid2col kx ky sx sy ix iy ex
237+ c = vid2colUnsafe ch kx ky sx sy ix iy ex
235 eo = vecToList $ fmap extract dEdy
239 eo = vecToList $ fmap extract dEdy
236 ek = extract kernel
240 ek = extract kernel
238− vs = vid2col 1 1 1 1 ox oy eo
242+ vs = vid2colUnsafe fl 1 1 1 1 ox oy eo
240 kN = fromJust . create $ tr c LA.<> vs
244 kN = fromJust . create $ tr c LA.<> vs
242 dW = vs LA.<> tr ek
246 dW = vs LA.<> tr ek
244− xW = col2vid kx ky sx sy ix iy dW
248+ xW = col2vidUnsafe kx ky sx sy ix iy dW
245 in (Convolution' kN, S3D' . mkVector . fmap (fromJust . create) $ xW)
249 in (Convolution' kN, S3D' . mkVector . fmap (fromJust . create) $ xW)
247−im2col :: Int -> Int -> Int -> Int -> Matrix Double -> Matrix Double
248−im2col nrows ncols srows scols m =
249− let starts = fittingStarts (rows m) nrows srows (cols m) ncols scols
250− in im2colFit starts nrows ncols m
252−im2colFit :: [(Int,Int)] -> Int -> Int -> Matrix Double -> Matrix Double
253−im2colFit starts nrows ncols m =
254− let imRows = fmap (\start -> flatten $ subMatrix start (nrows, ncols) m) starts
257−vid2col :: Int -> Int -> Int -> Int -> Int -> Int -> [Matrix Double] -> Matrix Double
258−vid2col nrows ncols srows scols inputrows inputcols ms =
259− let starts = fittingStarts inputrows nrows srows inputcols ncols scols
260− subs = fmap (im2colFit starts nrows ncols) ms
261− in foldl1 (|||) subs
263−col2vid :: Int -> Int -> Int -> Int -> Int -> Int -> Matrix Double -> [Matrix Double]
264−col2vid nrows ncols srows scols drows dcols m =
265− let starts = fittingStart (cols m) (nrows * ncols) (nrows * ncols)
267− mats = fmap (\s -> subMatrix (0,s) (r, nrows * ncols) m) starts
268− colSts = fittingStarts drows nrows srows dcols ncols scols
269− in fmap (col2imfit colSts nrows ncols drows dcols) mats
271−col2im :: Int -> Int -> Int -> Int -> Int -> Int -> Matrix Double -> Matrix Double
272−col2im krows kcols srows scols drows dcols m =
273− let starts = fittingStarts drows krows srows dcols kcols scols
274− in col2imfit starts krows kcols drows dcols m
276−col2imfit :: [(Int,Int)] -> Int -> Int -> Int -> Int -> Matrix Double -> Matrix Double
277−col2imfit starts krows kcols drows dcols m =
278− let indicies = fmap (\[a,b] -> (a,b)) $ sequence [[0..(krows-1)], [0..(kcols-1)]]
279− convs = fmap (zip indicies . toList) . toRows $ m
280− pairs = zip convs starts
281− accums = concat $ fmap (\(conv',(stx',sty')) -> fmap (\((ix,iy), val) -> ((ix + stx', iy + sty'), val)) conv') pairs
282− in accum (LA.konst 0 (drows, dcols)) (+) accums
285−-- | These functions are not even remotely safe, but it's only called from the statically typed
286−-- commands, so we should be good ?!?!?
287−-- Returns the starting sub matrix locations which fit inside the larger matrix for the
288−-- convolution. Takes into account the stride and kernel size.
289−fittingStarts :: Int -> Int -> Int -> Int -> Int -> Int -> [(Int,Int)]
290−fittingStarts nrows kernelrows steprows ncols kernelcols stepcolsh =
291− let rs = fittingStart nrows kernelrows steprows
292− cs = fittingStart ncols kernelcols stepcolsh
293− ls = sequence [rs, cs]
294− in fmap (\[a,b] -> (a,b)) ls
296−-- | Returns the starting sub vector which fit inside the larger vector for the
297−-- convolution. Takes into account the stride and kernel size.
298−fittingStart :: Int -> Int -> Int -> [Int]
299−fittingStart width kernel steps =
300− let go left | left + kernel < width
301− = left : go (left + steps)
302− | left + kernel == width
305− = error "Kernel and step do not fit in matrix."