使用本征张量复制TensorFlows Conv2D操作

时间:2019-04-05 09:58:10

标签: python c++11 tensorflow eigen convolution

我正在尝试在c ++中实现TensorFlow图的轻量级(最小的库依赖性)版本,并且试图使用Eigen Tensor对象来执行图操作。现在,我陷入尝试使用本征Tensor.convolve()方法来尝试并复制TensorFlow的Conv2D操作行为的问题。为简单起见,我最初的Conv2D操作没有填充和跨度。

卷积层的输入是一个51x51x1张量,它正与大小为3x3x1x16的滤波器组卷积。在tensorflow中,这会生成大小为49x49x16的输出张量。使用下面的Eigen代码在C ++中设置相同的操作只会填充输出张量的第一个通道,因此,顶部49x49x1的单元格包含正确的值,但其余1-15的通道未填充。

  Eigen::TensorMap<Eigen::Tensor<float,4> > filter(filter, 3, 3, 1, 16 );
  Eigen::TensorMap<Eigen::Tensor<float,3> > input(inputBuffer, 51, 51, 1 );
  Eigen::TensorMap<Eigen::Tensor<float,3> > output(outputBuffer, 49, 49, 16);

  Eigen::array<ptrdiff_t, 2> convDims({0, 1});
  output = input.convolve(filter, convDims);

我以为我对这些功能的作用以及它们没有执行相同的操作的理解是错过的。为了使我的实现正常工作,我尝试遍历16个过滤器通道,并将卷积方法分别应用于每个通道,但是我遇到了以下代码无法理解的编译器错误:

  for (int s=0; s<16; ++s)
  {
    Eigen::array<int, 4> fOffset = {0, 0, 0, s};
    Eigen::array<int, 4> fExtent = {3, 3, 1, 1};

    Eigen::array<int, 3> oOffset = {0, 0, s};
    Eigen::array<int, 3> oExtent = {49, 49, 1};

    auto filterSlice = filter.slice(fOffset, fExtent);

    output.slice(oOffset, oExtent) = input.convolve(filterSlice, convDims);
  }

此代码在Eigen Tensor代码内的某处产生以下错误,这可能与sli​​ce方法的结果赋值有关,但我不确定。如果将结果分配给自动类型,则会进行编译,但是如果稍后对结果进行评估,则不会进行编译。

如果有人知道如何解决此错误,或更笼统地说我如何使用本征张量来复制Conv2D操作,那将是一个很大的帮助。

/home/user/tensorflow_xla/bcc-2.0.2-gcc/sparc-gaisler-elf/include/unsupported/Eigen/CXX11/src/Tensor/TensorConvolution.h: In instantiation of 'void Eigen::TensorEvaluator<const Eigen::TensorConvolutionOp<Dimensions, InputXprType, KernelXprType>, Device>::preloadKernel() [with Indices = const std::array<int, 2>; InputArgType = const Eigen::TensorMap<Eigen::Tensor<float, 3> >; KernelArgType = const Eigen::TensorSlicingOp<const std::array<int, 4>, const std::array<int, 4>, Eigen::TensorMap<Eigen::Tensor<float, 4> > >; Device = Eigen::DefaultDevice]':
/home/user/tensorflow_xla/bcc-2.0.2-gcc/sparc-gaisler-elf/include/unsupported/Eigen/CXX11/src/Tensor/TensorConvolution.h:383:18:   required from 'bool Eigen::TensorEvaluator<const Eigen::TensorConvolutionOp<Dimensions, InputXprType, KernelXprType>, Device>::evalSubExprsIfNeeded(Eigen::TensorEvaluator<const Eigen::TensorConvolutionOp<Dimensions, InputXprType, KernelXprType>, Device>::Scalar*) [with Indices = const std::array<int, 2>; InputArgType = const Eigen::TensorMap<Eigen::Tensor<float, 3> >; KernelArgType = const Eigen::TensorSlicingOp<const std::array<int, 4>, const std::array<int, 4>, Eigen::TensorMap<Eigen::Tensor<float, 4> > >; Device = Eigen::DefaultDevice; Eigen::TensorEvaluator<const Eigen::TensorConvolutionOp<Dimensions, InputXprType, KernelXprType>, Device>::Scalar = float]'
/home/user/tensorflow_xla/bcc-2.0.2-gcc/sparc-gaisler-elf/include/unsupported/Eigen/CXX11/src/Tensor/TensorAssign.h:146:62:   required from 'bool Eigen::TensorEvaluator<const Eigen::TensorAssignOp<LhsXprType, RhsXprType>, Device>::evalSubExprsIfNeeded(Eigen::TensorEvaluator<const Eigen::TensorAssignOp<LhsXprType, RhsXprType>, Device>::Scalar*) [with LeftArgType = Eigen::TensorSlicingOp<const std::array<int, 3>, const std::array<int, 3>, Eigen::TensorMap<Eigen::Tensor<float, 3> > >; RightArgType = const Eigen::TensorConvolutionOp<const std::array<int, 2>, const Eigen::TensorMap<Eigen::Tensor<float, 3> >, const Eigen::TensorSlicingOp<const std::array<int, 4>, const std::array<int, 4>, Eigen::TensorMap<Eigen::Tensor<float, 4> > > >; Device = Eigen::DefaultDevice; Eigen::TensorEvaluator<const Eigen::TensorAssignOp<LhsXprType, RhsXprType>, Device>::Scalar = float]'
/home/user/tensorflow_xla/bcc-2.0.2-gcc/sparc-gaisler-elf/include/unsupported/Eigen/CXX11/src/Tensor/TensorExecutor.h:45:16:   required from 'static void Eigen::internal::TensorExecutor<Expression, Device, Vectorizable, Tileable>::run(const Expression&, const Device&) [with Expression = const Eigen::TensorAssignOp<Eigen::TensorSlicingOp<const std::array<int, 3>, const std::array<int, 3>, Eigen::TensorMap<Eigen::Tensor<float, 3> > >, const Eigen::TensorConvolutionOp<const std::array<int, 2>, const Eigen::TensorMap<Eigen::Tensor<float, 3> >, const Eigen::TensorSlicingOp<const std::array<int, 4>, const std::array<int, 4>, Eigen::TensorMap<Eigen::Tensor<float, 4> > > > >; Device = Eigen::DefaultDevice; bool Vectorizable = false; bool Tileable = false]'
/home/user/tensorflow_xla/bcc-2.0.2-gcc/sparc-gaisler-elf/include/unsupported/Eigen/CXX11/src/Tensor/TensorMorphing.h:448:65:   required from 'Eigen::TensorSlicingOp<StartIndices, Sizes, XprType>& Eigen::TensorSlicingOp<StartIndices, Sizes, XprType>::operator=(const OtherDerived&) [with OtherDerived = Eigen::TensorConvolutionOp<const std::array<int, 2>, const Eigen::TensorMap<Eigen::Tensor<float, 3> >, const Eigen::TensorSlicingOp<const std::array<int, 4>, const std::array<int, 4>, Eigen::TensorMap<Eigen::Tensor<float, 4> > > >; StartIndices = const std::array<int, 3>; Sizes = const std::array<int, 3>; XprType = Eigen::TensorMap<Eigen::Tensor<float, 3> >]'
../tfmin_generated/terrain_model.cpp:215:92:   required from here
/home/user/tensorflow_xla/bcc-2.0.2-gcc/sparc-gaisler-elf/include/unsupported/Eigen/CXX11/src/Tensor/TensorConvolution.h:527:52: error: 'Eigen::TensorEvaluator<const Eigen::TensorSlicingOp<const std::array<int, 4>, const std::array<int, 4>, Eigen::TensorMap<Eigen::Tensor<float, 4> > >, Eigen::DefaultDevice>::Dimensions {aka const struct std::array<int, 4>}' has no member named 'TotalSize'
       size_t kernel_sz = m_kernelImpl.dimensions().TotalSize() * sizeof(Scalar);

1 个答案:

答案 0 :(得分:1)

因此,我最终找到了仅使用本征张量函数调用即可执行2D卷积的方法,而无需任何循环。帮助我到达这里的代码是将我链接到的Tensorflow eigen_spatial_convolutions.h文件@jdehesa。我链接的行具有对行数据和列数据进行Conv2D操作所需的特征码,因此您可能只需要一半的数据。

从根本上讲,您需要使用Eigen方法extract_image_patches从输入张量中提取每个过滤器实例的感知字段。然后,将这个和内核张量的输出重塑为2D张量。这意味着每个内核都是重塑内核张量的垂直列,并且重塑图像补丁的每一行都是每个补丁。然后执行收缩,实际上是这两个2D张量的矩阵相乘,然后将结果重新整形为正确的尺寸以产生输出。

一开始我花了一段时间才明白,但这是可以做到的。

outputTensor = inputTensor
.extract_image_patches(kern_w, kern_h, stride_w, stride_h, dilation_w, dilation_h, padding)
.reshape(Eigen::array<int, 2>({patch_count, kern_w*kern_h}))
.contract(kernalTensor.reshape(Eigen::array<int, 2>({kern_w*kern_h, kern_count})), {Eigen::IndexPair < int > (1, 0)})
.reshape(Eigen::array<int, 3>({ output_w, output_h, kern_count }));