TensorFlow深度学习框架扩展知识详解

目录

TensorFlow设计原则

TensorFlow架构及源码解析

源码解析

源码一级目录

tensorflow目录

core目录

TensorFlow ROCm port 设计

TensorFlow策略详解

1. 基础知识

2. 数据流

3. 图构建及优化

构建计算图

会话是啥

4. op层构建

4.1 从python端找到对应的c源码文件

4.2 op和kernel的对应


TensorFlow设计原则

​ 借鉴数据流系统高层编程模型的思想,期望用户通过简单的数据流编程来实现在不同形态、不同规模(异构)设备上的高效开发和执行,具体来说TensorFlow的设计原则主要集中在三个方面:性能高、易开发、可移植。

TensorFlow架构及源码解析

​ TensorFlow 是一种采用数据流图(data flow graphs),用于数值计算的开源软件库。其中 Tensor 代表传递的数据为张量(多维数组),Flow 代表使用计算图进行运算。数据流图用「节点」(nodes)和「边」(edges)组成的有向图来描述数学运算。nodes一般用来表示施加的数学操作,但也可以表示数据输入的起点和输出的终点,或者是读取 / 写入持久变量(persistent variable)的终点。edges表示节点之间的输入 / 输出关系。

源码解析

学习环境

项目内容
计算卡DCU
源码GitHub - ROCm/tensorflow-upstream at r1.15-rocm
测试环境docker-tf-rocm-r1.15
TF1.15
python3.6.9
OSUbuntu

首先了解一下bazel如何组织编译tensorflow代码。

源码一级目录
目录/文件内容
rocm_docsrocm平台上tensorflow usage介绍
tensorflowtensorflow的核心代码
third_partytesnorflow使用的第三方库:eigen3(特征运算,SVD、LU分解等)/fft2d/protobuf...
tools只有tf_env_collect.sh
configure配置tensorflow的安装环境
tensorflow目录
目录内容
/c面向用户的 C++ 编程 API
/cc
/compiler运行时优化,分析计算图,对op进行融合,提高计算效率,XLA技术等
/contirb存放有其他项目贡献者添加的相关贡献代码,非核心官方代码
/coretensorflow 的核心代码模块,基本全是C++,运行时 图操作 op定义 kernel实现
/examples
/g3doc官方文档
/go面向用户的GO语言编程API
/java面向用户的java语言编程API
/js
/lite
/python面向用户的python语言编程API
security
stream_executortensorflow 流图的并行计算执行,核心代码,主要是关于cuda/rocm的封装。
tools一些工具
core目录
目录/文件内容
api_def面向用户的上层接口OP定义
common_runtime本地运行时,包含 会话(session)、线程(thread),内存管理(memory), 设备调度(device)等基本运行库。
debug
distributed_runtime分布式运行时,分布式情况下的运行库,提供相关运行支撑。包括主控进程、工作进程以及远程通信
example
framework框架基础模块定义,主要是通用组件的结构格式定义;
graph计算流图相关基础操作(类结构),包括 拆分、合并、执行 等操作,被外面的 executor 调用;
grappler图优化部分,主要包括计算图的优化以及计算图代价模型的建立
kernels计算核函数,op的源码实现,包括卷积/激活函数等计算;
lib公共基础库用于内部调用,包括 hash、io、jpeg、math 等;
nccl异步通信管理
ops计算节点部分,计算节点注册逻辑,对 kernels下的op进行注册和对外声明;
platform运行平台相关的(hadoop/windows)
profiler使用tfprof 优化
protobuftensorflow下各个 模块间 进行 数据传输 的 数据结构定义,通过proto进行配置实现。
publicSession定义和使用不同面向用户API进行计算的usage
summary模型文件的保存
tpu嵌入tpu的优化
user_ops用户自定义op,tensorflow的扩展模块
util

TensorFlow ROCm port 设计

1**、系统环境**

  • configure:

设置 TF_ENABLE_XLA=1

增加TF_NEED_ROCM=1 ROCM_TOOLKIT_PATH=/opt/rocm,用于rocm平台环境识别;

  • third_party/gpus:

添加rocm_configure.bzl

为rocm库等的调用新建 rocm/目录

编译过程中rocm_configure.bzl会调用新添加的crosstool,crosstool/CROSSTOOL_hipcc.tpl

并添加 crosstool/clang/bin/crosstool_wrapper_driver_rocm.tpl作为编译和链接的wrapper

  • tensorflow/workspace.bzl:

采用 rocm_configrue() 识别rocm环境

为适应rocm平台修改eigen的fetch方式

修改一些头文件的链接

  • tensorflow/tensorflow.bzl:

重命名 tf_cuda_library() 为 tf_gpu_library

重命名cuda_py_tests() 为 gpu_py_tests()

重命名tf_cuda_test_tags()为tf_gpu_tests_tags()

添加调用 ROCm-specific functions的逻辑代码,例如: if_rocm() 或者if_rocm_is_configured()

2**、StreamExecutor**

添加执行StreamExecutor 的Rocm 后端

添加目录 tensorflow/stream_executor/rocm,包含rocm执行StreamExecutor的接口

集成 HIP runtime APIs for stream, memory management, and copy

集成MIOpen计算库

集成 使用GEMM 操作的rocBLAS库函数

集成 rocRAND 库用于RNG操作;(基本没有被用到)

集成rocFFT 库用于 FFT 操作

3**、Common Runtime**

在目录tensorflow/core/commmon_runtime/gpu下:

  • gpu_device.cc

强制使用rocm 平台

重命名 EigenCudaStreamDevice为EigenROCmStreamDevice

移除CUDA 算力计算代码并替换上rocmisaversion

  • gpu_init.cc, pool_allocator.h, process_state.cc, process_state.h

强制使用rocm 平台

摆脱对于 CUPTI的依赖

  • gpu_util.h, gpu_util.cc

增加 DnnScratchAllocator 替代CudnnScratchAllocator

4**、GPU kernel implementation**

  • 重命名下述文件,使其能够兼容nv和rocm两个平台

cuda_device_array.h to gpu_device_array.h

cuda_device_array_gpu.h to gpu_device_array_gpu.h

cudnn_pooling_gpu.cc to dnn_pooling_gpu.cc

util/cuda_kernel_helper.h to util/gpu_kernel_helper.h

宏定义区分两个平台TENSORFLOW_USE_ROCM 和GOOGLE_CUDA

CUDA 平台:使能 GOOGLE_CUDA

ROCm 平台: 使能 TENSORFLOW_USE_ROCM

  • EIGEN_USE_HIP

配合 TENSORFLOW_USE_ROCM 宏一起使用

  • Launchkernel**宏**

使用GpuLaunchKernel 替代CUDA <<< >>>

(后面使用llvm编译器之后可以不需要替换了)

util/gpu_kernel_helper.h:添加GpuLaunchKernel

重命名CudaLaunchConfig为 GpuLaunchConfig

List of supported operators tensorflow-upstream/rocm_docs/core_kernels.md at develop-upstream · ROCm/tensorflow-upstream · GitHub

5**、Fusion Support**

用于rocm平台计算性能提升

  • 支持的融合方式,默认开启
Convolution --> Bias --> Activation (forward and inference)` `BatchNorm --> Activation (forward, backward, and inferece)` `Add + Relu` `AddN + ReluGrad
  • 环境变量设置,禁用某些功能
TF_ROCM_FUSION_DISABLE_CBA=``1` `禁用 Convolution+Bias+Activation fusion (forward and inference)` `TF_ROCM_FUSION_DISABLE_BNA =``1` `禁用 BatchNorm+Activation fusions (forward, backward and inference)` `TF_ROCM_FUSION_DISABLE_ADDRELU = ``1` `禁用 Add+Relu fusion` `TF_ROCM_FUSION_DISABLE_ADDNRELUGRAD=``1``禁用 AddN+ReluGrad fusion

TensorFlow策略详解

1. 基础知识

Tensorflow工具或者说深度学习本身就是一个连贯紧密的系统。一般的系统是一个自治独立的、能实现复杂功能的整体。系统的主要任务是对输入进行处理,以得到想要的输出结果。我们之前见过的很多系统都是线性的,就像汽车生产工厂的流水线一样,输入->系统处理→输出。系统内部由很多单一的基本部件构成,这些单一部件具有特定的功能,且需要稳定的特性;系统设计者通过特殊的连接方式,让这些简单部件进行连接,以使它们之间可以进行数据交流和信息互换,来达到相互配合而完成具体工作的目的。

对于任何一个系统来说,都应该拥有稳定、独立、能处理特殊任务的单一部件;且拥有一套良好的内部沟通机制,以让系统可以健康安全的运行。

现实中的很多系统都是线性的,被设计好的、不能进行更改的,比如工厂的流水线,这样的系统并不具备自我调整的能力,无法对外界的环境做出反应,因此也就不具备“智能”。

深度学习(神经网络)之所以具备智能,就是因为它具有反馈机制。深度学习具有一套对输出所做的评价函数(损失函数),损失函数在对神经网络做出评价后,会通过某种方式(梯度下降法)更新网络的组成参数,以期望系统得到更好的输出数据。

由此可见,神经网络的系统主要由以下几个方面组成:

  • 输入
  • 系统本身(神经网络结构),以及涉及到系统本身构建的问题:如网络构建方式、网络执行方式、变量维护、模型存储和恢复等等问题
  • 损失函数
  • 反馈方式:训练方式

定义好以上的组成部分,我们就可以用流程化的方式将其组合起来,让系统对输入进行学习,调整参数。因为该系统的反馈机制,所以,组成的方式肯定需要循环。

而对于Tensorflow来说,其设计理念肯定离不开神经网络本身。所以,学习Tensorflow之前,对神经网络有一个整体、深刻的理解也是必须的。

如何构建、执行一个神经网络? 在Tensorflow中,用计算图来构建网络,用会话来具体执行网络。深入理解了这两点,能够帮助理解Tensorflow的设计思路,以及运行机制。

首先,了解一下计算图和会话。

  • 图(tf.Graph):计算图,主要用于构建网络,本身不进行任何实际的计算。计算图的设计启发是高等数学里面的链式求导法则的图。我们可以将计算图理解为是一个计算模板或者计划书。
  • 会话(tf.session):会话,主要用于执行网络。所有关于神经网络的计算都在这里进行,它执行的依据是计算图或者计算图的一部分,同时,会话也会负责分配计算资源和变量存放,以及维护执行过程中的变量。

TensorFlow==Tensor + Flow,tensor是张量,flow流动,张量是图里面的圈圈(也叫做op)进行流动(线表示传递和变换)。

TensorFlow支持三种数据类型的tensor:

tf.*的op定义都可以在python3.6/dist-packages/tensorflowcore/_init.py这个里面找到。

tensor函数说明
常量 constanttf.constant(value, dtype=None, shape=None, name='Const', verify_shape=False)*tensorflow/python/framework/constant_op.py常量是其值不能改变的张量。
变量 variabletf.Variable(, name=)*tensorflow/python/ops/variables.py当一个tensor在会话中的值需要更新时,使用变量来表示。例如,在神经网络中,训练中不停的更新权重,需要将权重声明为变量。变量在使用前需要被显示初始化。需要注意的是,常量存储在计算图的定义中,每次加载图时都会加载相关变量。换句话说,它们是占用内存的。另一方面,变量又是分开存储的。它们可以存储在参数服务器上。
占位符 placeholdertf.placeholder(dtype, shape=None, name=None)*tensorflow/python/ops/array_ops.py用于将值输入 TensorFlow 图中。它们可以和 feed_dict 一起使用来输入数据。在训练神经网络时,用于提供新的训练样本。在会话中运行计算图时,可以为占位符赋值。这样在构建一个计算图时不需要真正地输入数据。需要注意的是,占位符不包含任何数据,因此不需要初始化它们。
2. 数据流
3. 图构建及优化
构建计算图
操作定义
tf.Graph()tensorflow/python/framework/ops.py(有一系列操作)

计算图从创建到真正的执行,需要经历多次处理,大致分为以下四个步骤:

  • 计算图剪枝:根据输入输出列表在完整计算图上进行剪枝操作,从而得到本地执行所需要的最小依赖计算图。
  • 计算图分配:在最小依赖计算图上,根据特定设备的分配原则及操作(Op)的设备约束对计算图中的节点进行设备分配。
  • 计算图优化:TF对计算图做系列优化操作,以提高计算图的运行效率

图优化由Grappler模块来实现,典型的优化方法包括:ConstFold(常量折叠等)、Arithmetic(算术简化)、Layout(布局优化)、Remapper(算子融合)。算子融合可以充分利用芳村时间和空间局部性。

ConstFold:分析静态图过程中,检测到一些常量节点可以提前计算

Arithmetic:公共子表达式消除和算术简化

Layout:主要针对GPU运算,TF中Tensor默认采用NHWC格式,而GPU中NCHW计算更高效 NHWC2NCHW

Remapper:算子融合:Conv2D + BiasAdd + Activation Conv2D + FusedBatchNorm + Activation MatMul + BiasAdd + Activation

  • 计算图切分:根据划分结果,对计算图进行切分,从而为每个设备创建自身的计算子图。

当我们定义常量/变量或者啥啥操作时,tensorflow会默认生成一张计算图。

创建一个常量

graph = tf.Graph()``with graph.as_default():`` ``img = tf.constant(``1.0``, shape=[``1``,``5``,``5``,``3``])

计算图中会生成一个node,一个node结点由name, op, input, attrs组成,即结点名称、操作、输入以及一系列的属性(类型、形状、值)等,计算图就是由这样一个一个的node组成的。

对于tf.constant()函数,计算图上只会生成一个node,但有的函数,如tf.Variable(initializer, name)(注意其第一个参数是初始化器)就会生成多个node结点。

继续添加代码

img = tf.constant(``1.0``, shape=[``1``,``5``,``5``,``3``])``k = tf.constant(``1.0``, shape=[``3``,``3``,``3``,``1``])

注意,如果没有对结点进行命名,Tensorflow自动会将其命名为:Const、Const_1、const_2......。

创建一个变量

img = tf.constant(``1.0``, shape=[``1``,``5``,``5``,``3``])``k = tf.constant(``1.0``, shape=[``3``,``3``,``3``,``1``])``kernel = tf.Variable(k)

执行完tf.Variable()函数后,一共产生了三个结点:

Variable:变量维护(不存放实际的值) Variable/Assign:变量分配 Variable/read:变量使用 图中只是完成了操作的定义,但并没有执行操作(如Variable/Assign结点的Assign操作,所以,此时候变量依然不可以使用,这就是为什么要在会话中初始化的原因)。

添加计算代码

img = tf.constant(``1.0``, shape=[``1``,``5``,``5``,``3``])``k = tf.constant(``1.0``, shape=[``3``,``3``,``3``,``1``])``kernel = tf.Variable(k)``y = tf.nn.conv2d(img, kernel, strides=[``1``,``2``,``2``,``1``], padding=``"SAME"``)

在执行之前,要进行初始化

img = tf.constant(``1.0``, shape=[``1``,``5``,``5``,``3``])``k = tf.constant(``1.0``, shape=[``3``,``3``,``3``,``1``])``kernel = tf.Variable(k)``y2 = tf.nn.conv2d(img, kernel, strides=[``1``,``2``,``2``,``1``], padding=``"SAME"``)``init = tf.global_variables_initializer()

执行完tf.global_variables_initializer()函数以后,计算图如下:

tf.global_variables_initializer()产生了一个名为init的node,该结点将所有的Variable/Assign结点作为输入,以达到对整张计算图中的变量进行初始化。 所以,在开启会话后,执行的第一步操作,就是变量初始化(当然变量初始化的方式有很多种,我们也可以显示调用tf.assign()来完成对单个结点的初始化)。

会话是啥

tf.Session()

4. op层构建
4.1 从python端找到对应的c源码文件

4.1.1 定位一个op的源码位置

以tf.nn.conv2d为例 来介绍一下吧

$ grep -rn ``'class Variable('` `-R ./*``./tensorflow/python/ops/variables.py:``267``:``class` `Variable(six.with_metaclass(VariableMetaclass,

查找op定义的文件: tensorflow/python/ops/nn_ops.py

conv2d 展开源码

咦?gen_nn_ops.conv2d这个在哪里呢? 查找bazel编译

在这里 tensorflow/python/BUILD

load(``"//tensorflow/python:build_defs.bzl"``,``"tf_gen_op_wrapper_private_py"``)``tf_gen_op_wrapper_private_py(``name=``"nn_ops_gen"``,``visibility=[``"//learning/brain/python/ops:__pkg__"``,``"//tensorflow/compiler/tests:__pkg__"``,``"//tensorflow/contrib/quantization:__pkg__"``,``"//tensorflow/python/kernel_tests:__pkg__"``,``"//tensorflow/python/tools:__pkg__"``,``],``)

tensorflow/python/build_defs.bzl里面 tf_gen_op_wrapper_private_py 调用 tf_gen_op_wrapper_py (tensorflow/tensorflow.bzl)

展开源码

可以看到是从tensorflow/python/framework/python_op_gen_main.cc开始构建的,最终生成的文件是tensorflow/python/ops/gen_nn_ops.py(安装路径下) 在tensorflow/core/ops/ops.pbtxt中包含所有ops的操作

4.1.2 python和cpp函数名的对应

在底层cpp代码中,google采用的是驼峰形式去编写cpp的代码,如AbcDefGh,而前端语言python遵循的是小写下划线的方式,如abc_def_gh,所以这二者中,tensorflow内置了一个转换函数,在

tensorflow/python/framework/python_op_gen_internal.cc

op name 转换

void` `GenerateLowerCaseOpName(``const` `string& str, string* result) {`` ``const` `char` `joiner = ``'_'``;`` ``const` `int` `last_index = str.size() - 1;`` ``for` `(``int` `i = 0; i <= last_index; ++i) {``  ``const` `char` `c = str[i];``  ``// Emit a joiner only if a previous-lower-to-now-upper or a``  ``// now-upper-to-next-lower transition happens.``  ``if` `(``isupper``(c) && (i > 0)) {``   ``if` `(``islower``(str[i - 1]) || ((i < last_index) && ``islower``(str[i + 1]))) {    result->push_back(joiner);``   ``}``  ``}``  ``result->push_back(``tolower``(c));`` ``}``}

还是刚才的栗子,我们要找tf.nn.conv2d在cpp中的实现就是去找Conv2D

看过了源码解析 我们知道 所有的kernel实现都在tensorflow/core/kernels里面呐,grep一下。

tensorflow/core/kernels/conv_ops.h

template` `<``typename` `Device, ``typename` `T>``struct` `LaunchConv2DOp {`` ``void` `operator()(OpKernelContext* ctx, ``bool` `use_cudnn, ``bool` `cudnn_use_autotune,``         ``const` `Tensor& input, ``const` `Tensor& filter, ``int` `row_dilation,``         ``int` `col_dilation, ``int` `row_stride, ``int` `col_stride,``         ``const` `Padding& padding,``         ``const` `std::vector<int64>& explicit_paddings, Tensor* output,``         ``TensorFormat data_format);``};`` ` `#if GOOGLE_CUDA || TENSORFLOW_USE_ROCM``template` `<``typename` `T>``struct` `LaunchConv2DOp<Eigen::GpuDevice, T> {`` ``void` `operator()(OpKernelContext* ctx, ``bool` `use_cudnn, ``bool` `cudnn_use_autotune,``         ``const` `Tensor& input, ``const` `Tensor& filter, ``int` `row_dilation,``         ``int` `col_dilation, ``int` `row_stride, ``int` `col_stride,``         ``const` `Padding& padding,``         ``const` `std::vector<int64>& explicit_paddings, Tensor* output,``         ``TensorFormat data_format);``};``#endif // GOOGLE_CUDA || TENSORFLOW_USE_ROCM

4.1.3 参考资料

https://www.cnblogs.com/shouhuxianjian/p/9416934.html

python - looking for source code of from gen_nn_ops in tensorflow - Stack Overflow

4.2 op和kernel的对应

4.2.1 简介

这里,我们通过了解如何在TensorFlow框架中添加一个新的op,来理解op和kernel的对应关系。

为了方便,我就不另写一个kernel了,就用depthwise为例说明吧。美其名曰 更好的理解TensorFlow架构。

想要新建一个op,我们需要分以下几个步骤进行:

  1. 在一个 C++ 文件中注册新 op。op 的注册与实现是相互独立的。在其注册时描述了op该如何执行。例如,注册op时定义了op的名字,并指定了它的输入和输出.
  2. 使用 C++ 实现op。每一个实现称之为一个 "kernel",可以存在多个 kernel, 以适配不同的架构 (CPU, GPU 等)或不同的输入/输出类型。
  3. 创建一个 Python 包装器(wrapper)。这个包装器是创建op的公开 API。当注册op时, 会自动生成一个默认 默认的包装器.。既可以直接使用默认包装器,也可以添加一个新的包装器。
  4. 写一个函数计算op的梯度。(非必须)
  5. 写一个函数,描述op的输入和输出 shape。 该函数能够允许从op推断 shape。(非必须)
  6. 测试op,通常使用 Pyhton。

4.2.2 定义op的接口

向 TensorFlow注册将要定义op的接口。在注册时,指定op的名称,它的输入(类型和名称) 和输出(类型和名称),和所需的属性说明。

我们来看一下DepthwiseConv2dNativeBackpropFilter是怎么定义的呢?

在文件 tensorflow/core/ops/nn_ops.cc里面,使用 REGISTER_OP 宏来定义op的接口。

REGISTER_OP

REGISTER_OP(``"DepthwiseConv2dNativeBackpropFilter"``)``  ``.Input(``"input: T"``)``  ``.Input(``"filter_sizes: int32"``)``  ``.Input(``"out_backprop: T"``)``  ``.Output(``"output: T"``)``  ``.Attr(``"T: {half, bfloat16, float, double}"``)``  ``.Attr(``"strides: list(int)"``)``  ``.Attr(GetPaddingAttrString())``  ``.Attr(GetConvnetDataFormatAttrString())``  ``.Attr(``"dilations: list(int) = [1, 1, 1, 1]"``)``  ``.SetShapeFn([](InferenceContext* c) {``   ``ShapeHandle s;``   ``TF_RETURN_IF_ERROR(c->MakeShapeFromShapeTensor(1, &s));   TF_RETURN_IF_ERROR(c->WithRank(s, 4, &s));``   ``c->set_output(0, s);``   ``return` `Status::OK();``  ``});

如果我们想要自己创建一个op,可以在tensorflow/core/user_ops路径下创建文件。

op 的名称必须是为唯一的,并使用驼峰命名法。

4.2.3 op和kernel的对应

所谓的op和kernel的对应,也就是,定义op的kernel实现。

在定义接口之后,提供一个或多个op的实现,也就是会有一个或多个kernel对应着一个op。

为每一个kernel 创建一个对应的类,继承 OpKernel,覆盖 Compute 方法。Compute 方法提供一个类型为 OpKernelContext* 的参数 context,用于访问一些有用的信息,例如输入和输出的 tensor。

来看一下OpKernel长啥样。

OP的kernel实现

展开源码

实现 kernel 后,需要把它注册到 TensorFlow。

注册时,可以指定该 kernel 运行时的约束 条件。例如可以指定一个 kernel 在 CPU 上运行,另一个在 GPU 上运行。

使用 REGISTER_KERNEL_BUILDER 来把方法名和类进行绑定。

REGISTER_KERNEL_BUILDER 展开源码

在这里我们发现 REGISTER_KERNEL_BUILDER(Name("DepthwiseConv2dNativeBackpropFilter") 同一个Name 对应了两个kernel实现,区别在于一个.Label("cudnn_grouped_convolution"),

可以使用构建图中的_kernel_label_map来指定计算的kernel 太优秀了叭

tf.get_default_graph()._kernel_label_map({``"DepthwiseConv2dNativeBackpropFilter"``:``"cudnn_grouped_convolution"``})

4.2.4 生成客户端包装器

这里,需要了解一点点tensorlfow的编译芝士,查看第一章吧~

然后看一下,tensorflow/core/kernels/BUILD文件里面关于depthwise的编译描述。

python op wrapper

当编译 TensorFlow 时, 所有定义过的op会自动在tensorflow_core/python/ops/gen_nn_ops.py文件中生成 Python op包装器。

分别通过:

from tensorflow.python.ops.gen_nn_ops ``import` `*``from tensorflow.python.ops.nn_ops ``import` `*

把那些op导入到 tensorflow/python/ops/nn_ops.py 和 tensorflow/python/ops/nn.py 两个文件中。

这样我们就可以使用 以下语句调用这个kernel计算啦。

nn 函数调用

tf.nn.depthwise_conv2d_native_backprop_filter

这样,就为我们 在抓取了kernel hcc层的name 之后 找到对应的python op 打下基础啦。

另外呢,如果是自己定义的op的话,也很好wrap。

当编译 TensorFlow 时, 所有放在tensorflow/core/user_ops目录下的op会自动在bazel-genfiles/tensorflow/python/ops/gen_user_ops.py 文件中生成 Python op包装器。

通过以下声明,把那些op导入到tensorflow_core/python/user_ops/user_ops.py

gen user op

from` `tensorflow.python.ops.gen_user_ops ``import` `*

4.2.5 检查 Op 能否正常工作

4.2.6 验证条件

我们在注册op name的时候,有限制op计算时的参数类型。

那么,我们来看一下,TensorFlow是怎么进行判断的呢?

举个栗子:

template <typename Device, ``class` `T>``class` `DepthwiseConv2dNativeBackpropFilterOp : ``public` `OpKernel {`` ``public``:`` ``explicit DepthwiseConv2dNativeBackpropFilterOp(OpKernelConstruction* context)``   ``: OpKernel(context) {``  ``OP_REQUIRES_OK(context, context->GetAttr(``"strides"``, &strides_));``  ``OP_REQUIRES(context, strides_.size() == ``4``,``        ``errors::InvalidArgument(``"Sliding window strides field must "``                    ``"specify 4 dimensions"``));``......`` ``void` `Compute(OpKernelContext* context) override {``  ``const` `Tensor& input = context->input(``0``);``  ``const` `Tensor& filter_sizes = context->input(``1``);``  ``OP_REQUIRES(``    ``context, TensorShapeUtils::IsVector(filter_sizes.shape()),``    ``errors::InvalidArgument(``      ``"Conv2DBackpropFilter: filter_sizes input must be 1-dim, not "``,``      ``filter_sizes.dims()));

Compute函数里面,OP_REQUIRES 断言的输入是否是一个 vector, 如果不是 vector, 将返回InvalidArgument 状态。

OP_REQUIRES 宏包含三个参数

  1. context: 可以是一个 OpKernelContext 或 OpKernelConstruction 指针 (参见 tensorflow/core/framework/op_kernel.h),其 SetStatus() 方法将被使用到。
  2. 判断条件: tensorflow/core/public/tensor_shape.h 中有一些验证 tensor shape 的函数.
  3. 条件不满足时产生的错误: 错误用一个 Status 对象表示, 参见 tensorflow/core/public/status.h.Status 包含一个类型 (通常是 InvalidArgument, 但也可以是任何类型) 和一个消息. 构造 一个错误的函数位于 tensorflow/core/lib/core/errors.h 中.

OP_REQUIRES_OK

测试一个函数返回的 Status 对象是否是一个错误

4.2.7 参考资料

http:``//www.tensorfly.cn/tfdoc/how_tos/adding_an_op.html
  • 23
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

技术瘾君子1573

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值