@@ -0,0 +1,251 @@
1+{-# LANGUAGE TemplateHaskell #-}
2+{-# LANGUAGE DataKinds #-}
4+{-# OPTIONS_GHC -fno-warn-missing-signatures #-}
5+module Test.Grenade.Layers.Convolution where
7+import Control.Monad.Identity
9+import Grenade.Core.Shape
10+import Grenade.Core.Vector as Grenade
11+import Grenade.Core.Network
12+import Grenade.Layers.Convolution
14+import Numeric.LinearAlgebra hiding (uniformSample, konst, (===))
15+import qualified Numeric.LinearAlgebra.Static as HStatic
17+import Test.QuickCheck hiding ((><))
19+prop_im2col_no_stride = once $
23+ , 9.0, 10.0, 11.0, 12.0 ]
28+ , 5.0, 6.0, 9.0, 10.0
29+ , 6.0, 7.0, 10.0, 11.0
30+ , 7.0, 8.0, 11.0, 12.0 ]
31+ out = im2col 2 2 1 1 input
34+prop_im2col_stride = once $
38+ , 9.0, 10.0, 11.0, 12.0 ]
42+ , 5.0, 6.0, 9.0, 10.0
43+ , 7.0, 8.0, 11.0, 12.0 ]
44+ out = im2col 2 2 1 2 input
47+prop_im2col_other = once $
51+ , 9.0, 10.0, 11.0, 12.0 ]
53+ [ 1.0, 2.0, 5.0, 6.0 , 9.0, 10.0
54+ , 3.0, 4.0, 7.0, 8.0 , 11.0 ,12.0 ]
55+ out = im2col 3 2 1 2 input
58+-- If there's no overlap (stride is the same size as the kernel)
59+-- then col2im . im2col should be symmetric.
60+prop_im2col_sym_on_same_stride = once $
64+ , 9.0, 10.0, 11.0, 12.0 ]
65+ out = col2im 3 2 3 2 3 4 . im2col 3 2 3 2 $ input
68+-- If there is an overlap, then the gradient passed back should be
69+-- the sum of the gradients across the filters.
70+prop_im2col_col2im_additive = once $
74+ , 1.0, 1.0, 1.0, 1.0 ]
78+ , 1.0, 2.0, 2.0, 1.0 ]
79+ out = col2im 2 2 1 1 3 4 . im2col 2 2 1 1 $ input
82+prop_simple_conv_forwards = once $
83+ -- Create a convolution kernel with 4 filters.
84+ -- [ 1, 0 [ 0, 1 [ 0, 1 [ 0, 0
85+ -- , 0,-1 ] ,-1, 0 ] , 1, 0 ] ,-1,-1 ]
86+ let myKernel = (HStatic.matrix
89+ , 0.0, -1.0, 1.0, -1.0
90+ ,-1.0, 0.0, 0.0, -1.0 ] :: HStatic.L 4 4)
91+ zeroKernel = (HStatic.matrix
95+ , 0.0, 0.0, 0.0, 0.0 ] :: HStatic.L 4 4)
96+ --expectedKernel = (HStatic.matrix
97+ -- [ 0.0, 0.0, 0.0, -2.0
98+ -- ,-2.0, 1.0, 1.0, -5.0
99+ -- ,-3.0, -1.0, 1.0, -5.0
100+ -- ,-5.0, 0.0, 0.0, -7.0 ] :: HStatic.L 4 4)
102+ convLayer = Convolution myKernel zeroKernel :: Convolution 1 4 2 2 1 1
104+ input = S2D' (HStatic.matrix
106+ , 3.0, 4.0, 6.0] :: HStatic.L 2 3)
108+ expect = ([(HStatic.matrix
109+ [ -3.0 , -4.0 ] :: HStatic.L 1 2)
111+ [ -1.0 , 1.0 ] :: HStatic.L 1 2)
113+ [ 5.0 , 9.0 ] :: HStatic.L 1 2)
115+ [ -7.0 , -10.0 ] :: HStatic.L 1 2)]) :: [HStatic.L 1 2]
116+ out = runIdentity $ runForwards convLayer input :: S' ('D3 1 2 4)
118+ grad = S3D' ( mkVector
120+ [ 1 , 0 ] :: HStatic.L 1 2)
122+ [ 0 , 0 ] :: HStatic.L 1 2)
124+ [ 0 , 0 ] :: HStatic.L 1 2)
126+ [ 0 , 1 ] :: HStatic.L 1 2)] ) :: S' ('D3 1 2 4)
128+ expectBack = (HStatic.matrix
130+ , 0.0, -2.0,-1.0] :: HStatic.L 2 3)
131+ (nc, inX) = runIdentity $ runBackards 1 convLayer input grad :: ( Convolution 1 4 2 2 1 1 , S' ('D2 2 3))
133+ in case (out, inX, nc) of
134+ (S3D' out' , S2D' inX', Convolution _ _)
135+ -> ((HStatic.extract <$> expect) === (HStatic.extract <$> vecToList out'))
136+ .&&. ((HStatic.extract expectBack) === (HStatic.extract inX'))
137+ -- Temporarily disabled, as l2 adjustment puts in off 5%
138+ -- .&&. HStatic.extract expectedKernel === HStatic.extract kernel'
141+prop_vid2col_no_stride = once $
143+ [ 1.0, 2.0, 3.0, 4.0
144+ , 5.0, 6.0, 7.0, 8.0
145+ , 9.0, 10.0, 11.0, 12.0 ]
147+ [ 21.0, 22.0, 23.0, 24.0
148+ , 25.0, 26.0, 27.0, 28.0
149+ , 29.0, 30.0, 31.0, 32.0 ] ]
151+ [ 1.0, 2.0, 5.0, 6.0 , 21.0, 22.0, 25.0, 26.0
152+ , 2.0, 3.0, 6.0, 7.0 , 22.0, 23.0, 26.0, 27.0
153+ , 3.0, 4.0, 7.0, 8.0 , 23.0, 24.0, 27.0, 28.0
154+ , 5.0, 6.0, 9.0, 10.0 , 25.0, 26.0, 29.0, 30.0
155+ , 6.0, 7.0, 10.0, 11.0 , 26.0, 27.0, 30.0, 31.0
156+ , 7.0, 8.0, 11.0, 12.0 , 27.0, 28.0, 31.0, 32.0 ]
157+ out = vid2col 2 2 1 1 3 4 input
160+prop_vid2col_stride = once $
162+ [ 1.0, 2.0, 3.0, 4.0
163+ , 5.0, 6.0, 7.0, 8.0
164+ , 9.0, 10.0, 11.0, 12.0 ]
166+ [ 21.0, 22.0, 23.0, 24.0
167+ , 25.0, 26.0, 27.0, 28.0
168+ , 29.0, 30.0, 31.0, 32.0 ] ]
170+ [ 1.0, 2.0, 5.0, 6.0 , 21.0, 22.0, 25.0, 26.0
171+ , 3.0, 4.0, 7.0, 8.0 , 23.0, 24.0, 27.0, 28.0
172+ , 5.0, 6.0, 9.0, 10.0 , 25.0, 26.0, 29.0, 30.0
173+ , 7.0, 8.0, 11.0, 12.0 , 27.0, 28.0, 31.0, 32.0 ]
174+ out = vid2col 2 2 1 2 3 4 input
178+prop_vid2col_invert = once $
180+ [ 1.0, 2.0, 3.0, 4.0
181+ , 5.0, 6.0, 7.0, 8.0
182+ , 9.0, 10.0, 11.0, 12.0 ]
184+ [ 21.0, 22.0, 23.0, 24.0
185+ , 25.0, 26.0, 27.0, 28.0
186+ , 29.0, 30.0, 31.0, 32.0 ] ]
187+ out = col2vid 3 2 3 2 3 4 . vid2col 3 2 3 2 3 4 $ input
190+-- This test show that 2D convs act the same
191+-- 3D convs with one layer
192+prop_single_conv_forwards = once $
193+ -- Create a convolution kernel with 4 filters.
194+ -- [ 1, 0 [ 0, 1 [ 0, 1 [ 0, 0
195+ -- , 0,-1 ] ,-1, 0 ] , 1, 0 ] ,-1,-1 ]
196+ let myKernel = (HStatic.matrix
197+ [ 1.0, 0.0, 0.0, 0.0
198+ , 0.0, 1.0, 1.0, 0.0
199+ , 0.0, -1.0, 1.0, -1.0
200+ ,-1.0, 0.0, 0.0, -1.0 ] :: HStatic.L 4 4)
201+ zeroKernel = (HStatic.matrix
202+ [ 0.0, 0.0, 0.0, 0.0
203+ , 0.0, 0.0, 0.0, 0.0
204+ , 0.0, 0.0, 0.0, 0.0
205+ , 0.0, 0.0, 0.0, 0.0 ] :: HStatic.L 4 4)
206+ --expectedKernel = (HStatic.matrix
207+ -- [ 0.0, 0.0, 0.0, -2.0
208+ -- ,-2.0, 1.0, 1.0, -5.0
209+ -- ,-3.0, -1.0, 1.0, -5.0
210+ -- ,-5.0, 0.0, 0.0, -7.0 ] :: HStatic.L 4 4)
212+ convLayer = Convolution myKernel zeroKernel :: Convolution 1 4 2 2 1 1
214+ input = S3D' ( mkVector [HStatic.matrix
216+ , 3.0, 4.0, 6.0] :: HStatic.L 2 3] ) :: S' ('D3 2 3 1)
218+ expect = ([(HStatic.matrix
219+ [ -3.0 , -4.0 ] :: HStatic.L 1 2)
221+ [ -1.0 , 1.0 ] :: HStatic.L 1 2)
223+ [ 5.0 , 9.0 ] :: HStatic.L 1 2)
225+ [ -7.0 , -10.0 ] :: HStatic.L 1 2)]) :: [HStatic.L 1 2]
226+ out = runIdentity $ runForwards convLayer input :: S' ('D3 1 2 4)
228+ grad = S3D' ( mkVector
230+ [ 1 , 0 ] :: HStatic.L 1 2)
232+ [ 0 , 0 ] :: HStatic.L 1 2)
234+ [ 0 , 0 ] :: HStatic.L 1 2)
236+ [ 0 , 1 ] :: HStatic.L 1 2)] ) :: S' ('D3 1 2 4)
238+ expectBack = (HStatic.matrix
240+ , 0.0, -2.0,-1.0] :: HStatic.L 2 3)
241+ (nc, inX) = runIdentity $ runBackards 1 convLayer input grad :: ( Convolution 1 4 2 2 1 1 , S' ('D3 2 3 1))
243+ in case (out, inX, nc) of
244+ (S3D' out' , S3D' inX', Convolution _ _)
245+ -> ((HStatic.extract <$> expect) === (HStatic.extract <$> vecToList out'))
246+ .&&. ([HStatic.extract expectBack] === (HStatic.extract <$> vecToList inX'))
247+ -- .&&. HStatic.extract expectedKernel === HStatic.extract kernel'
251+tests = $quickCheckAll