| #ifndef EIGEN_CXX11_NEURAL_NETWORKS_BACKWARD_CUBOID_CONVOLUTIONS_H |
| #define EIGEN_CXX11_NEURAL_NETWORKS_BACKWARD_CUBOID_CONVOLUTIONS_H |
| |
| #include "Patch3d.h" |
| |
| namespace Eigen { |
| |
| /** CuboidConvolutionBackwardInput |
| * \ingroup CXX11_NeuralNetworks_Module |
| * |
| * \brief Computes the backprop for the input of a 3D convolution. |
| * |
| * The output_backward parameter is expected to be a tensor with a rank of 4 or more (channels, depth, height, width, and optionally others) |
| * The kernel parameter is expected to be a 5D tensor (filters, channels, kernel_depth, kernel_height, kernel_width) |
| * output_backward and kernel have to be in the same layout. |
| * |
| * The dimensions of the result will be filters, depth, height, width (and others if applicable). |
| * |
| * It is possible to swap the order of the depth, width and height dimensions provided that the same order is used in the input, the kernel, and the output. |
| * |
| * All dimension orders above are given for col-major, and should be reversed for row-major. |
| */ |
| |
| template <typename OutputBackward, typename Kernel> |
| EIGEN_ALWAYS_INLINE static const typename internal::conditional< |
| internal::traits<OutputBackward>::Layout == ColMajor, |
| TensorReshapingOp< |
| const DSizes<typename internal::traits<OutputBackward>::Index, |
| internal::traits<OutputBackward>::NumDimensions>, |
| const TensorContractionOp< |
| const array< IndexPair<typename internal::traits<OutputBackward>::Index>, 2>, |
| const TensorReshapingOp< |
| const DSizes< typename internal::traits<OutputBackward>::Index, 3>, |
| const TensorReverseOp<const array<bool, 5>, const Kernel> |
| >, |
| const TensorReshapingOp< |
| const DSizes< typename internal::traits<OutputBackward>::Index, 3>, |
| const TensorVolumePatchOp<Dynamic, Dynamic, Dynamic, const OutputBackward> |
| > |
| > |
| >, |
| TensorReshapingOp< |
| const DSizes<typename internal::traits<OutputBackward>::Index, |
| internal::traits<OutputBackward>::NumDimensions>, |
| const TensorContractionOp< |
| const array< IndexPair<typename internal::traits<OutputBackward>::Index>, 2>, |
| const TensorReshapingOp< |
| const DSizes< typename internal::traits<OutputBackward>::Index, 3>, |
| const TensorVolumePatchOp<Dynamic, Dynamic, Dynamic, const OutputBackward> |
| >, |
| const TensorReshapingOp< |
| const DSizes<typename internal::traits<OutputBackward>::Index, 3>, |
| const TensorReverseOp<const array<bool, 5>, const Kernel> |
| > |
| > |
| > |
| >::type |
| CuboidConvolutionBackwardInput( |
| const Kernel& kernel, const OutputBackward& output_backward, |
| typename internal::traits<OutputBackward>::Index inputPlanes, |
| typename internal::traits<OutputBackward>::Index inputRows, |
| typename internal::traits<OutputBackward>::Index inputCols, |
| const DenseIndex stridePlanes = 1, const DenseIndex strideRows = 1, |
| const DenseIndex strideCols = 1) { |
| typedef typename internal::traits<OutputBackward>::Index TensorIndex; |
| const TensorRef<const Tensor<typename internal::traits<Kernel>::Scalar, internal::traits<Kernel>::NumDimensions, internal::traits<Kernel>::Layout, TensorIndex> > kern(kernel); |
| const TensorRef<const Tensor<typename internal::traits<OutputBackward>::Scalar, internal::traits<OutputBackward>::NumDimensions, internal::traits<OutputBackward>::Layout, TensorIndex> > out(output_backward); |
| |
| EIGEN_STATIC_ASSERT(internal::traits<Kernel>::Layout == internal::traits<OutputBackward>::Layout, YOU_MADE_A_PROGRAMMING_MISTAKE); |
| |
| static const bool isColMajor = (internal::traits<OutputBackward>::Layout == ColMajor); |
| |
| static const int NumDims = internal::traits<OutputBackward>::NumDimensions; |
| |
| // Number of filters to apply. This is the same as the output depth of the result |
| const TensorIndex kernelFilters = isColMajor ? kern.dimensions()[0] : kern.dimensions()[4]; |
| // Number of channels. This is the same as the input depth. |
| const TensorIndex kernelChannels = isColMajor ? kern.dimensions()[1] : kern.dimensions()[3]; |
| const TensorIndex kernelPlanes = isColMajor ? kern.dimensions()[2] : kern.dimensions()[2]; |
| const TensorIndex kernelRows = isColMajor ? kern.dimensions()[3] : kern.dimensions()[1]; |
| const TensorIndex kernelCols = isColMajor ? kern.dimensions()[4] : kern.dimensions()[0]; |
| |
| const TensorIndex outputPlanes = isColMajor ? out.dimensions()[1] : out.dimensions()[NumDims - 2]; |
| const TensorIndex outputRows = isColMajor ? out.dimensions()[2] : out.dimensions()[NumDims - 3]; |
| const TensorIndex outputCols = isColMajor ? out.dimensions()[3] : out.dimensions()[NumDims - 4]; |
| |
| TensorIndex forward_pad_z, forward_pad_y, forward_pad_x; |
| const TensorIndex size_z = Eigen::divup(inputPlanes, static_cast<TensorIndex>(stridePlanes)); |
| const TensorIndex size_y = Eigen::divup(inputRows, static_cast<TensorIndex>(strideRows)); |
| const TensorIndex size_x = Eigen::divup(inputCols, static_cast<TensorIndex>(strideCols)); |
| |
| // Infer padding type. |
| if (size_z == outputPlanes && size_y == outputRows && size_x == outputCols) { |
| // SAME padding. |
| const TensorIndex dz = numext::maxi<TensorIndex>(0, (size_z - 1) * stridePlanes + kernelPlanes - inputPlanes); |
| const TensorIndex dy = numext::maxi<TensorIndex>(0, (size_y - 1) * strideRows + kernelRows - inputRows); |
| const TensorIndex dx = numext::maxi<TensorIndex>(0, (size_x - 1) * strideCols + kernelCols - inputCols); |
| |
| forward_pad_z = dz / 2; |
| forward_pad_y = dy / 2; |
| forward_pad_x = dx / 2; |
| } else { |
| // VALID padding. |
| forward_pad_z = 0; |
| forward_pad_y = 0; |
| forward_pad_x = 0; |
| } |
| const TensorIndex padding_ztop = kernelPlanes - 1 - forward_pad_z; |
| const TensorIndex padding_top = kernelRows - 1 - forward_pad_y; |
| const TensorIndex padding_left = kernelCols - 1 - forward_pad_x; |
| |
| const TensorIndex padding_zbottom = inputPlanes + kernelPlanes - 1 - (outputPlanes - 1) * stridePlanes - 1 - padding_ztop; |
| const TensorIndex padding_bottom = inputRows + kernelRows - 1 - (outputRows - 1) * strideRows - 1 - padding_top; |
| const TensorIndex padding_right = inputCols + kernelCols - 1 - (outputCols - 1) * strideCols - 1 - padding_left; |
| |
| eigen_assert(padding_ztop >= 0); |
| eigen_assert(padding_zbottom >= 0); |
| eigen_assert(padding_top >= 0); |
| eigen_assert(padding_left >= 0); |
| eigen_assert(padding_bottom >= 0); |
| eigen_assert(padding_right >= 0); |
| |
| // The kernel has dimensions filters X channels X patch_planes X patch_rows X patch_cols. |
| // We need to reverse the kernel along the spatial dimensions. |
| array<bool, 5> kernel_reverse; |
| if (isColMajor) { |
| kernel_reverse[0] = false; |
| kernel_reverse[1] = false; |
| kernel_reverse[2] = true; |
| kernel_reverse[3] = true; |
| kernel_reverse[4] = true; |
| } else { |
| kernel_reverse[0] = true; |
| kernel_reverse[1] = true; |
| kernel_reverse[2] = true; |
| kernel_reverse[3] = false; |
| kernel_reverse[4] = false; |
| } |
| |
| DSizes<TensorIndex, 3> kernel_dims; |
| if (isColMajor) { |
| kernel_dims[0] = kernelFilters; |
| kernel_dims[1] = kernelChannels; |
| kernel_dims[2] = kernelRows * kernelCols * kernelPlanes; |
| } else { |
| kernel_dims[0] = kernelRows * kernelCols * kernelPlanes; |
| kernel_dims[1] = kernelChannels; |
| kernel_dims[2] = kernelFilters; |
| } |
| |
| // The output_backward has dimensions out_depth X out_planes X out_rows X out_cols X OTHERS |
| // When we extract the image patches from output_backward, it will have dimensions: |
| // out_depth X (patch_planes * patch_rows * patch_cols) X (input_planes * input_rows * input_cols * OTHERS) |
| DSizes<TensorIndex, 3> pre_contract_dims; |
| if (isColMajor) { |
| pre_contract_dims[0] = kernelFilters; |
| pre_contract_dims[1] = kernelRows * kernelCols * kernelPlanes; |
| pre_contract_dims[2] = inputRows * inputCols * inputPlanes; |
| for (int i = 4; i < NumDims; ++i) { |
| pre_contract_dims[2] *= out.dimension(i); |
| } |
| } else { |
| pre_contract_dims[2] = kernelFilters; |
| pre_contract_dims[1] = kernelRows * kernelCols * kernelPlanes; |
| pre_contract_dims[0] = inputRows * inputCols * inputPlanes; |
| for (int i = 0; i < NumDims - 4; ++i) { |
| pre_contract_dims[0] *= out.dimension(i); |
| } |
| } |
| |
| // We will contract along dimensions (0, 2) in kernel and (0, 1) in |
| // output_backward, if this is col-major, and |
| // dimensions (0, 2) in kernel and (1, 2) in output_backward, if this row-major. |
| array<IndexPair<TensorIndex>, 2> contract_dims; |
| if (isColMajor) { |
| // col-major: kernel.contract(output.patches) |
| contract_dims[0] = IndexPair<TensorIndex>(0, 0); |
| contract_dims[1] = IndexPair<TensorIndex>(2, 1); |
| } else { |
| // row-major: output.patches.contract(kernel) |
| contract_dims[0] = IndexPair<TensorIndex>(1, 0); |
| contract_dims[1] = IndexPair<TensorIndex>(2, 2); |
| } |
| |
| // Post contraction, the dimensions of the input_backprop is |
| // channels X input_planes X input_rows X input_cols X OTHERS |
| DSizes<TensorIndex, NumDims> post_contract_dims; |
| if (isColMajor) { |
| post_contract_dims[0] = kernelChannels; |
| post_contract_dims[1] = inputPlanes; |
| post_contract_dims[2] = inputRows; |
| post_contract_dims[3] = inputCols; |
| for (int i = 4; i < NumDims; ++i) { |
| post_contract_dims[i] = out.dimension(i); |
| } |
| } else { |
| post_contract_dims[NumDims - 1] = kernelChannels; |
| post_contract_dims[NumDims - 2] = inputPlanes; |
| post_contract_dims[NumDims - 3] = inputRows; |
| post_contract_dims[NumDims - 4] = inputCols; |
| for (int i = 0; i < NumDims - 4; ++i) { |
| post_contract_dims[i] = out.dimension(i); |
| } |
| } |
| |
| DSizes<TensorIndex, NumDims> strides; |
| for (int i = 0; i < NumDims; i++) { |
| strides[i] = 1; |
| } |
| if (isColMajor) { |
| strides[1] = stridePlanes; |
| strides[2] = strideRows; |
| strides[3] = strideCols; |
| } else { |
| strides[NumDims - 2] = stridePlanes; |
| strides[NumDims - 3] = strideRows; |
| strides[NumDims - 4] = strideCols; |
| } |
| |
| return choose( |
| Cond<internal::traits<OutputBackward>::Layout == ColMajor>(), |
| kernel.reverse(kernel_reverse) |
| .reshape(kernel_dims) |
| .contract( |
| output_backward.extract_volume_patches(kernelPlanes, kernelRows, kernelCols, |
| 1, 1, 1, stridePlanes, strideRows, strideCols, |
| padding_ztop, padding_zbottom, |
| padding_top, padding_bottom, |
| padding_left, padding_right) |
| .reshape(pre_contract_dims), |
| contract_dims) |
| .reshape(post_contract_dims), |
| output_backward.extract_volume_patches(kernelPlanes, kernelRows, kernelCols, |
| 1, 1, 1, stridePlanes, strideRows, strideCols, |
| padding_ztop, padding_zbottom, |
| padding_top, padding_bottom, |
| padding_left, padding_right) |
| .reshape(pre_contract_dims) |
| .contract(kernel.reverse(kernel_reverse).reshape(kernel_dims), |
| contract_dims) |
| .reshape(post_contract_dims)); |
| } |
| |
| |
| /** CuboidConvolutionBackwardKernel |
| * \ingroup CXX11_NeuralNetworks_Module |
| * |
| * \brief Computes the backprop for the filter of a 3D convolution. |
| * |
| * The output_backward parameter is expected to be a tensor with a rank of 4 or more (channels, depth, height, width, and optionally others) |
| * The kernel parameter is expected to be a 4D tensor (filters, channels, kernel_depth, kernel_height, kernel_width) |
| * output_backward and kernel have to be in the same layout. |
| * |
| * The dimensions of the result will be filters, depth, height, width (and others if applicable). |
| * |
| * It is possible to swap the order of the depth, width and height dimensions provided that the same order is used in the input, the kernel, and the output. |
| * |
| * All dimension orders above are given for col-major, and should be reversed for row-major. |
| */ |
| template <typename OutputBackward, typename Input> |
| EIGEN_ALWAYS_INLINE static const typename internal::conditional< |
| internal::traits<OutputBackward>::Layout == ColMajor, |
| const TensorShufflingOp< |
| const array<typename internal::traits<OutputBackward>::Index, 5>, |
| const TensorReverseOp< |
| const array<bool, 5>, |
| const TensorReshapingOp< |
| const DSizes<typename internal::traits<OutputBackward>::Index, 5>, |
| const TensorContractionOp< |
| const array< IndexPair<typename internal::traits<Input>::Index>, 2>, |
| const TensorReshapingOp< |
| const DSizes<typename internal::traits<Input>::Index, 3>, |
| const Input>, |
| const TensorReshapingOp< |
| const DSizes< typename internal::traits<OutputBackward>::Index, 4>, |
| const TensorVolumePatchOp<Dynamic, Dynamic, Dynamic, const OutputBackward> |
| > |
| > |
| > |
| > |
| >, |
| const TensorShufflingOp< |
| const array<typename internal::traits<OutputBackward>::Index, 5>, |
| const TensorReverseOp< |
| const array<bool, 5>, |
| const TensorReshapingOp< |
| const DSizes<typename internal::traits<OutputBackward>::Index, 5>, |
| const TensorContractionOp< |
| const array< IndexPair<typename internal::traits<Input>::Index>, 2>, |
| const TensorReshapingOp< |
| const DSizes< typename internal::traits<OutputBackward>::Index, 4>, |
| const TensorVolumePatchOp<Dynamic, Dynamic, Dynamic, const OutputBackward> |
| >, |
| const TensorReshapingOp< |
| const DSizes<typename internal::traits<Input>::Index, 3>, |
| const Input |
| > |
| > |
| > |
| > |
| > |
| >::type |
| CuboidConvolutionBackwardKernel( |
| const Input& input, const OutputBackward& output_backward, |
| typename internal::traits<Input>::Index kernelPlanes, |
| typename internal::traits<Input>::Index kernelRows, |
| typename internal::traits<Input>::Index kernelCols, |
| const DenseIndex stridePlanes = 1, |
| const DenseIndex strideRows = 1, |
| const DenseIndex strideCols = 1) { |
| typedef typename internal::traits<Input>::Index TensorIndex; |
| TensorRef<Tensor<typename internal::traits<Input>::Scalar, internal::traits<Input>::NumDimensions, internal::traits<Input>::Layout, TensorIndex> > in(input); |
| TensorRef<Tensor<typename internal::traits<OutputBackward>::Scalar, internal::traits<OutputBackward>::NumDimensions, internal::traits<OutputBackward>::Layout, TensorIndex> > out(output_backward); |
| |
| EIGEN_STATIC_ASSERT(internal::traits<Input>::Layout == internal::traits<OutputBackward>::Layout, YOU_MADE_A_PROGRAMMING_MISTAKE); |
| |
| static const bool isColMajor = (internal::traits<Input>::Layout == ColMajor); |
| |
| static const int NumDims = internal::traits<Input>::NumDimensions; |
| EIGEN_STATIC_ASSERT(internal::traits<Input>::NumDimensions == internal::traits<OutputBackward>::NumDimensions, YOU_MADE_A_PROGRAMMING_MISTAKE); |
| |
| const TensorIndex inputPlanes = isColMajor ? in.dimension(1) : in.dimension(NumDims - 2); |
| const TensorIndex inputRows = isColMajor ? in.dimension(2) : in.dimension(NumDims - 3); |
| const TensorIndex inputCols = isColMajor ? in.dimension(3) : in.dimension(NumDims - 4); |
| |
| const TensorIndex outputPlanes = isColMajor ? out.dimension(1) : out.dimension(NumDims - 2); |
| const TensorIndex outputRows = isColMajor ? out.dimension(2) : out.dimension(NumDims - 3); |
| const TensorIndex outputCols = isColMajor ? out.dimension(3) : out.dimension(NumDims - 4); |
| |
| const TensorIndex kernelFilters = isColMajor ? out.dimension(0) : out.dimension(NumDims - 1); |
| const TensorIndex kernelChannels = isColMajor ? in.dimension(0) : in.dimension(NumDims - 1); |
| |
| TensorIndex forward_pad_z, forward_pad_y, forward_pad_x; |
| const TensorIndex size_z = Eigen::divup(inputPlanes, static_cast<TensorIndex>(stridePlanes)); |
| const TensorIndex size_y = Eigen::divup(inputRows, static_cast<TensorIndex>(strideRows)); |
| const TensorIndex size_x = Eigen::divup(inputCols, static_cast<TensorIndex>(strideCols)); |
| |
| // Infer padding type. |
| if (size_z == outputPlanes && size_y == outputRows && size_x == outputCols) { |
| // SAME padding. |
| const TensorIndex dz = numext::maxi<TensorIndex>(0, (size_z - 1) * stridePlanes + kernelPlanes - inputPlanes); |
| const TensorIndex dy = numext::maxi<TensorIndex>(0, (size_y - 1) * strideRows + kernelRows - inputRows); |
| const TensorIndex dx = numext::maxi<TensorIndex>(0, (size_x - 1) * strideCols + kernelCols - inputCols); |
| |
| forward_pad_z = dz / 2; |
| forward_pad_y = dy / 2; |
| forward_pad_x = dx / 2; |
| } else { |
| // VALID padding. |
| forward_pad_z = 0; |
| forward_pad_y = 0; |
| forward_pad_x = 0; |
| } |
| |
| const TensorIndex padding_ztop = kernelPlanes - 1 - forward_pad_z; |
| const TensorIndex padding_top = kernelRows - 1 - forward_pad_y; |
| const TensorIndex padding_left = kernelCols - 1 - forward_pad_x; |
| |
| const TensorIndex padding_zbottom = inputPlanes + kernelPlanes - 1 - (outputPlanes - 1) * stridePlanes - 1 - padding_ztop; |
| const TensorIndex padding_bottom = inputRows + kernelRows - 1 - (outputRows - 1) * strideRows - 1 - padding_top; |
| const TensorIndex padding_right = inputCols + kernelCols - 1 - (outputCols - 1) * strideCols - 1 - padding_left; |
| |
| eigen_assert(padding_ztop >= 0); |
| eigen_assert(padding_zbottom >= 0); |
| eigen_assert(padding_top >= 0); |
| eigen_assert(padding_left >= 0); |
| eigen_assert(padding_bottom >= 0); |
| eigen_assert(padding_right >= 0); |
| |
| // The output_backward has dimensions out_depth X out_plaens X out_rows X out_cols X OTHERS |
| // When we extract the image patches from output_backward (with input as the |
| // kernel), it will have dimensions |
| // (out_depth) X (input_planes * input_rows * input_cols) X (kernel_planes * kernel_rows * kernel_cols) X OTHERS |
| DSizes<TensorIndex, 4> pre_contract_dims; |
| if (isColMajor) { |
| pre_contract_dims[0] = kernelFilters; |
| pre_contract_dims[1] = inputRows * inputCols * inputPlanes; |
| pre_contract_dims[2] = kernelRows * kernelCols * kernelPlanes; |
| pre_contract_dims[3] = 1; |
| for (int i = 4; i < NumDims; ++i) { |
| pre_contract_dims[3] *= out.dimension(i); |
| } |
| } else { |
| pre_contract_dims[3] = kernelFilters; |
| pre_contract_dims[2] = inputRows * inputCols * inputPlanes; |
| pre_contract_dims[1] = kernelRows * kernelCols * kernelPlanes; |
| pre_contract_dims[0] = 1; |
| for (int i = 0; i < NumDims - 4; ++i) { |
| pre_contract_dims[0] *= out.dimension(i); |
| } |
| } |
| |
| // The input has dimensions in_depth X (input_planes * input_rows * input_cols) X OTHERS |
| DSizes<TensorIndex, 3> input_dims; |
| if (isColMajor) { |
| input_dims[0] = kernelChannels; |
| input_dims[1] = inputRows * inputCols * inputPlanes; |
| input_dims[2] = 1; |
| for (int i = 4; i < NumDims; ++i) { |
| input_dims[2] *= in.dimension(i); |
| } |
| eigen_assert(input_dims[2] == pre_contract_dims[3]); |
| } else { |
| input_dims[2] = kernelChannels; |
| input_dims[1] = inputRows * inputCols * inputPlanes; |
| input_dims[0] = 1; |
| for (int i = 0; i < NumDims - 4; ++i) { |
| input_dims[0] *= in.dimension(i); |
| } |
| eigen_assert(input_dims[0] == pre_contract_dims[0]); |
| } |
| |
| // We will contract along dimensions (1, 2) in in and (1, 3) in out, if |
| // this is col-major. |
| // For row-major, it's dimensions (0, 1) in in and (0, 2) in out. |
| array<IndexPair<TensorIndex>, 2> contract_dims; |
| if (isColMajor) { |
| // col-major: in.contract(output.patches) |
| contract_dims[0] = IndexPair<TensorIndex>(1, 1); |
| contract_dims[1] = IndexPair<TensorIndex>(2, 3); |
| } else { |
| // row-major: output.patches.contract(in) |
| contract_dims[0] = IndexPair<TensorIndex>(0, 0); |
| contract_dims[1] = IndexPair<TensorIndex>(2, 1); |
| } |
| |
| // After the contraction, the kernel will have dimension |
| // in_depth X out_depth X kernel_patches X kernel_rows X kernel_cols |
| // We will need to shuffle the first two dimensions and reverse the spatial dimensions. |
| // The end shape is: |
| // out_depth X in_shape X kernel_planes X kernel_rows X kernel_cols |
| |
| // This is the shape of the kernel *before* the shuffling. |
| DSizes<TensorIndex, 5> kernel_dims; |
| if (isColMajor) { |
| kernel_dims[0] = kernelChannels; |
| kernel_dims[1] = kernelFilters; |
| kernel_dims[2] = kernelPlanes; |
| kernel_dims[3] = kernelRows; |
| kernel_dims[4] = kernelCols; |
| } else { |
| kernel_dims[0] = kernelCols; |
| kernel_dims[1] = kernelRows; |
| kernel_dims[2] = kernelPlanes; |
| kernel_dims[3] = kernelFilters; |
| kernel_dims[4] = kernelChannels; |
| } |
| |
| // Flip filters and channels. |
| array<TensorIndex, 5> kernel_shuffle; |
| if (isColMajor) { |
| kernel_shuffle[0] = 1; |
| kernel_shuffle[1] = 0; |
| kernel_shuffle[2] = 2; |
| kernel_shuffle[3] = 3; |
| kernel_shuffle[4] = 4; |
| } else { |
| kernel_shuffle[0] = 0; |
| kernel_shuffle[1] = 1; |
| kernel_shuffle[2] = 2; |
| kernel_shuffle[3] = 4; |
| kernel_shuffle[4] = 3; |
| } |
| |
| // Reverse the spatial dimensions. |
| array<bool, 5> kernel_reverse; |
| if (isColMajor) { |
| kernel_reverse[0] = false; |
| kernel_reverse[1] = false; |
| kernel_reverse[2] = true; |
| kernel_reverse[3] = true; |
| kernel_reverse[4] = true; |
| } else { |
| kernel_reverse[0] = true; |
| kernel_reverse[1] = true; |
| kernel_reverse[2] = true; |
| kernel_reverse[3] = false; |
| kernel_reverse[4] = false; |
| } |
| |
| DSizes<TensorIndex, NumDims> strides; |
| for (int i = 0; i < NumDims; i++) { |
| strides[i] = 1; |
| } |
| if (isColMajor) { |
| strides[1] = stridePlanes; |
| strides[2] = strideRows; |
| strides[3] = strideCols; |
| } else { |
| strides[NumDims - 2] = stridePlanes; |
| strides[NumDims - 3] = strideRows; |
| strides[NumDims - 4] = strideCols; |
| } |
| return choose( |
| Cond<internal::traits<Input>::Layout == ColMajor>(), |
| input.reshape(input_dims) |
| .contract( |
| output_backward.extract_volume_patches( |
| inputPlanes, inputRows, inputCols, 1, |
| 1, 1, stridePlanes, strideRows, strideCols, |
| |
| padding_ztop, padding_zbottom, padding_top, |
| padding_bottom, padding_left, padding_right) |
| .reshape(pre_contract_dims), |
| contract_dims) |
| .reshape(kernel_dims) |
| .reverse(kernel_reverse) |
| .shuffle(kernel_shuffle), |
| output_backward.extract_volume_patches( |
| inputPlanes, inputRows, inputCols, 1, 1, 1, |
| stridePlanes, strideRows, strideCols, padding_ztop, |
| padding_zbottom, padding_top, padding_bottom, |
| padding_left, padding_right) |
| .reshape(pre_contract_dims) |
| .contract(input.reshape(input_dims), contract_dims) |
| .reshape(kernel_dims) |
| .reverse(kernel_reverse) |
| .shuffle(kernel_shuffle)); |
| } |
| |
| } // end namespace Eigen |
| |
| #endif // EIGEN_CXX11_NEURAL_NETWORKS_BACKWARD_CUBOID_CONVOLUTIONS_H |