暂时并未解决我的问题,但感觉将来会有用,特此转载 。

在使用 tensorflow 的时候,有时不可避免的会需要自定义 op,官方文档对于 定义 op 的前向过程介绍挺详细,但是对于 梯度 的介绍有点随意。 本文主要介绍在 python 端,和在 c++ 端对 op 的梯度进行定义。

1.使用python定义op的梯度

第一个例子:

from tensorflow.python.framework import ops
from tensorflow.python.ops import array_ops
from tensorflow.python.ops import sparse_ops@ops.RegisterGradient("ZeroOut")
def _zero_out_grad(op, grad):"""The gradients for `zero_out`.Args:op: The `zero_out` `Operation` that we are differentiating, which we can useto find the inputs and outputs of the original op.grad: Gradient with respect to the output of the `zero_out` op.Returns:Gradients with respect to the input of `zero_out`."""to_zero = op.inputs[0]shape = array_ops.shape(to_zero)index = array_ops.zeros_like(shape)first_grad = array_ops.reshape(grad, [-1])[0]to_zero_grad = sparse_ops.sparse_to_dense([index], shape, first_grad, 0)return [to_zero_grad]  # List of one Tensor, since we have one input

这个是官网的示例,从这个例子中,我们可以学到,在 python 中定义 op 的梯度的时候:

  • 需要一个 装饰器, @ops.RegisterGradient("OpName")
  • 梯度函数的签名是 def _computer_gradient(op, grad),第一个用来接收 要计算梯度的 op,第二个用来接收 上一层传回来的梯度。
  • 梯度的计算,依旧是 op 的组合。

尚不清楚的是:

  • 如果 op 有多个输出的话,grad应该怎么处理?
  • 梯度计算函数中的 操作 依旧是 tensorflow 已有的操作,如果 tensorflow 没有想要的操作,应该怎么办?

第二个例子:用 roi 层的实现来作为例子

@ops.RegisterGradient("RoiPool")
def _roi_pool_grad(op, grad, _):"""The gradients for `roi_pool`.Args:op: The `roi_pool` `Operation` that we are differentiating, which we can useto find the inputs and outputs of the original op.grad: Gradient with respect to the output of the `roi_pool` op.Returns:Gradients with respect to the input of `RoiPool`."""data = op.inputs[0]rois = op.inputs[1]argmax = op.outputs[1]pooled_height = op.get_attr('pooled_height')pooled_width = op.get_attr('pooled_width')spatial_scale = op.get_attr('spatial_scale')# compute gradientdata_grad = roi_pooling_op.roi_pool_grad(data, rois, argmax, grad, pooled_height, pooled_width, spatial_scale)return [data_grad, None]  # roi有两个输入,但是第二个输入不需要 梯度

这是 roi 层 定义 梯度的代码 roi 层有两个输入,两个输出。从这里我们可以学到:

  • 如果想获取 op 的属性,使用 op.get_attr("attr_name")
  • op.inputs[i] 可以获取 op 的 第 i 个输入。
  • op.outputs[j],可以获取 op 的第 i 个输出。
  • roi 梯度的核心还是 roi_pool_grad 计算的,这个 op 不是 tensorflow本身自带的,而是后期注册的。这个告诉我们,如果没有合适的 op 帮助我们计算 梯度,我们可以 注册一个 op ,用这个op 计算 梯度。

关于多个输出的 op

tensorflow 中到底有没有多输出的 op , 这个不太清楚,但是我根据官网的 zero_out 代码写了一個鬼畜的多输出代码,没有任何实用价值,仅供娱乐

#include "tensorflow/core/framework/op.h"
#include "tensorflow/core/framework/shape_inference.h"
#include "tensorflow/core/framework/op_kernel.h"#include <cfloat>#include "third_party/eigen3/unsupported/Eigen/CXX11/Tensor"
#include "tensorflow/core/framework/tensor_shape.h"
#include <iostream>
using namespace std;
using namespace tensorflow;REGISTER_OP("ZeroOut").Input("to_zero: int32").Output("zeroed: int32").Output("indice: int32");class ZeroOutOp : public OpKernel {public:explicit ZeroOutOp(OpKernelConstruction* context) : OpKernel(context) {cout<<"hello, there"<<endl;}void Compute(OpKernelContext* context) override {// Grab the input tensorconst Tensor& input_tensor = context->input(0);auto input = input_tensor.flat<int32>();// Create an output tensorTensor* output_tensor = NULL;Tensor* output_tensor_indice = NULL;TensorShape indice_shape;int dims[] = {1};TensorShapeUtils::MakeShape(dims, 1, &indice_shape);OP_REQUIRES_OK(context, context->allocate_output(0, input_tensor.shape(),&output_tensor));OP_REQUIRES_OK(context, context->allocate_output(1, indice_shape,&output_tensor_indice));auto output_flat = output_tensor->flat<int32>();auto indice_flat = output_tensor_indice->flat<int32>();indice_flat(0) = 3;// Set all but the first element of the output tensor to 0.const int N = input.size();for (int i = 1; i < N; i++) {output_flat(i) = 0;}// Preserve the first input value if possible.if (N > 0) output_flat(0) = input(0);}
};REGISTER_KERNEL_BUILDER(Name("ZeroOut").Device(DEVICE_CPU), ZeroOutOp);// 编译命令
// TF_INC=$(python -c 'import tensorflow as tf; print(tf.sysconfig.get_include())')
//  g++ -std=c++11 -shared zero_out.cc -o zero_out.so -fPIC -I $TF_INC -O2 -D_GLIBCXX_USE_CXX11_ABI=0

通过这个例子,学到了:

  • 对于多输出的 oprun 时候返回的不是 ndarray ,而是 一个对象 <class '598f596b9f78ee7154fbfc866bcdc81d.ZeroOut'>,我们可以通过下标索引来获取 op 的 第一,第二个 outputs

多输出梯度的定义

当 ZeroOut 有两个输出的时候,下面这个代码会报错,TypeError: _zero_out_grad() takes 2 positional arguments but 3 were given, 这就说明,如果 op 有 N 个输出的话,那么gradient 函数就应该有 N+1 个参数,分别代表,op 和 各个输出的梯度。

# wrong!!
@ops.RegisterGradient("ZeroOut")
def _zero_out_grad(op, grad):to_zero = op.inputs[0]shape = array_ops.shape(to_zero)index = array_ops.zeros_like(shape)first_grad = array_ops.reshape(grad, [-1])[0]to_zero_grad = sparse_ops.sparse_to_dense([index], shape, first_grad, 0)return [to_zero_grad]# right!!
@ops.RegisterGradient("ZeroOut")
def _zero_out_grad(op, grad, _):to_zero = op.inputs[0]shape = array_ops.shape(to_zero)index = array_ops.zeros_like(shape)first_grad = array_ops.reshape(grad, [-1])[0]to_zero_grad = sparse_ops.sparse_to_dense([index], shape, first_grad, 0)return [to_zero_grad]

2. C++端定义op的梯度

c++ 定义 op 的梯度主要使用的 REGISTER_OP_GRADIENT 宏 和 FDH (Function Define Helper)

先从最简单的看起 ReluGrad 源码地址

#include "tensorflow/core/framework/function.h"
#include "tensorflow/core/lib/core/errors.h"
#include "tensorflow/core/util/padding.h"
#include "tensorflow/core/util/tensor_format.h"namespace tensorflow {typedef FunctionDefHelper FDH;
Status ReluGrad(const AttrSlice& attrs, FunctionDef* g) {// clang-format off*g = FDH::Define(// Arg defs,定义参数,前向过程的输入 和 输出的梯度{"x: T", "dy: T"},// Ret val defs,梯度返回值定义{"dx: T"},// Attr defs,属性定义,{{"T: {float, double}"}},// Nodes,节点定义:用已有的 op 计算梯度,下面的定义过程就是梯度的计算过程。{{{"dx"}, "ReluGrad", {"dy", "x"}, {{"T", "$T"}}}});// clang-format onreturn Status::OK();
}
REGISTER_OP_GRADIENT("Relu", ReluGrad);} 
  • 1

一些核心 op 的 REGISTER 部分 github

参考资料

https://zhuanlan.zhihu.com/p/25929909

tensorflow自定义op:梯度相关推荐

  1. tensorflow自定义op和梯度

    参考资料 官网教程链接 http://www.tensorfly.cn/tfdoc/how_tos/adding_an_op.html#AUTOGENERATED-implement-the-grad ...

  2. tensorflow:自定义op

    比官网介绍的更好理解,特此转载 tensorflow:自定义op简单介绍 2017年06月26日 13:32:55 阅读数:6094 tensorflow 自定义 op 本文只是简单的翻译了 http ...

  3. tensorflow:自定义op简单介绍

    本文只是简单的翻译了 https://www.tensorflow.org/extend/adding_an_op 的简单部分,高级部分请移步官网. 可能需要新定义 c++ operation 的几种 ...

  4. TensorFlow使用Python自定义op和损失函数

    TensorFlow使用Python自定义op和损失函数 TensorFlow是静态图结构,即必须把所有的操作以及网络结构定义好(后来有了动态图功能,即Eager Execution ),在没有用tf ...

  5. 【Tensorflow】Tensorflow 自定义梯度

    目录 前言 自定义梯度 说明 gradient_override_map的使用 多输入与多输出op 利用stop_gradient 参考 [fishing-pan:https://blog.csdn. ...

  6. Ubuntu tensorflow自定义GPU版本op节点

    参考:https://blog.csdn.net/qq_27637315/article/details/79114633 windows增加op节点: https://github.com/tens ...

  7. Tensorflow利用函数修饰符@tf.custom_gradients自定义函数梯度

    Tensorflow学习笔记(1) 利用函数修饰符@tf.custom_gradients自定义函数梯度_寂乐居士的博客-CSDN博客_tf.custom_gradient python中的修饰符以及 ...

  8. TensorFlow实现自定义Op

    『写在前面』 以CTC Beam search decoder为例,简单整理一下TensorFlow实现自定义Op的操作流程. 基本的流程 1. 定义Op接口 #include "tenso ...

  9. Pytorch1.1.0 入门 自定义op(python)

    因为需求,需要调研tensorRT与ONNX关于自定义层的方法.经过之前的调研,首先,关于onnx,开发者手册中的介绍有限,在已知的demo中没有关于onnx自定义层的,详情见TensorRT 5.1 ...

最新文章

  1. ***小程序wx.getUserInfo不能弹出授权窗口后的解决方案
  2. 验证url 地址是否是图片
  3. 【机器学习】Apriori 算法进行关联分析和FP-growth算法
  4. Python入门100题 | 第068题
  5. Linux下的iwpriv(iwlist、iwconfig)的简单应用
  6. python3将列表当作队列使用
  7. 第九章 限制性图谱和正则表达式
  8. Java读取.mdb文件
  9. AngularJS - uib-datepicker-popup - 日期控件
  10. 查看dmp文件oracle版本,Oracle的DMP文件修改版本号
  11. ORACLE和MYSQL一些函数和实现效果的对比、替换
  12. Zabbix-Sender 增加自定义监控项-- Ping 到目的地链路监控--bat脚本循环运行
  13. 为何现在的90后员工的离职率越来越高了?
  14. SuperMap 产品安全白皮书
  15. 用计算机弹一笑倾城简谱,一笑倾城歌词,一笑倾城歌词简谱
  16. artemis mq配置开机启动 (centos7)(artemis Init Script)
  17. Python制作发票扫描系统
  18. Java变态题目(持续更新)
  19. MATLAB多径衰落信道仿真程序,基于Matlab的移动通信中多径衰落信道的仿真
  20. Chapter 19 稳恒磁场

热门文章

  1. 2.SpringBoot整合Mybatis(一对一)
  2. Beyond Compare v3.3.13 中文版
  3. C语言程序设计-p163例7-9
  4. 面对封号潮,跨境收款如何做到不受牵连?
  5. 汉语言文学专业c学校,理科生能报汉语言文学专业吗?哪些学校找理科生
  6. 学会这道题,解决位运算,布莱恩·克尼根算法!
  7. python:实现布赖恩·克尼汉法算法(附完整源码)
  8. HUAWEI P20系列国内发布 徕卡三摄+AI开启智慧摄影新时代
  9. vue H5app plus调取手机相册,限制图片大小,图片转base64
  10. MyBatis-Plus 代码生成器最新版配置方案