@@ -198,6 +198,55 @@ final class LayerTests: XCTestCase {
198
198
XCTAssertEqual ( output, expected)
199
199
}
200
200
201
+ func testConv3DGradient( ) {
202
+ let filter = Tensor ( shape: [ 1 , 4 , 4 , 1 , 1 ] , scalars: ( 0 ..< 16 ) . map ( Float . init) )
203
+ let bias = Tensor < Float > ( ones: [ 2 ] )
204
+ let layer = Conv3D ( filter: filter,
205
+ bias: bias,
206
+ activation: identity,
207
+ strides: ( 2 , 2 , 2 ) ,
208
+ padding: . same)
209
+ let input = Tensor ( shape: [ 1 , 4 , 4 , 4 , 1 ] , scalars: ( 0 ..< 64 ) . map ( Float . init) )
210
+ let grads = gradient ( at: input, layer) { $1 ( $0) . sum ( ) }
211
+ // The expected value of the gradient was computed using the following Python code:
212
+ // ```
213
+ // import tensorflow as tf
214
+ // x = tf.reshape(tf.range(64, dtype=tf.float32), [1, 4, 4, 4, 1])
215
+ // filter = tf.reshape(tf.range(72, dtype=tf.float32), [1, 4, 4, 1, 1])
216
+ // bias = tf.ones([2])
217
+ // with tf.GradientTape() as tape:
218
+ // tape.watch([x, filter, bias])
219
+ // y = tf.math.reduce_sum(tf.nn.conv3d(input=x,
220
+ // filters=filter,
221
+ // strides=[1, 2, 2, 2, 1],
222
+ // padding="SAME") + bias)
223
+ // print(tape.gradient(y, [x, filter, bias]))
224
+ // ```
225
+ XCTAssertEqual ( grads. 0 ,
226
+ [ [ [ [ [ 10.0 ] , [ 20.0 ] , [ 24.0 ] , [ 12.0 ] ] ,
227
+ [ [ 20.0 ] , [ 40.0 ] , [ 48.0 ] , [ 24.0 ] ] ,
228
+ [ [ 36.0 ] , [ 72.0 ] , [ 80.0 ] , [ 40.0 ] ] ,
229
+ [ [ 18.0 ] , [ 36.0 ] , [ 40.0 ] , [ 20.0 ] ] ] ,
230
+ [ [ [ 0.0 ] , [ 0.0 ] , [ 0.0 ] , [ 0.0 ] ] ,
231
+ [ [ 0.0 ] , [ 0.0 ] , [ 0.0 ] , [ 0.0 ] ] ,
232
+ [ [ 0.0 ] , [ 0.0 ] , [ 0.0 ] , [ 0.0 ] ] ,
233
+ [ [ 0.0 ] , [ 0.0 ] , [ 0.0 ] , [ 0.0 ] ] ] ,
234
+ [ [ [ 10.0 ] , [ 20.0 ] , [ 24.0 ] , [ 12.0 ] ] ,
235
+ [ [ 20.0 ] , [ 40.0 ] , [ 48.0 ] , [ 24.0 ] ] ,
236
+ [ [ 36.0 ] , [ 72.0 ] , [ 80.0 ] , [ 40.0 ] ] ,
237
+ [ [ 18.0 ] , [ 36.0 ] , [ 40.0 ] , [ 20.0 ] ] ] ,
238
+ [ [ [ 0.0 ] , [ 0.0 ] , [ 0.0 ] , [ 0.0 ] ] ,
239
+ [ [ 0.0 ] , [ 0.0 ] , [ 0.0 ] , [ 0.0 ] ] ,
240
+ [ [ 0.0 ] , [ 0.0 ] , [ 0.0 ] , [ 0.0 ] ] ,
241
+ [ [ 0.0 ] , [ 0.0 ] , [ 0.0 ] , [ 0.0 ] ] ] ] ] )
242
+ XCTAssertEqual ( grads. 1 . filter,
243
+ [ [ [ [ [ 84.0 ] ] , [ [ 168.0 ] ] , [ [ 176.0 ] ] , [ [ 88.0 ] ] ] ,
244
+ [ [ [ 168.0 ] ] , [ [ 336.0 ] ] , [ [ 352.0 ] ] , [ [ 176.0 ] ] ] ,
245
+ [ [ [ 200.0 ] ] , [ [ 400.0 ] ] , [ [ 416.0 ] ] , [ [ 208.0 ] ] ] ,
246
+ [ [ [ 100.0 ] ] , [ [ 200.0 ] ] , [ [ 208.0 ] ] , [ [ 104.0 ] ] ] ] ] )
247
+ XCTAssertEqual ( grads. 1 . bias, [ 8.0 , 8.0 ] )
248
+ }
249
+
201
250
func testDepthwiseConv2D( ) {
202
251
let filter = Tensor ( shape: [ 2 , 2 , 2 , 2 ] , scalars: ( 0 ..< 16 ) . map ( Float . init) )
203
252
let bias = Tensor < Float > ( [ 1 , 2 , 3 , 4 ] )
@@ -1274,6 +1323,7 @@ final class LayerTests: XCTestCase {
1274
1323
( " testConv2DGradient " , testConv2DGradient) ,
1275
1324
( " testConv2DDilation " , testConv2DDilation) ,
1276
1325
( " testConv3D " , testConv3D) ,
1326
+ ( " testConv3DGradient " , testConv3DGradient) ,
1277
1327
( " testDepthwiseConv2D " , testDepthwiseConv2D) ,
1278
1328
( " testDepthwiseConv2DGradient " , testDepthwiseConv2DGradient) ,
1279
1329
( " testSeparableConv1D " , testSeparableConv1D) ,
0 commit comments