1 /* Copyright 2015 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 #ifndef TENSORFLOW_CORE_KERNELS_EIGEN_BACKWARD_CUBOID_CONVOLUTIONS_H_ 17 #define TENSORFLOW_CORE_KERNELS_EIGEN_BACKWARD_CUBOID_CONVOLUTIONS_H_ 18 19 #include "third_party/eigen3/unsupported/Eigen/CXX11/Tensor" 20 #include "tensorflow/core/kernels/eigen_volume_patch.h" 21 22 namespace Eigen { 23 24 /** CuboidConvolutionBackwardInput 25 * \ingroup CXX11_NeuralNetworks_Module 26 * 27 * \brief Computes the backprop for the input of a 3D convolution. 28 * 29 * The output_backward parameter is expected to be a tensor with a rank of 4 or 30 * more (channels, depth, height, width, and optionally others) 31 * The kernel parameter is expected to be a 5D tensor (filters, channels, 32 * kernel_depth, kernel_height, kernel_width) 33 * output_backward and kernel have to be in the same layout. 34 * 35 * The dimensions of the result will be filters, depth, height, width (and 36 * others if applicable). 37 * 38 * It is possible to swap the order of the depth, width and height dimensions 39 * provided that the same order is used in the input, the kernel, and the 40 * output. 41 * 42 * All dimension orders above are given for col-major, and should be reversed 43 * for row-major. 44 */ 45 46 template <typename OutputBackward, typename Kernel> 47 EIGEN_ALWAYS_INLINE static const typename internal::conditional< 48 internal::traits<OutputBackward>::Layout == ColMajor, 49 TensorReshapingOp< 50 const DSizes<typename internal::traits<OutputBackward>::Index, 51 internal::traits<OutputBackward>::NumDimensions>, 52 const TensorContractionOp< 53 const array< 54 IndexPair<typename internal::traits<OutputBackward>::Index>, 1>, 55 const Eigen::TensorForcedEvalOp<const TensorReshapingOp< 56 const DSizes<typename internal::traits<OutputBackward>::Index, 57 2>, 58 const TensorShufflingOp< 59 const array< 60 typename internal::traits<OutputBackward>::Index, 5>, 61 const TensorReverseOp<const Eigen::array<bool, 5>, 62 const Kernel>>>>, 63 const TensorReshapingOp< 64 const DSizes<typename internal::traits<OutputBackward>::Index, 65 2>, 66 const TensorVolumePatchOp<Dynamic, Dynamic, Dynamic, 67 const OutputBackward>>>>, 68 TensorReshapingOp< 69 const DSizes<typename internal::traits<OutputBackward>::Index, 70 internal::traits<OutputBackward>::NumDimensions>, 71 const TensorContractionOp< 72 const array< 73 IndexPair<typename internal::traits<OutputBackward>::Index>, 1>, 74 const TensorReshapingOp< 75 const DSizes<typename internal::traits<OutputBackward>::Index, 76 2>, 77 const TensorVolumePatchOp<Dynamic, Dynamic, Dynamic, 78 const OutputBackward>>, 79 const Eigen::TensorForcedEvalOp<const TensorReshapingOp< 80 const DSizes<typename internal::traits<OutputBackward>::Index, 81 2>, 82 const TensorShufflingOp< 83 const array< 84 typename internal::traits<OutputBackward>::Index, 5>, 85 const TensorReverseOp<const Eigen::array<bool, 5>, 86 const Kernel>>>>>>>::type 87 CuboidConvolutionBackwardInput( 88 const Kernel& kernel, const OutputBackward& output_backward, 89 typename internal::traits<OutputBackward>::Index inputPlanes, 90 typename internal::traits<OutputBackward>::Index inputRows, 91 typename internal::traits<OutputBackward>::Index inputCols, 92 const DenseIndex plane_stride = 1, const DenseIndex row_stride = 1, 93 const DenseIndex col_stride = 1) { 94 typedef typename internal::traits<OutputBackward>::Index TensorIndex; 95 const TensorRef<const Tensor<typename internal::traits<Kernel>::Scalar, 96 internal::traits<Kernel>::NumDimensions, 97 internal::traits<Kernel>::Layout, TensorIndex>> 98 kern(kernel); 99 const TensorRef< 100 const Tensor<typename internal::traits<OutputBackward>::Scalar, 101 internal::traits<OutputBackward>::NumDimensions, 102 internal::traits<OutputBackward>::Layout, TensorIndex>> 103 out(output_backward); 104 105 EIGEN_STATIC_ASSERT(internal::traits<Kernel>::Layout == 106 internal::traits<OutputBackward>::Layout, 107 YOU_MADE_A_PROGRAMMING_MISTAKE); 108 109 static const bool isColMajor = 110 (internal::traits<OutputBackward>::Layout == ColMajor); 111 112 static const int NumDims = internal::traits<OutputBackward>::NumDimensions; 113 114 // Number of filters to apply. This is the same as the output depth of the 115 // result 116 const TensorIndex kernelFilters = 117 isColMajor ? kern.dimensions()[0] : kern.dimensions()[4]; 118 // Number of channels. This is the same as the input depth. 119 const TensorIndex kernelChannels = 120 isColMajor ? kern.dimensions()[1] : kern.dimensions()[3]; 121 const TensorIndex kernelPlanes = 122 isColMajor ? kern.dimensions()[2] : kern.dimensions()[2]; 123 const TensorIndex kernelRows = 124 isColMajor ? kern.dimensions()[3] : kern.dimensions()[1]; 125 const TensorIndex kernelCols = 126 isColMajor ? kern.dimensions()[4] : kern.dimensions()[0]; 127 128 const TensorIndex outputPlanes = 129 isColMajor ? out.dimensions()[1] : out.dimensions()[NumDims - 2]; 130 const TensorIndex outputRows = 131 isColMajor ? out.dimensions()[2] : out.dimensions()[NumDims - 3]; 132 const TensorIndex outputCols = 133 isColMajor ? out.dimensions()[3] : out.dimensions()[NumDims - 4]; 134 135 // TODO(ezhulenev): Add support for inflated strides. Without inflated strides 136 // effective kernel planes/rows/cols are always the same as the kernel itself 137 // (see eigen_spatial_convolutions for details). 138 const TensorIndex kernelPlanesEff = kernelPlanes; 139 const TensorIndex kernelRowsEff = kernelRows; 140 const TensorIndex kernelColsEff = kernelCols; 141 142 // Computing the forward padding. 143 const TensorIndex forward_pad_top_z = numext::maxi<Index>( 144 0, 145 ((outputPlanes - 1) * plane_stride + kernelPlanesEff - inputPlanes) / 2); 146 const TensorIndex forward_pad_top = numext::maxi<Index>( 147 0, ((outputRows - 1) * row_stride + kernelRowsEff - inputRows) / 2); 148 const TensorIndex forward_pad_left = numext::maxi<Index>( 149 0, ((outputCols - 1) * col_stride + kernelColsEff - inputCols) / 2); 150 151 const TensorIndex padding_top_z = kernelPlanesEff - 1 - forward_pad_top_z; 152 const TensorIndex padding_top = kernelRowsEff - 1 - forward_pad_top; 153 const TensorIndex padding_left = kernelColsEff - 1 - forward_pad_left; 154 155 const TensorIndex padding_bottom_z = inputPlanes - 156 (outputPlanes - 1) * plane_stride - 2 - 157 padding_top_z + kernelPlanesEff; 158 const TensorIndex padding_bottom = inputRows - (outputRows - 1) * row_stride - 159 2 - padding_top + kernelRowsEff; 160 const TensorIndex padding_right = inputCols - (outputCols - 1) * col_stride - 161 2 - padding_left + kernelColsEff; 162 163 eigen_assert(padding_top_z >= 0); 164 eigen_assert(padding_top >= 0); 165 eigen_assert(padding_left >= 0); 166 eigen_assert(padding_bottom_z >= 0); 167 eigen_assert(padding_bottom >= 0); 168 eigen_assert(padding_right >= 0); 169 170 // The kernel has dimensions : 171 // filters x channels x patch_planes x patch_rows x patch_cols. 172 // We need to reverse the kernel along the spatial dimensions. 173 Eigen::array<bool, 5> kernel_reverse; 174 if (isColMajor) { 175 kernel_reverse[0] = false; 176 kernel_reverse[1] = false; 177 kernel_reverse[2] = true; 178 kernel_reverse[3] = true; 179 kernel_reverse[4] = true; 180 } else { 181 kernel_reverse[0] = true; 182 kernel_reverse[1] = true; 183 kernel_reverse[2] = true; 184 kernel_reverse[3] = false; 185 kernel_reverse[4] = false; 186 } 187 188 // Reorder the dimensions to: 189 // filters x patch_planes x patch_rows x patch_cols x channels 190 array<TensorIndex, 5> kernel_shuffle; 191 if (isColMajor) { 192 // From: filters x channels x planes x rows x cols 193 // To: filters x planes x rows x cols x channels 194 kernel_shuffle[0] = 0; 195 kernel_shuffle[1] = 2; 196 kernel_shuffle[2] = 3; 197 kernel_shuffle[3] = 4; 198 kernel_shuffle[4] = 1; 199 } else { 200 // From: cols x rows x planes x channels x filters 201 // To: channels x cols x rows x planes x filters 202 kernel_shuffle[0] = 3; 203 kernel_shuffle[1] = 0; 204 kernel_shuffle[2] = 1; 205 kernel_shuffle[3] = 2; 206 kernel_shuffle[4] = 4; 207 } 208 209 // Collapse the dims 210 DSizes<TensorIndex, 2> kernel_dims; 211 if (isColMajor) { 212 kernel_dims[0] = kernelFilters * kernelPlanes * kernelRows * kernelCols; 213 kernel_dims[1] = kernelChannels; 214 } else { 215 kernel_dims[1] = kernelFilters * kernelPlanes * kernelRows * kernelCols; 216 kernel_dims[0] = kernelChannels; 217 } 218 219 // The output_backward has dimensions out_depth X out_planes X out_rows X 220 // out_cols X OTHERS 221 // When we extract the image patches from output_backward, it will have 222 // dimensions: 223 // out_depth X (patch_planes * patch_rows * patch_cols) X (input_planes * 224 // input_rows * input_cols * OTHERS) 225 DSizes<TensorIndex, 2> pre_contract_dims; 226 if (isColMajor) { 227 pre_contract_dims[0] = 228 kernelFilters * kernelPlanes * kernelRows * kernelCols; 229 pre_contract_dims[1] = inputPlanes * inputRows * inputCols; 230 for (int i = 4; i < NumDims; ++i) { 231 pre_contract_dims[1] *= out.dimension(i); 232 } 233 } else { 234 pre_contract_dims[1] = 235 kernelFilters * kernelPlanes * kernelRows * kernelCols; 236 pre_contract_dims[0] = inputPlanes * inputRows * inputCols; 237 for (int i = 0; i < NumDims - 4; ++i) { 238 pre_contract_dims[0] *= out.dimension(i); 239 } 240 } 241 242 // We will contract along the collapsed dimension that contains the 243 // kernelFilters, kernelPlanes, kernelRows and kernelCols. 244 array<IndexPair<TensorIndex>, 1> contract_dims; 245 if (isColMajor) { 246 // col-major: kernel.contract(output.patches) 247 contract_dims[0] = IndexPair<TensorIndex>(0, 0); 248 } else { 249 // row-major: output.patches.contract(kernel) 250 contract_dims[0] = IndexPair<TensorIndex>(1, 1); 251 } 252 253 // Post contraction, the dimensions of the input_backprop is 254 // channels X input_planes X input_rows X input_cols X OTHERS 255 DSizes<TensorIndex, NumDims> post_contract_dims; 256 if (isColMajor) { 257 post_contract_dims[0] = kernelChannels; 258 post_contract_dims[1] = inputPlanes; 259 post_contract_dims[2] = inputRows; 260 post_contract_dims[3] = inputCols; 261 for (int i = 4; i < NumDims; ++i) { 262 post_contract_dims[i] = out.dimension(i); 263 } 264 } else { 265 post_contract_dims[NumDims - 1] = kernelChannels; 266 post_contract_dims[NumDims - 2] = inputPlanes; 267 post_contract_dims[NumDims - 3] = inputRows; 268 post_contract_dims[NumDims - 4] = inputCols; 269 for (int i = 0; i < NumDims - 4; ++i) { 270 post_contract_dims[i] = out.dimension(i); 271 } 272 } 273 274 return choose( 275 Cond<internal::traits<OutputBackward>::Layout == ColMajor>(), 276 kernel.reverse(kernel_reverse) 277 .shuffle(kernel_shuffle) 278 .reshape(kernel_dims) 279 .eval() 280 .contract(output_backward 281 .extract_volume_patches( 282 kernelPlanes, kernelRows, kernelCols, 1, 1, 1, 283 plane_stride, row_stride, col_stride, padding_top_z, 284 padding_bottom_z, padding_top, padding_bottom, 285 padding_left, padding_right) 286 .reshape(pre_contract_dims), 287 contract_dims) 288 .reshape(post_contract_dims), 289 output_backward 290 .extract_volume_patches(kernelPlanes, kernelRows, kernelCols, 1, 1, 1, 291 plane_stride, row_stride, col_stride, 292 padding_top_z, padding_bottom_z, padding_top, 293 padding_bottom, padding_left, padding_right) 294 .reshape(pre_contract_dims) 295 .contract(kernel.reverse(kernel_reverse) 296 .shuffle(kernel_shuffle) 297 .reshape(kernel_dims) 298 .eval(), 299 contract_dims) 300 .reshape(post_contract_dims)); 301 } 302 303 /** CuboidConvolutionBackwardKernel 304 * \ingroup CXX11_NeuralNetworks_Module 305 * 306 * \brief Computes the backprop for the filter of a 3D convolution. 307 * 308 * The output_backward parameter is expected to be a tensor with a rank of 4 or 309 * more (channels, depth, height, width, and optionally others) 310 * The kernel parameter is expected to be a 4D tensor (filters, channels, 311 * kernel_depth, kernel_height, kernel_width) 312 * output_backward and kernel have to be in the same layout. 313 * 314 * The dimensions of the result will be filters, depth, height, width (and 315 * others if applicable). 316 * 317 * It is possible to swap the order of the depth, width and height dimensions 318 * provided that the same order is used in the input, the kernel, and the 319 * output. 320 * 321 * All dimension orders above are given for col-major, and should be reversed 322 * for row-major. 323 */ 324 template <typename OutputBackward, typename Input> 325 EIGEN_ALWAYS_INLINE static const typename internal::conditional< 326 internal::traits<Input>::Layout == ColMajor, 327 const TensorReverseOp< 328 const Eigen::array<typename internal::traits<Input>::Index, 329 internal::traits<Input>::NumDimensions>, 330 const Eigen::TensorShufflingOp< 331 const Eigen::array<typename internal::traits<Input>::Index, 332 internal::traits<Input>::NumDimensions>, 333 const Eigen::TensorReshapingOp< 334 const Eigen::DSizes<typename internal::traits<Input>::Index, 335 internal::traits<Input>::NumDimensions>, 336 const TensorContractionOp< 337 const array< 338 IndexPair<typename internal::traits<Input>::Index>, 1>, 339 const Eigen::TensorForcedEvalOp<const TensorReshapingOp< 340 const DSizes<typename internal::traits<Input>::Index, 341 2>, 342 const Eigen::TensorShufflingOp< 343 const Eigen::array< 344 typename internal::traits<Input>::Index, 345 internal::traits<Input>::NumDimensions>, 346 const OutputBackward>>>, 347 const TensorReshapingOp< 348 const DSizes<typename internal::traits<Input>::Index, 349 2>, 350 const TensorVolumePatchOp< 351 Dynamic, Dynamic, Dynamic, 352 const Eigen::TensorForcedEvalOp< 353 const Eigen::TensorShufflingOp< 354 const Eigen::array< 355 typename internal::traits<Input>::Index, 356 internal::traits<Input>::NumDimensions>, 357 const Input>>>>>>>>, 358 const TensorReverseOp< 359 const Eigen::array<typename internal::traits<Input>::Index, 360 internal::traits<Input>::NumDimensions>, 361 const Eigen::TensorShufflingOp< 362 const Eigen::array<typename internal::traits<Input>::Index, 363 internal::traits<Input>::NumDimensions>, 364 const Eigen::TensorReshapingOp< 365 const Eigen::DSizes<typename internal::traits<Input>::Index, 366 internal::traits<Input>::NumDimensions>, 367 const TensorContractionOp< 368 const array< 369 IndexPair<typename internal::traits<Input>::Index>, 1>, 370 const TensorReshapingOp< 371 const DSizes<typename internal::traits<Input>::Index, 372 2>, 373 const TensorVolumePatchOp< 374 Dynamic, Dynamic, Dynamic, 375 const Eigen::TensorForcedEvalOp< 376 const Eigen::TensorShufflingOp< 377 const Eigen::array< 378 typename internal::traits<Input>::Index, 379 internal::traits<Input>::NumDimensions>, 380 const Input>>>>, 381 const Eigen::TensorForcedEvalOp<const TensorReshapingOp< 382 const DSizes<typename internal::traits<Input>::Index, 383 2>, 384 const Eigen::TensorShufflingOp< 385 const Eigen::array< 386 typename internal::traits<Input>::Index, 387 internal::traits<Input>::NumDimensions>, 388 const OutputBackward>>>>>>>>::type 389 CuboidConvolutionBackwardKernel( 390 const Input& input, const OutputBackward& output_backward, 391 typename internal::traits<Input>::Index kernelPlanes, 392 typename internal::traits<Input>::Index kernelRows, 393 typename internal::traits<Input>::Index kernelCols, 394 const DenseIndex stridePlanes = 1, const DenseIndex strideRows = 1, 395 const DenseIndex strideCols = 1) { 396 typedef typename internal::traits<Input>::Index TensorIndex; 397 TensorRef<Tensor<typename internal::traits<Input>::Scalar, 398 internal::traits<Input>::NumDimensions, 399 internal::traits<Input>::Layout, TensorIndex>> 400 in(input); 401 TensorRef<Tensor<typename internal::traits<OutputBackward>::Scalar, 402 internal::traits<OutputBackward>::NumDimensions, 403 internal::traits<OutputBackward>::Layout, TensorIndex>> 404 out(output_backward); 405 406 EIGEN_STATIC_ASSERT(internal::traits<Input>::Layout == 407 internal::traits<OutputBackward>::Layout, 408 YOU_MADE_A_PROGRAMMING_MISTAKE); 409 410 static const bool isColMajor = (internal::traits<Input>::Layout == ColMajor); 411 412 static const int NumDims = internal::traits<Input>::NumDimensions; 413 EIGEN_STATIC_ASSERT(internal::traits<Input>::NumDimensions == 414 internal::traits<OutputBackward>::NumDimensions, 415 YOU_MADE_A_PROGRAMMING_MISTAKE); 416 417 // We do not support higher dimensional backward convolutions, or convolutions 418 // without batch dimension. 419 // TODO(ezhulenev): Relax this constraint, and turn on tests without batch 420 // dimension in eigen_backward_cuboid_convolutions_test.cc. 421 EIGEN_STATIC_ASSERT(internal::traits<Input>::NumDimensions == 5, 422 YOU_MADE_A_PROGRAMMING_MISTAKE); 423 424 const TensorIndex inputPlanes = 425 isColMajor ? in.dimension(1) : in.dimension(NumDims - 2); 426 const TensorIndex inputRows = 427 isColMajor ? in.dimension(2) : in.dimension(NumDims - 3); 428 const TensorIndex inputCols = 429 isColMajor ? in.dimension(3) : in.dimension(NumDims - 4); 430 431 const TensorIndex outputPlanes = 432 isColMajor ? out.dimension(1) : out.dimension(NumDims - 2); 433 const TensorIndex outputRows = 434 isColMajor ? out.dimension(2) : out.dimension(NumDims - 3); 435 const TensorIndex outputCols = 436 isColMajor ? out.dimension(3) : out.dimension(NumDims - 4); 437 438 // Number of filters. This is the same as the output depth. 439 const TensorIndex kernelFilters = 440 isColMajor ? out.dimension(0) : out.dimension(NumDims - 1); 441 // Number of channels. This is the same as the input depth. 442 const TensorIndex kernelChannels = 443 isColMajor ? in.dimension(0) : in.dimension(NumDims - 1); 444 445 // Number of batches in the input tensor. 446 const TensorIndex batch = 447 isColMajor ? in.dimension(4) : in.dimension(NumDims - 5); 448 449 // TODO(ezhulenev): Add support for inflated strides. Without inflated strides 450 // effective kernel planes/rows/cols are always the same as the kernel itself 451 // (see eigen_spatial_convolutions for details). 452 const TensorIndex kernelPlanesEff = kernelPlanes; 453 const TensorIndex kernelRowsEff = kernelRows; 454 const TensorIndex kernelColsEff = kernelCols; 455 456 // Compute forward padding from input and output_backward dimensions. 457 const TensorIndex padPlanes = numext::maxi<Index>( 458 0, (outputPlanes - 1) * stridePlanes + kernelPlanesEff - inputPlanes); 459 const TensorIndex padRows = numext::maxi<Index>( 460 0, (outputRows - 1) * strideRows + kernelRowsEff - inputRows); 461 const TensorIndex padCols = numext::maxi<Index>( 462 0, (outputCols - 1) * strideCols + kernelColsEff - inputCols); 463 464 const TensorIndex padding_top_z = padPlanes / 2; 465 const TensorIndex padding_top = padRows / 2; 466 const TensorIndex padding_left = padCols / 2; 467 468 // Compute paddings for output_backward before extracting patches. 469 const auto expanded_out_planes = (outputPlanes - 1) * stridePlanes + 1; 470 const auto expanded_out_rows = (outputRows - 1) * strideRows + 1; 471 const auto expanded_out_cols = (outputCols - 1) * strideCols + 1; 472 const auto padded_out_planes = inputPlanes + kernelPlanes - 1; 473 const auto padded_out_rows = inputRows + kernelRows - 1; 474 const auto padded_out_cols = inputCols + kernelCols - 1; 475 const auto top_pad_planes = kernelPlanes - 1 - padding_top_z; 476 const auto top_pad_rows = kernelRows - 1 - padding_top; 477 const auto left_pad_cols = kernelCols - 1 - padding_left; 478 const auto bottom_pad_planes = 479 padded_out_planes - expanded_out_planes - top_pad_planes; 480 const auto bottom_pad_rows = 481 padded_out_rows - expanded_out_rows - top_pad_rows; 482 const auto right_pad_cols = 483 padded_out_cols - expanded_out_cols - left_pad_cols; 484 485 // Reorder output_backward dimensions. 486 array<TensorIndex, 5> output_backward_shuffle; 487 if (isColMajor) { 488 // From: [out_depth, out_planes, out_rows, out_cols, batch] 489 // To: [batch, out_planes, out_rows, out_cols, out_depth] 490 output_backward_shuffle = {4, 1, 2, 3, 0}; 491 } else { 492 // From: [batch, out_cols, out_rows, out_planes, out_depth] 493 // To: [out_depth, out_cols, out_rows, out_planes, batch] 494 output_backward_shuffle = {4, 1, 2, 3, 0}; 495 } 496 497 // Reorder input dimensions. 498 array<TensorIndex, 5> input_shuffle; 499 if (isColMajor) { 500 // From: [in_depth, in_planes, in_rows, in_cols, batch] 501 // To: [in_depth, batch, in_planes, in_rows, in_cols] 502 input_shuffle = {0, 4, 1, 2, 3}; 503 } else { 504 // From: [batch, in_cols, in_rows, in_planes, in_depth] 505 // To: [in_cols, in_rows, in_planes, batch, in_depth] 506 input_shuffle = {1, 2, 3, 0, 4}; 507 } 508 509 // Input is playing the role of a "kernel" in this convolution. 510 DSizes<TensorIndex, 2> input_dims; 511 if (isColMajor) { 512 input_dims[0] = kernelChannels; 513 input_dims[1] = batch * inputPlanes * inputRows * inputCols; 514 } else { 515 input_dims[1] = kernelChannels; 516 input_dims[0] = inputCols * inputRows * inputPlanes * batch; 517 } 518 519 // Molds the output of the patch extraction result into a 2D tensor: 520 // - the first dimension (dims[0]): the patch values to be multiplied with the 521 // kernels 522 // - the second dimension (dims[1]): everything else 523 DSizes<TensorIndex, 2> pre_contract_dims; 524 if (isColMajor) { 525 pre_contract_dims[0] = batch * inputPlanes * inputRows * inputCols; 526 pre_contract_dims[1] = 527 kernelPlanes * kernelRows * kernelCols * kernelFilters; 528 } else { 529 pre_contract_dims[1] = inputCols * inputRows * inputPlanes * batch; 530 pre_contract_dims[0] = 531 kernelFilters * kernelCols * kernelRows * kernelPlanes; 532 } 533 534 // We will contract along the collapsed dimension that contains the 535 // batch, inputPlanes, inputRows and inputCols. 536 array<IndexPair<TensorIndex>, 1> contract_dims; 537 contract_dims[0] = IndexPair<TensorIndex>(1, 0); 538 539 // Dimensions after contraction. 540 DSizes<TensorIndex, NumDims> post_contract_dims; 541 if (isColMajor) { 542 post_contract_dims[0] = kernelChannels; 543 post_contract_dims[1] = kernelPlanes; 544 post_contract_dims[2] = kernelRows; 545 post_contract_dims[3] = kernelCols; 546 post_contract_dims[4] = kernelFilters; 547 } else { 548 post_contract_dims[0] = kernelFilters; 549 post_contract_dims[1] = kernelCols; 550 post_contract_dims[2] = kernelRows; 551 post_contract_dims[3] = kernelPlanes; 552 post_contract_dims[4] = kernelChannels; 553 } 554 555 // Reorder output of contraction to valid filter shape. 556 array<TensorIndex, 5> kernel_shuffle; 557 if (isColMajor) { 558 // From: [in_depth, kernel_planes, kernel_rows, kernel_cols, out_depth] 559 // To: [out_depth, in_depth, kernel_planes, kernel_rows, kernel_cols] 560 kernel_shuffle = {4, 0, 1, 2, 3}; 561 } else { 562 // From: [out_depth, kernel_cols, kernel_rows, kernel_planes, in_depth] 563 // To: [kernel_cols, kernel_rows, kernel_planes, in_depth, out_depth] 564 kernel_shuffle = {1, 2, 3, 4, 0}; 565 } 566 567 // Reverse kernel backprop dimensions. 568 array<TensorIndex, 5> kernel_reverse; 569 if (isColMajor) { 570 kernel_reverse = {false, false, true, true, true}; 571 } else { 572 kernel_reverse = {true, true, true, false, false}; 573 } 574 575 // Create convolution input (aka source of patches) from output backward 576 // tensor by shuffling dimensions. 577 const auto the_input = 578 output_backward.shuffle(output_backward_shuffle).eval(); 579 580 // Create convolution kernel (aka filter) from input by shuffling and 581 // reshaping. 582 const auto the_kernel = 583 input.shuffle(input_shuffle).reshape(input_dims).eval(); 584 585 return choose(Cond<internal::traits<Input>::Layout == ColMajor>(), 586 the_kernel.contract( 587 the_input 588 .extract_volume_patches( 589 inputPlanes, inputRows, inputCols, 1, 1, 1, 590 stridePlanes, strideRows, strideCols, 591 top_pad_planes, bottom_pad_planes, top_pad_rows, 592 bottom_pad_rows, left_pad_cols, right_pad_cols) 593 .reshape(pre_contract_dims), 594 contract_dims), 595 the_input 596 .extract_volume_patches( 597 inputPlanes, inputRows, inputCols, 1, 1, 1, 598 stridePlanes, strideRows, strideCols, top_pad_planes, 599 bottom_pad_planes, top_pad_rows, bottom_pad_rows, 600 left_pad_cols, right_pad_cols) 601 .reshape(pre_contract_dims) 602 .contract(the_kernel, contract_dims)) 603 .reshape(post_contract_dims) 604 .shuffle(kernel_shuffle) 605 .reverse(kernel_reverse); 606 } 607 608 } // end namespace Eigen 609 610 #endif // TENSORFLOW_CORE_KERNELS_EIGEN_BACKWARD_CUBOID_CONVOLUTIONS_H_ 611