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: 33 ArrayGradTest() : scope_(Scope::NewRootScope()) {} 34 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 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 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 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 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 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 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 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 111 TEST_F(ArrayGradTest, DiagGrad) { 112 TensorShape x_shape({5, 2}); 113 auto x = Placeholder(scope_, DT_FLOAT, Placeholder::Shape(x_shape)); 114 auto y = Diag(scope_, x); 115 TensorShape y_shape({5, 2, 5, 2}); 116 RunTest(x, x_shape, y, y_shape); 117 } 118 119 TEST_F(ArrayGradTest, DiagPartGrad) { 120 TensorShape x_shape({5, 2, 5, 2}); 121 auto x = Placeholder(scope_, DT_FLOAT, Placeholder::Shape(x_shape)); 122 auto y = DiagPart(scope_, x); 123 TensorShape y_shape({5, 2}); 124 RunTest(x, x_shape, y, y_shape); 125 } 126 127 TEST_F(ArrayGradTest, MatrixDiagGrad) { 128 TensorShape x_shape({5, 2}); 129 auto x = Placeholder(scope_, DT_FLOAT, Placeholder::Shape(x_shape)); 130 auto y = MatrixDiag(scope_, x); 131 TensorShape y_shape({5, 2, 2}); 132 RunTest(x, x_shape, y, y_shape); 133 } 134 135 TEST_F(ArrayGradTest, MatrixBandPartGrad) { 136 TensorShape shape({5, 5}); 137 auto x = Placeholder(scope_, DT_FLOAT, Placeholder::Shape(shape)); 138 const int64 num_lower = 1; 139 const int64 num_upper = 2; 140 auto y = MatrixBandPart(scope_, x, num_lower, num_upper); 141 RunTest(x, shape, y, shape); 142 } 143 144 TEST_F(ArrayGradTest, GatherNdGrad_SimpleIndexing) { 145 TensorShape x_shape({2, 2}); 146 auto x = Placeholder(scope_, DT_FLOAT, Placeholder::Shape(x_shape)); 147 auto indices = Const(scope_, {{0, 0}, {1, 1}}); 148 TensorShape y_shape({2}); 149 auto y = GatherNd(scope_, x, indices); 150 RunTest(x, x_shape, y, y_shape); 151 } 152 153 TEST_F(ArrayGradTest, GatherNdGrad_SliceIndexing) { 154 TensorShape shape({2, 2}); 155 auto x = Placeholder(scope_, DT_FLOAT, Placeholder::Shape(shape)); 156 auto indices = Const(scope_, {{1}, {0}}); 157 auto y = GatherNd(scope_, x, indices); 158 RunTest(x, shape, y, shape); 159 } 160 161 TEST_F(ArrayGradTest, CheckNumericsGrad) { 162 TensorShape shape({5, 2}); 163 auto x = Placeholder(scope_, DT_FLOAT, Placeholder::Shape(shape)); 164 auto y = CheckNumerics(scope_, x, "CheckNumerics failed"); 165 RunTest(x, shape, y, shape); 166 } 167 168 TEST_F(ArrayGradTest, ReshapeGrad) { 169 TensorShape x_shape({5, 2}); 170 auto x = Placeholder(scope_, DT_FLOAT, Placeholder::Shape(x_shape)); 171 TensorShape y_shape({2, 5}); 172 auto y = Reshape(scope_, x, {2, 5}); 173 RunTest(x, x_shape, y, y_shape); 174 } 175 176 TEST_F(ArrayGradTest, ExpandDimsGrad) { 177 TensorShape x_shape({5, 2}); 178 auto x = Placeholder(scope_, DT_FLOAT, Placeholder::Shape(x_shape)); 179 TensorShape y_shape({1, 5, 2}); 180 auto y = ExpandDims(scope_, x, 0); 181 RunTest(x, x_shape, y, y_shape); 182 } 183 184 TEST_F(ArrayGradTest, SqueezeGrad) { 185 TensorShape x_shape({1, 5, 1, 2}); 186 auto x = Placeholder(scope_, DT_FLOAT, Placeholder::Shape(x_shape)); 187 TensorShape y_shape({5, 2}); 188 auto y = Squeeze(scope_, x); 189 RunTest(x, x_shape, y, y_shape); 190 } 191 192 TEST_F(ArrayGradTest, TransposeGrad) { 193 TensorShape x_shape({5, 2}); 194 auto x = Placeholder(scope_, DT_FLOAT, Placeholder::Shape(x_shape)); 195 TensorShape y_shape({2, 5}); 196 auto y = Transpose(scope_, x, {1, 0}); 197 RunTest(x, x_shape, y, y_shape); 198 } 199 200 TEST_F(ArrayGradTest, ReverseSequenceGrad) { 201 TensorShape shape({5, 2, 5}); 202 auto x = Placeholder(scope_, DT_FLOAT, Placeholder::Shape(shape)); 203 auto seq_lengths = Const(scope_, {1, 2, 3, 4, 5}); 204 // batch_dim defaults to 0. 205 auto y = ReverseSequence(scope_, x, seq_lengths, /* seq_dim */ 2); 206 RunTest(x, shape, y, shape); 207 } 208 209 TEST_F(ArrayGradTest, ReverseGrad) { 210 TensorShape shape({5, 2, 5}); 211 auto x = Placeholder(scope_, DT_FLOAT, Placeholder::Shape(shape)); 212 auto y = Reverse(scope_, x, {0, 2}); 213 RunTest(x, shape, y, shape); 214 } 215 216 TEST_F(ArrayGradTest, ScatterNdGrad_SimpleIndexing) { 217 TensorShape updates_shape({4}); 218 auto updates = 219 Placeholder(scope_, DT_FLOAT, Placeholder::Shape(updates_shape)); 220 auto indices = Const(scope_, {{4}, {3}, {1}, {7}}); 221 TensorShape y_shape({8}); 222 auto y = ScatterNd(scope_, indices, updates, {8}); 223 RunTest(updates, updates_shape, y, y_shape); 224 } 225 226 TEST_F(ArrayGradTest, ScatterNdGrad_SliceIndexing) { 227 TensorShape updates_shape({2, 4, 4}); 228 auto updates = 229 Placeholder(scope_, DT_FLOAT, Placeholder::Shape(updates_shape)); 230 auto indices = Const(scope_, {{0}, {2}}); 231 TensorShape y_shape({4, 4, 4}); 232 auto y = ScatterNd(scope_, indices, updates, {4, 4, 4}); 233 RunTest(updates, updates_shape, y, y_shape); 234 } 235 236 TEST_F(ArrayGradTest, ScatterNdNonAliasingAddGrad_SimpleIndexing) { 237 TensorShape updates_shape({4}); 238 TensorShape input_shape({8}); 239 auto input = Placeholder(scope_, DT_FLOAT, Placeholder::Shape(input_shape)); 240 auto updates = 241 Placeholder(scope_, DT_FLOAT, Placeholder::Shape(updates_shape)); 242 auto indices = Const(scope_, {{4}, {3}, {1}, {7}}); 243 auto y = ScatterNdNonAliasingAdd(scope_, input, indices, updates); 244 RunTest({input, updates}, {input_shape, updates_shape}, {y}, {input_shape}); 245 } 246 247 TEST_F(ArrayGradTest, ScatterNdNonAliasingAddGrad_SliceIndexing) { 248 TensorShape updates_shape({2, 4, 4}); 249 TensorShape input_shape({4, 4, 4}); 250 auto input = Placeholder(scope_, DT_FLOAT, Placeholder::Shape(input_shape)); 251 auto updates = 252 Placeholder(scope_, DT_FLOAT, Placeholder::Shape(updates_shape)); 253 auto indices = Const(scope_, {{0}, {2}}); 254 auto y = ScatterNdNonAliasingAdd(scope_, input, indices, updates); 255 RunTest({input, updates}, {input_shape, updates_shape}, {y}, {input_shape}); 256 } 257 258 TEST_F(ArrayGradTest, PadGrad) { 259 TensorShape x_shape({2, 3}); 260 auto x = Placeholder(scope_, DT_FLOAT, Placeholder::Shape(x_shape)); 261 auto paddings = Const(scope_, {{1, 1}, {2, 2}}); 262 TensorShape y_shape({4, 7}); 263 auto y = Pad(scope_, x, paddings); 264 RunTest(x, x_shape, y, y_shape); 265 } 266 267 TEST_F(ArrayGradTest, SpaceToBatchGrad) { 268 TensorShape x_shape({1, 2, 2, 1}); 269 auto x = Placeholder(scope_, DT_FLOAT, Placeholder::Shape(x_shape)); 270 auto paddings = Const(scope_, {{1, 1}, {1, 1}}); 271 TensorShape y_shape({4, 2, 2, 1}); 272 auto y = SpaceToBatch(scope_, x, paddings, /* block_size */ 2); 273 RunTest(x, x_shape, y, y_shape); 274 } 275 276 TEST_F(ArrayGradTest, SpaceToBatchNdGrad) { 277 TensorShape x_shape({2, 2, 4, 1}); 278 auto x = Placeholder(scope_, DT_FLOAT, Placeholder::Shape(x_shape)); 279 auto block_shape = Const(scope_, {2, 2}); 280 auto paddings = Const(scope_, {{0, 0}, {2, 0}}); 281 TensorShape y_shape({8, 1, 3, 1}); 282 auto y = SpaceToBatchND(scope_, x, block_shape, paddings); 283 RunTest(x, x_shape, y, y_shape); 284 } 285 286 TEST_F(ArrayGradTest, BatchToSpaceGrad) { 287 TensorShape x_shape({4, 2, 2, 1}); 288 auto x = Placeholder(scope_, DT_FLOAT, Placeholder::Shape(x_shape)); 289 auto paddings = Const(scope_, {{1, 1}, {1, 1}}); 290 TensorShape y_shape({1, 2, 2, 1}); 291 auto y = BatchToSpace(scope_, x, paddings, /* block_size */ 2); 292 RunTest(x, x_shape, y, y_shape); 293 } 294 295 TEST_F(ArrayGradTest, BatchToSpaceNdGrad) { 296 TensorShape x_shape({8, 1, 3, 1}); 297 auto x = Placeholder(scope_, DT_FLOAT, Placeholder::Shape(x_shape)); 298 auto block_shape = Const(scope_, {2, 2}); 299 auto paddings = Const(scope_, {{0, 0}, {2, 0}}); 300 TensorShape y_shape({2, 2, 4, 1}); 301 auto y = BatchToSpaceND(scope_, x, block_shape, paddings); 302 RunTest(x, x_shape, y, y_shape); 303 } 304 305 TEST_F(ArrayGradTest, SpaceToDepthGrad) { 306 TensorShape x_shape({1, 2, 2, 1}); 307 auto x = Placeholder(scope_, DT_FLOAT, Placeholder::Shape(x_shape)); 308 TensorShape y_shape({1, 1, 1, 4}); 309 auto y = SpaceToDepth(scope_, x, /* block_size */ 2); 310 RunTest(x, x_shape, y, y_shape); 311 } 312 313 TEST_F(ArrayGradTest, DepthToSpaceGrad) { 314 TensorShape x_shape({1, 1, 1, 4}); 315 auto x = Placeholder(scope_, DT_FLOAT, Placeholder::Shape(x_shape)); 316 TensorShape y_shape({1, 2, 2, 1}); 317 auto y = DepthToSpace(scope_, x, /* block_size */ 2); 318 RunTest(x, x_shape, y, y_shape); 319 } 320 321 TEST_F(ArrayGradTest, MirrorPadGrad_Reflect) { 322 TensorShape x_shape({2, 3}); 323 auto x = Placeholder(scope_, DT_FLOAT, Placeholder::Shape(x_shape)); 324 auto paddings = Const(scope_, {{1, 1}, {2, 2}}); 325 TensorShape y_shape({4, 7}); 326 auto y = MirrorPad(scope_, x, paddings, "REFLECT"); 327 RunTest(x, x_shape, y, y_shape); 328 } 329 330 TEST_F(ArrayGradTest, MirrorPadGrad_Symmetric) { 331 TensorShape x_shape({2, 3}); 332 auto x = Placeholder(scope_, DT_FLOAT, Placeholder::Shape(x_shape)); 333 auto paddings = Const(scope_, {{1, 1}, {2, 2}}); 334 TensorShape y_shape({4, 7}); 335 auto y = MirrorPad(scope_, x, paddings, "SYMMETRIC"); 336 RunTest(x, x_shape, y, y_shape); 337 } 338 339 TEST_F(ArrayGradTest, MirrorPadGradGrad_Reflect) { 340 TensorShape x_shape({4, 7}); 341 auto x = Placeholder(scope_, DT_FLOAT, Placeholder::Shape(x_shape)); 342 auto paddings = Const(scope_, {{1, 1}, {2, 2}}); 343 TensorShape y_shape({2, 3}); 344 auto y = MirrorPadGrad(scope_, x, paddings, "REFLECT"); 345 RunTest(x, x_shape, y, y_shape); 346 } 347 348 TEST_F(ArrayGradTest, MirrorPadGradGrad_Symmetric) { 349 TensorShape x_shape({4, 7}); 350 auto x = Placeholder(scope_, DT_FLOAT, Placeholder::Shape(x_shape)); 351 auto paddings = Const(scope_, {{1, 1}, {2, 2}}); 352 TensorShape y_shape({2, 3}); 353 auto y = MirrorPadGrad(scope_, x, paddings, "SYMMETRIC"); 354 RunTest(x, x_shape, y, y_shape); 355 } 356 357 } // namespace 358 } // namespace tensorflow 359