有没有办法知道在tensorflow中调用了哪个c++核心函数?

Posted

技术标签:

【中文标题】有没有办法知道在tensorflow中调用了哪个c++核心函数?【英文标题】:Is there any way to know which c++ core function is called in tensorflow? 【发布时间】:2020-11-08 02:06:58 【问题描述】:

听说tensorflow是用python封装的,核心功能是用c++实现的。 我想知道调用 Python 代码后调用了哪个核心 c++ 函数。有没有办法知道? tensorflow 分析器仅提供有关 python 函数的信息。谢谢

【问题讨论】:

【参考方案1】:

要获得 C++ 代码,您需要经历 3 个深度级别:Python 实现、包装器和 C++。

如果是 OP(如 Conv / Matmul / ...)

首先,您需要跟踪 python 实现调用的内容。如果你使用一些像 Keras 这样的高级库工具,那可能会很困难。如果您直接在 TF 中调用数学运算(例如 nn.conv2d),会更容易。 大多数操作都在tensorflow/python/ops 中实现。比如函数nn.conv2dtensorflow/python/ops/nn.ops.py中实现。

如您所见,此操作(与大多数操作一样)将工作委托给 gen_nn_ops.conv2d.py。在构建过程中有自动生成的文件,因此除非您愿意检查 bazel 文件并从源代码构建,否则您无法查看此文件。

幸运的是,在我看来,gen_ 文件中可用的函数与.cc 文件中定义的操作之间存在直接映射。

通过调查tensorflow/core/ops/nn_ops.cc你可以找到Conv Op的注册

REGISTER_OP("Conv2D")
    .Input("input: T")
    .Input("filter: T")
    .Output("output: T")
    .Attr("T: half, bfloat16, float, double")
    .Attr("strides: list(int)")
    .Attr("use_cudnn_on_gpu: bool = true")
    .Attr(GetPaddingAttrStringWithExplicit())
    .Attr(GetExplicitPaddingsAttrString())
    .Attr(GetConvnetDataFormatAttrString())
    .Attr("dilations: list(int) = [1, 1, 1, 1]")
    .SetShapeFn(shape_inference::Conv2DShapeWithExplicitPadding);

很遗憾,这个宏只告诉tensorflow有Conv2D这样的操作,却没有说明它应该如何运行。

在 tensorflow 中,Op 指定了需要做什么,但 Kernel 才是真正完成这项工作的那个。您可以通过查找REGISTER_KERNEL_BUILDER 宏来找到可以运行给定操作的内核。它负责将内核与 Op 匹配。 对于conv2d,你可以在tensorflow/core/kernels/conv_ops.cc找到一个


#define REGISTER_CPU(T)                                         \
  REGISTER_KERNEL_BUILDER(                                      \
      Name("Conv2D").Device(DEVICE_CPU).TypeConstraint<T>("T"), \
      Conv2DOp<CPUDevice, T>);

// If we're using the alternative GEMM-based implementation of Conv2D for the
// CPU implementation, don't register this EigenTensor-based version.
#if !defined(USE_GEMM_FOR_CONV)
TF_CALL_half(REGISTER_CPU);
TF_CALL_float(REGISTER_CPU);
TF_CALL_double(REGISTER_CPU);
#endif  // USE_GEMM_FOR_CONV

这终于把我们带到了我们正在寻找的东西。内核有计算方法,所以我们对 Conv2DOp::Compute 感兴趣。 这是(在同一个文件中定义):

  void Compute(OpKernelContext* context) override 
    // Input tensor is of the following dimensions:
    // [ batch, in_rows, in_cols, in_depth ]
    const Tensor& input = context->input(0);

    // Input filter is of the following dimensions:
    // [ filter_rows, filter_cols, in_depth, out_depth]
    const Tensor& filter = context->input(1);

    Conv2DDimensions dimensions;
    OP_REQUIRES_OK(context,
                   ComputeConv2DDimension(params_, input, filter, &dimensions));

    TensorShape out_shape = ShapeFromFormat(
        params_.data_format, dimensions.batch, dimensions.out_rows,
        dimensions.out_cols, dimensions.out_depth);

    // Output tensor is of the following dimensions:
    // [ in_batch, out_rows, out_cols, out_depth ]
    Tensor* output = nullptr;
    OP_REQUIRES_OK(context, context->allocate_output(0, out_shape, &output));
    
    ...  // Skipped for clarity

    if (params_.padding != EXPLICIT &&
        LaunchDeepConvOp<Device, T>::Run(
            context, input, filter, dimensions.batch, dimensions.input_rows,
            dimensions.input_cols, dimensions.in_depth, dimensions.filter_rows,
            dimensions.filter_cols, dimensions.pad_rows_before,
            dimensions.pad_cols_before, dimensions.out_rows,
            dimensions.out_cols, dimensions.out_depth, dimensions.dilation_rows,
            dimensions.dilation_cols, dimensions.stride_rows,
            dimensions.stride_cols, output, params_.data_format)) 
      return;
    
    ...

这是旅程的终点​​。一些操作在这个地方有实际的实现。 Conv2D 不是很令人满意 - 事实证明它将工作委托给LaunchDeepConvOp。如果需要,您可以深入挖掘。

如果不是操作

操作在 TF 中非常特殊。其他代码通过C API链接到python。

C api 可用作c_api.ccc_api.h。头文件声明了可用于 python 的 C 函数。源文件 (.cc) 是 C 和 C++ 之间的桥梁 - 它定义了调用相应 C++ 函数的 C 函数(或更准确地说,具有 C 链接的函数)。如果你知道 C 函数,就很容易追踪调用了哪个 C++ 函数。

从 Python 来看,它通常看起来像

# Import
from tensorflow.python import pywrap_tensorflow as c_api

...

# Usage
def get_all_registered_kernels():
  """Returns a KernelList proto of all registered kernels.
  """
  buf = c_api.TF_GetAllRegisteredKernels()

如您所见,名称是匹配的。生成了这个包装器的实现,不用找了。

【讨论】:

以上是关于有没有办法知道在tensorflow中调用了哪个c++核心函数?的主要内容,如果未能解决你的问题,请参考以下文章

UIActivityViewController - 有没有办法知道选择了哪个活动?

有没有办法堆叠两个 tensorflow 数据集?

有没有办法在 Tensorflow 自定义层(在 TPU 上)中动态 N 次复制张量?

在 Qt/C++ 中,有没有办法将按钮单击事件列表连接到一个插槽?

如何知道我使用的是哪个版本的 C 语言?

谁调用了我的 RPC?