1 /* Copyright 2016 The TensorFlow Authors. All Rights Reserved.
2
3 Licensed under the Apache License, Version 2.0 (the "License");
4 you may not use this file except in compliance with the License.
5 You may obtain a copy of the License at
6
7 http://www.apache.org/licenses/LICENSE-2.0
8
9 Unless required by applicable law or agreed to in writing, software
10 distributed under the License is distributed on an "AS IS" BASIS,
11 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 See the License for the specific language governing permissions and
13 limitations under the License.
14 ==============================================================================*/
15
16 #include "tensorflow/cc/framework/grad_op_registry.h"
17 #include "tensorflow/cc/framework/gradient_checker.h"
18 #include "tensorflow/cc/framework/testutil.h"
19 #include "tensorflow/cc/gradients/grad_testutil.h"
20 #include "tensorflow/cc/ops/array_ops_internal.h"
21 #include "tensorflow/cc/ops/standard_ops.h"
22 #include "tensorflow/core/framework/tensor_testutil.h"
23 #include "tensorflow/core/lib/core/status_test_util.h"
24
25 namespace tensorflow {
26 namespace {
27
28 using namespace ops; // NOLINT(build/namespaces)
29 using ops::internal::MirrorPadGrad;
30
31 class ArrayGradTest : public ::testing::Test {
32 protected:
ArrayGradTest()33 ArrayGradTest() : scope_(Scope::NewRootScope()) {}
34
RunTest(const Output & x,const TensorShape & x_shape,const Output & y,const TensorShape & y_shape)35 void RunTest(const Output& x, const TensorShape& x_shape, const Output& y,
36 const TensorShape& y_shape) {
37 TF_ASSERT_OK(scope_.status());
38 float max_error;
39 TF_ASSERT_OK((ComputeGradientError<float, float, float>(
40 scope_, {x}, {x_shape}, {y}, {y_shape}, &max_error)));
41 EXPECT_LT(max_error, 1e-3);
42 }
43
RunTest(const OutputList & xs,const std::vector<TensorShape> & x_shapes,const OutputList & ys,const std::vector<TensorShape> & y_shapes)44 void RunTest(const OutputList& xs, const std::vector<TensorShape>& x_shapes,
45 const OutputList& ys, const std::vector<TensorShape>& y_shapes) {
46 TF_ASSERT_OK(scope_.status());
47 float max_error;
48 TF_ASSERT_OK((ComputeGradientError<float, float, float>(
49 scope_, xs, x_shapes, ys, y_shapes, &max_error)));
50 EXPECT_LT(max_error, 1e-3);
51 }
52
53 Scope scope_;
54 };
55
TEST_F(ArrayGradTest,StackGrad_Axis0)56 TEST_F(ArrayGradTest, StackGrad_Axis0) {
57 TensorShape x_shape({1, 2, 3});
58 std::vector<Output> xs;
59 xs.push_back(Placeholder(scope_, DT_FLOAT, Placeholder::Shape(x_shape)));
60 xs.push_back(Placeholder(scope_, DT_FLOAT, Placeholder::Shape(x_shape)));
61 auto y = Stack(scope_, xs, Stack::Axis(0));
62 TensorShape y_shape({2, 1, 2, 3});
63 RunTest(xs, {x_shape, x_shape}, {y}, {y_shape});
64 }
65
TEST_F(ArrayGradTest,StackGrad_Axis1)66 TEST_F(ArrayGradTest, StackGrad_Axis1) {
67 TensorShape x_shape({1, 2, 3});
68 std::vector<Output> xs;
69 xs.push_back(Placeholder(scope_, DT_FLOAT, Placeholder::Shape(x_shape)));
70 xs.push_back(Placeholder(scope_, DT_FLOAT, Placeholder::Shape(x_shape)));
71 auto y = Stack(scope_, xs, Stack::Axis(1));
72 TensorShape y_shape({1, 2, 2, 3});
73 RunTest(xs, {x_shape, x_shape}, {y}, {y_shape});
74 }
75
TEST_F(ArrayGradTest,UnstackGrad_Axis0)76 TEST_F(ArrayGradTest, UnstackGrad_Axis0) {
77 TensorShape x_shape({4, 2, 3});
78 auto x = Placeholder(scope_, DT_FLOAT, Placeholder::Shape(x_shape));
79 // Unstacking the first dimension results in 4 outputs.
80 std::vector<TensorShape> y_shapes(4, TensorShape({2, 3}));
81 auto y = Unstack(scope_, x, 4, Unstack::Axis(0));
82 RunTest({x}, {x_shape}, y.output, y_shapes);
83 }
84
TEST_F(ArrayGradTest,UnstackGrad_Axis1)85 TEST_F(ArrayGradTest, UnstackGrad_Axis1) {
86 TensorShape x_shape({4, 2, 3});
87 auto x = Placeholder(scope_, DT_FLOAT, Placeholder::Shape(x_shape));
88 // Unstacking the second dimension results in 2 outputs.
89 std::vector<TensorShape> y_shapes(2, TensorShape({4, 3}));
90 auto y = Unstack(scope_, x, 2, Unstack::Axis(1));
91 RunTest({x}, {x_shape}, y.output, y_shapes);
92 }
93
TEST_F(ArrayGradTest,IdentityGrad)94 TEST_F(ArrayGradTest, IdentityGrad) {
95 TensorShape shape({5, 2});
96 auto x = Placeholder(scope_, DT_FLOAT, Placeholder::Shape(shape));
97 auto y = Identity(scope_, x);
98 RunTest(x, shape, y, shape);
99 }
100
TEST_F(ArrayGradTest,SplitGrad)101 TEST_F(ArrayGradTest, SplitGrad) {
102 TensorShape x_shape({5, 2});
103 auto x = Placeholder(scope_, DT_FLOAT, Placeholder::Shape(x_shape));
104 // Split along the second dimension.
105 auto split_dim = Const(scope_, 1, {});
106 auto y = Split(scope_, split_dim, x, /* num_split */ 2);
107 TensorShape y_shape = TensorShape({5, 1});
108 RunTest({x}, {x_shape}, y.output, {y_shape, y_shape});
109 }
110
TEST_F(ArrayGradTest,FillGrad)111 TEST_F(ArrayGradTest, FillGrad) {
112 TensorShape x_shape({});
113 auto x = Placeholder(scope_, DT_FLOAT, Placeholder::Shape(x_shape));
114 TensorShape y_shape({2, 5, 3});
115 auto y = Fill(scope_, {2, 5, 3}, x);
116 RunTest(x, x_shape, y, y_shape);
117 }
118
TEST_F(ArrayGradTest,DiagGrad)119 TEST_F(ArrayGradTest, DiagGrad) {
120 TensorShape x_shape({5, 2});
121 auto x = Placeholder(scope_, DT_FLOAT, Placeholder::Shape(x_shape));
122 auto y = Diag(scope_, x);
123 TensorShape y_shape({5, 2, 5, 2});
124 RunTest(x, x_shape, y, y_shape);
125 }
126
TEST_F(ArrayGradTest,DiagPartGrad)127 TEST_F(ArrayGradTest, DiagPartGrad) {
128 TensorShape x_shape({5, 2, 5, 2});
129 auto x = Placeholder(scope_, DT_FLOAT, Placeholder::Shape(x_shape));
130 auto y = DiagPart(scope_, x);
131 TensorShape y_shape({5, 2});
132 RunTest(x, x_shape, y, y_shape);
133 }
134
TEST_F(ArrayGradTest,MatrixDiagGrad)135 TEST_F(ArrayGradTest, MatrixDiagGrad) {
136 TensorShape x_shape({5, 2});
137 auto x = Placeholder(scope_, DT_FLOAT, Placeholder::Shape(x_shape));
138 auto y = MatrixDiag(scope_, x);
139 TensorShape y_shape({5, 2, 2});
140 RunTest(x, x_shape, y, y_shape);
141 }
142
TEST_F(ArrayGradTest,MatrixBandPartGrad)143 TEST_F(ArrayGradTest, MatrixBandPartGrad) {
144 TensorShape shape({5, 5});
145 auto x = Placeholder(scope_, DT_FLOAT, Placeholder::Shape(shape));
146 const int64 num_lower = 1;
147 const int64 num_upper = 2;
148 auto y = MatrixBandPart(scope_, x, num_lower, num_upper);
149 RunTest(x, shape, y, shape);
150 }
151
TEST_F(ArrayGradTest,GatherNdGrad_SimpleIndexing)152 TEST_F(ArrayGradTest, GatherNdGrad_SimpleIndexing) {
153 TensorShape x_shape({2, 2});
154 auto x = Placeholder(scope_, DT_FLOAT, Placeholder::Shape(x_shape));
155 auto indices = Const(scope_, {{0, 0}, {1, 1}});
156 TensorShape y_shape({2});
157 auto y = GatherNd(scope_, x, indices);
158 RunTest(x, x_shape, y, y_shape);
159 }
160
TEST_F(ArrayGradTest,GatherNdGrad_SliceIndexing)161 TEST_F(ArrayGradTest, GatherNdGrad_SliceIndexing) {
162 TensorShape shape({2, 2});
163 auto x = Placeholder(scope_, DT_FLOAT, Placeholder::Shape(shape));
164 auto indices = Const(scope_, {{1}, {0}});
165 auto y = GatherNd(scope_, x, indices);
166 RunTest(x, shape, y, shape);
167 }
168
TEST_F(ArrayGradTest,CheckNumericsGrad)169 TEST_F(ArrayGradTest, CheckNumericsGrad) {
170 TensorShape shape({5, 2});
171 auto x = Placeholder(scope_, DT_FLOAT, Placeholder::Shape(shape));
172 auto y = CheckNumerics(scope_, x, "CheckNumerics failed");
173 RunTest(x, shape, y, shape);
174 }
175
TEST_F(ArrayGradTest,ReshapeGrad)176 TEST_F(ArrayGradTest, ReshapeGrad) {
177 TensorShape x_shape({5, 2});
178 auto x = Placeholder(scope_, DT_FLOAT, Placeholder::Shape(x_shape));
179 TensorShape y_shape({2, 5});
180 auto y = Reshape(scope_, x, {2, 5});
181 RunTest(x, x_shape, y, y_shape);
182 }
183
TEST_F(ArrayGradTest,ExpandDimsGrad)184 TEST_F(ArrayGradTest, ExpandDimsGrad) {
185 TensorShape x_shape({5, 2});
186 auto x = Placeholder(scope_, DT_FLOAT, Placeholder::Shape(x_shape));
187 TensorShape y_shape({1, 5, 2});
188 auto y = ExpandDims(scope_, x, 0);
189 RunTest(x, x_shape, y, y_shape);
190 }
191
TEST_F(ArrayGradTest,SqueezeGrad)192 TEST_F(ArrayGradTest, SqueezeGrad) {
193 TensorShape x_shape({1, 5, 1, 2});
194 auto x = Placeholder(scope_, DT_FLOAT, Placeholder::Shape(x_shape));
195 TensorShape y_shape({5, 2});
196 auto y = Squeeze(scope_, x);
197 RunTest(x, x_shape, y, y_shape);
198 }
199
TEST_F(ArrayGradTest,TransposeGrad)200 TEST_F(ArrayGradTest, TransposeGrad) {
201 TensorShape x_shape({5, 2});
202 auto x = Placeholder(scope_, DT_FLOAT, Placeholder::Shape(x_shape));
203 TensorShape y_shape({2, 5});
204 auto y = Transpose(scope_, x, {1, 0});
205 RunTest(x, x_shape, y, y_shape);
206 }
207
TEST_F(ArrayGradTest,ReverseSequenceGrad)208 TEST_F(ArrayGradTest, ReverseSequenceGrad) {
209 TensorShape shape({5, 2, 5});
210 auto x = Placeholder(scope_, DT_FLOAT, Placeholder::Shape(shape));
211 auto seq_lengths = Const(scope_, {1, 2, 3, 4, 5});
212 // batch_dim defaults to 0.
213 auto y = ReverseSequence(scope_, x, seq_lengths, /* seq_dim */ 2);
214 RunTest(x, shape, y, shape);
215 }
216
TEST_F(ArrayGradTest,ReverseGrad)217 TEST_F(ArrayGradTest, ReverseGrad) {
218 TensorShape shape({5, 2, 5});
219 auto x = Placeholder(scope_, DT_FLOAT, Placeholder::Shape(shape));
220 auto y = Reverse(scope_, x, {0, 2});
221 RunTest(x, shape, y, shape);
222 }
223
TEST_F(ArrayGradTest,ScatterNdGrad_SimpleIndexing)224 TEST_F(ArrayGradTest, ScatterNdGrad_SimpleIndexing) {
225 TensorShape updates_shape({4});
226 auto updates =
227 Placeholder(scope_, DT_FLOAT, Placeholder::Shape(updates_shape));
228 auto indices = Const(scope_, {{4}, {3}, {1}, {7}});
229 TensorShape y_shape({8});
230 auto y = ScatterNd(scope_, indices, updates, {8});
231 RunTest(updates, updates_shape, y, y_shape);
232 }
233
TEST_F(ArrayGradTest,ScatterNdGrad_SliceIndexing)234 TEST_F(ArrayGradTest, ScatterNdGrad_SliceIndexing) {
235 TensorShape updates_shape({2, 4, 4});
236 auto updates =
237 Placeholder(scope_, DT_FLOAT, Placeholder::Shape(updates_shape));
238 auto indices = Const(scope_, {{0}, {2}});
239 TensorShape y_shape({4, 4, 4});
240 auto y = ScatterNd(scope_, indices, updates, {4, 4, 4});
241 RunTest(updates, updates_shape, y, y_shape);
242 }
243
TEST_F(ArrayGradTest,ScatterNdNonAliasingAddGrad_SimpleIndexing)244 TEST_F(ArrayGradTest, ScatterNdNonAliasingAddGrad_SimpleIndexing) {
245 TensorShape updates_shape({4});
246 TensorShape input_shape({8});
247 auto input = Placeholder(scope_, DT_FLOAT, Placeholder::Shape(input_shape));
248 auto updates =
249 Placeholder(scope_, DT_FLOAT, Placeholder::Shape(updates_shape));
250 auto indices = Const(scope_, {{4}, {3}, {1}, {7}});
251 auto y = ScatterNdNonAliasingAdd(scope_, input, indices, updates);
252 RunTest({input, updates}, {input_shape, updates_shape}, {y}, {input_shape});
253 }
254
TEST_F(ArrayGradTest,ScatterNdNonAliasingAddGrad_SliceIndexing)255 TEST_F(ArrayGradTest, ScatterNdNonAliasingAddGrad_SliceIndexing) {
256 TensorShape updates_shape({2, 4, 4});
257 TensorShape input_shape({4, 4, 4});
258 auto input = Placeholder(scope_, DT_FLOAT, Placeholder::Shape(input_shape));
259 auto updates =
260 Placeholder(scope_, DT_FLOAT, Placeholder::Shape(updates_shape));
261 auto indices = Const(scope_, {{0}, {2}});
262 auto y = ScatterNdNonAliasingAdd(scope_, input, indices, updates);
263 RunTest({input, updates}, {input_shape, updates_shape}, {y}, {input_shape});
264 }
265
TEST_F(ArrayGradTest,PadGrad)266 TEST_F(ArrayGradTest, PadGrad) {
267 TensorShape x_shape({2, 3});
268 auto x = Placeholder(scope_, DT_FLOAT, Placeholder::Shape(x_shape));
269 auto paddings = Const(scope_, {{1, 1}, {2, 2}});
270 TensorShape y_shape({4, 7});
271 auto y = Pad(scope_, x, paddings);
272 RunTest(x, x_shape, y, y_shape);
273 }
274
TEST_F(ArrayGradTest,SpaceToBatchGrad)275 TEST_F(ArrayGradTest, SpaceToBatchGrad) {
276 TensorShape x_shape({1, 2, 2, 1});
277 auto x = Placeholder(scope_, DT_FLOAT, Placeholder::Shape(x_shape));
278 auto paddings = Const(scope_, {{1, 1}, {1, 1}});
279 TensorShape y_shape({4, 2, 2, 1});
280 auto y = SpaceToBatch(scope_, x, paddings, /* block_size */ 2);
281 RunTest(x, x_shape, y, y_shape);
282 }
283
TEST_F(ArrayGradTest,SpaceToBatchNdGrad)284 TEST_F(ArrayGradTest, SpaceToBatchNdGrad) {
285 TensorShape x_shape({2, 2, 4, 1});
286 auto x = Placeholder(scope_, DT_FLOAT, Placeholder::Shape(x_shape));
287 auto block_shape = Const(scope_, {2, 2});
288 auto paddings = Const(scope_, {{0, 0}, {2, 0}});
289 TensorShape y_shape({8, 1, 3, 1});
290 auto y = SpaceToBatchND(scope_, x, block_shape, paddings);
291 RunTest(x, x_shape, y, y_shape);
292 }
293
TEST_F(ArrayGradTest,BatchToSpaceGrad)294 TEST_F(ArrayGradTest, BatchToSpaceGrad) {
295 TensorShape x_shape({4, 2, 2, 1});
296 auto x = Placeholder(scope_, DT_FLOAT, Placeholder::Shape(x_shape));
297 auto paddings = Const(scope_, {{1, 1}, {1, 1}});
298 TensorShape y_shape({1, 2, 2, 1});
299 auto y = BatchToSpace(scope_, x, paddings, /* block_size */ 2);
300 RunTest(x, x_shape, y, y_shape);
301 }
302
TEST_F(ArrayGradTest,BatchToSpaceNdGrad)303 TEST_F(ArrayGradTest, BatchToSpaceNdGrad) {
304 TensorShape x_shape({8, 1, 3, 1});
305 auto x = Placeholder(scope_, DT_FLOAT, Placeholder::Shape(x_shape));
306 auto block_shape = Const(scope_, {2, 2});
307 auto paddings = Const(scope_, {{0, 0}, {2, 0}});
308 TensorShape y_shape({2, 2, 4, 1});
309 auto y = BatchToSpaceND(scope_, x, block_shape, paddings);
310 RunTest(x, x_shape, y, y_shape);
311 }
312
TEST_F(ArrayGradTest,SpaceToDepthGrad)313 TEST_F(ArrayGradTest, SpaceToDepthGrad) {
314 TensorShape x_shape({1, 2, 2, 1});
315 auto x = Placeholder(scope_, DT_FLOAT, Placeholder::Shape(x_shape));
316 TensorShape y_shape({1, 1, 1, 4});
317 auto y = SpaceToDepth(scope_, x, /* block_size */ 2);
318 RunTest(x, x_shape, y, y_shape);
319 }
320
TEST_F(ArrayGradTest,DepthToSpaceGrad)321 TEST_F(ArrayGradTest, DepthToSpaceGrad) {
322 TensorShape x_shape({1, 1, 1, 4});
323 auto x = Placeholder(scope_, DT_FLOAT, Placeholder::Shape(x_shape));
324 TensorShape y_shape({1, 2, 2, 1});
325 auto y = DepthToSpace(scope_, x, /* block_size */ 2);
326 RunTest(x, x_shape, y, y_shape);
327 }
328
TEST_F(ArrayGradTest,MirrorPadGrad_Reflect)329 TEST_F(ArrayGradTest, MirrorPadGrad_Reflect) {
330 TensorShape x_shape({2, 3});
331 auto x = Placeholder(scope_, DT_FLOAT, Placeholder::Shape(x_shape));
332 auto paddings = Const(scope_, {{1, 1}, {2, 2}});
333 TensorShape y_shape({4, 7});
334 auto y = MirrorPad(scope_, x, paddings, "REFLECT");
335 RunTest(x, x_shape, y, y_shape);
336 }
337
TEST_F(ArrayGradTest,MirrorPadGrad_Symmetric)338 TEST_F(ArrayGradTest, MirrorPadGrad_Symmetric) {
339 TensorShape x_shape({2, 3});
340 auto x = Placeholder(scope_, DT_FLOAT, Placeholder::Shape(x_shape));
341 auto paddings = Const(scope_, {{1, 1}, {2, 2}});
342 TensorShape y_shape({4, 7});
343 auto y = MirrorPad(scope_, x, paddings, "SYMMETRIC");
344 RunTest(x, x_shape, y, y_shape);
345 }
346
TEST_F(ArrayGradTest,MirrorPadGradGrad_Reflect)347 TEST_F(ArrayGradTest, MirrorPadGradGrad_Reflect) {
348 TensorShape x_shape({4, 7});
349 auto x = Placeholder(scope_, DT_FLOAT, Placeholder::Shape(x_shape));
350 auto paddings = Const(scope_, {{1, 1}, {2, 2}});
351 TensorShape y_shape({2, 3});
352 auto y = MirrorPadGrad(scope_, x, paddings, "REFLECT");
353 RunTest(x, x_shape, y, y_shape);
354 }
355
TEST_F(ArrayGradTest,MirrorPadGradGrad_Symmetric)356 TEST_F(ArrayGradTest, MirrorPadGradGrad_Symmetric) {
357 TensorShape x_shape({4, 7});
358 auto x = Placeholder(scope_, DT_FLOAT, Placeholder::Shape(x_shape));
359 auto paddings = Const(scope_, {{1, 1}, {2, 2}});
360 TensorShape y_shape({2, 3});
361 auto y = MirrorPadGrad(scope_, x, paddings, "SYMMETRIC");
362 RunTest(x, x_shape, y, y_shape);
363 }
364
TEST_F(ArrayGradTest,StridedSliceGrad)365 TEST_F(ArrayGradTest, StridedSliceGrad) {
366 TensorShape x_shape({6, 4, 4});
367 auto x = Placeholder(scope_, DT_FLOAT, Placeholder::Shape(x_shape));
368
369 // y = x[2:6:2, 1:3, 1:3]
370 auto y = StridedSlice(scope_, x, {2, 1, 1}, {6, 3, 3}, {2, 1, 1});
371 // y.shape = [2, 2, 2];
372 RunTest(x, x_shape, y, {2, 2, 2});
373
374 // y = x[2:6:2, 1:3, 1:3]
375 // begin_mask = 1<<1 (ignore begin_index = 1)
376 // end_mask = 1<<2 (ignore end_index = 2)
377 y = StridedSlice(scope_, x, {2, 1, 1}, {6, 3, 3}, {2, 1, 1},
378 StridedSlice::BeginMask(1 << 1).EndMask(1 << 2));
379 // y.shape = [2, 3, 3];
380 RunTest(x, x_shape, y, {2, 3, 3});
381
382 // y = [tf.newaxis, 2:6:2, 1:3, 1:3]
383 y = StridedSlice(scope_, x, {0, 2, 1, 1}, {0, 6, 3, 3}, {1, 2, 1, 1},
384 StridedSlice::NewAxisMask(1 << 0));
385 // y.shape = [1, 2, 2, 2];
386 RunTest(x, x_shape, y, {1, 2, 2, 2});
387 }
388
TEST_F(ArrayGradTest,SliceGrad)389 TEST_F(ArrayGradTest, SliceGrad) {
390 TensorShape x_shape({3, 5, 3});
391 auto x = Placeholder(scope_, DT_FLOAT, Placeholder::Shape(x_shape));
392 auto y = Slice(scope_, x, {1, 2, 1}, {1, 3, 2});
393 RunTest(x, x_shape, y, {1, 3, 2});
394 }
395
396 } // namespace
397 } // namespace tensorflow
398