@@ -360,8 +360,51 @@ final class LayerTests: XCTestCase {
360
360
let expectedNoBias = Tensor < Float > ( shape: [ 1 , 4 , 2 , 1 ] ,
361
361
scalars: [ 0 , 4 , 4 , 20 , 16 , 56 , 40 , 104 ] )
362
362
XCTAssertEqual ( outputNoBias, expectedNoBias)
363
- }
364
-
363
+ }
364
+
365
+ func testTransposedConv2DGradient( ) {
366
+ let filter = Tensor ( shape: [ 3 , 3 , 2 , 4 ] , scalars: ( 0 ..< 72 ) . map ( Float . init) )
367
+ let bias = Tensor < Float > ( zeros: [ 2 ] )
368
+ let layer = TransposedConv2D < Float > ( filter: filter,
369
+ bias: bias,
370
+ activation: identity,
371
+ strides: ( 2 , 2 ) ,
372
+ padding: . same)
373
+ let input = Tensor ( shape: [ 2 , 2 , 2 , 4 ] , scalars: ( 0 ..< 32 ) . map ( Float . init) )
374
+ let grads = gradient ( at: input, layer) { $1 ( $0) . sum ( ) }
375
+ // The expected value of the gradient was computed using the following Python code:
376
+ // ```
377
+ // import tensorflow as tf
378
+ // x = tf.reshape(tf.range(32, dtype=tf.float32), [2, 2, 2, 4])
379
+ // filter = tf.reshape(tf.range(72, dtype=tf.float32), [3, 3, 2, 4])
380
+ // bias = tf.zeros([2])
381
+ // with tf.GradientTape() as tape:
382
+ // tape.watch([x, filter, bias])
383
+ // y = tf.math.reduce_sum(tf.nn.conv2d_transpose(input=x,
384
+ // filters=filter,
385
+ // output_shape=[2, 4, 4, 2],
386
+ // strides=[1, 2, 2, 1],
387
+ // data_format="NHWC",
388
+ // padding="SAME") + bias)
389
+ // print(tape.gradient(y, [x, filter, bias]))
390
+ // ```
391
+ XCTAssertEqual ( grads. 0 ,
392
+ [ [ [ [ 612 , 630 , 648 , 666 ] , [ 360 , 372 , 384 , 396 ] ] ,
393
+ [ [ 264 , 276 , 288 , 300 ] , [ 144 , 152 , 160 , 168 ] ] ] ,
394
+ [ [ [ 612 , 630 , 648 , 666 ] , [ 360 , 372 , 384 , 396 ] ] ,
395
+ [ [ 264 , 276 , 288 , 300 ] , [ 144 , 152 , 160 , 168 ] ] ] ] )
396
+ XCTAssertEqual ( grads. 1 . filter,
397
+ [ [ [ [ 112 , 120 , 128 , 136 ] , [ 112 , 120 , 128 , 136 ] ] ,
398
+ [ [ 112 , 120 , 128 , 136 ] , [ 112 , 120 , 128 , 136 ] ] ,
399
+ [ [ 48 , 52 , 56 , 60 ] , [ 48 , 52 , 56 , 60 ] ] ] ,
400
+ [ [ [ 112 , 120 , 128 , 136 ] , [ 112 , 120 , 128 , 136 ] ] ,
401
+ [ [ 112 , 120 , 128 , 136 ] , [ 112 , 120 , 128 , 136 ] ] ,
402
+ [ [ 48 , 52 , 56 , 60 ] , [ 48 , 52 , 56 , 60 ] ] ] ,
403
+ [ [ [ 40 , 44 , 48 , 52 ] , [ 40 , 44 , 48 , 52 ] ] ,
404
+ [ [ 40 , 44 , 48 , 52 ] , [ 40 , 44 , 48 , 52 ] ] ,
405
+ [ [ 16 , 18 , 20 , 22 ] , [ 16 , 18 , 20 , 22 ] ] ] ] )
406
+ XCTAssertEqual ( grads. 1 . bias, [ 32 , 32 ] )
407
+ }
365
408
366
409
func testTransposedConv3D( ) {
367
410
let filter = Tensor ( shape: [ 2 , 2 , 2 , 1 , 1 ] , scalars: ( 0 ..< 8 ) . map ( Float . init) )
@@ -1615,6 +1658,7 @@ final class LayerTests: XCTestCase {
1615
1658
( " testConv3DGradient " , testConv3DGradient) ,
1616
1659
( " testTransposedConv1D " , testTransposedConv1D) ,
1617
1660
( " testTransposedConv2D " , testTransposedConv2D) ,
1661
+ ( " testTransposedConv2DGradient " , testTransposedConv2DGradient) ,
1618
1662
( " testTransposedConv3D " , testTransposedConv3D) ,
1619
1663
( " testDepthwiseConv2D " , testDepthwiseConv2D) ,
1620
1664
( " testDepthwiseConv2DGradient " , testDepthwiseConv2DGradient) ,
0 commit comments