tensorflow自定义op:梯度
暂时并未解决我的问题,但感觉将来会有用,特此转载 。
在使用 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
通过这个例子,学到了:
- 对于多输出的
op
,run
时候返回的不是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:梯度相关推荐
- tensorflow自定义op和梯度
参考资料 官网教程链接 http://www.tensorfly.cn/tfdoc/how_tos/adding_an_op.html#AUTOGENERATED-implement-the-grad ...
- tensorflow:自定义op
比官网介绍的更好理解,特此转载 tensorflow:自定义op简单介绍 2017年06月26日 13:32:55 阅读数:6094 tensorflow 自定义 op 本文只是简单的翻译了 http ...
- tensorflow:自定义op简单介绍
本文只是简单的翻译了 https://www.tensorflow.org/extend/adding_an_op 的简单部分,高级部分请移步官网. 可能需要新定义 c++ operation 的几种 ...
- TensorFlow使用Python自定义op和损失函数
TensorFlow使用Python自定义op和损失函数 TensorFlow是静态图结构,即必须把所有的操作以及网络结构定义好(后来有了动态图功能,即Eager Execution ),在没有用tf ...
- 【Tensorflow】Tensorflow 自定义梯度
目录 前言 自定义梯度 说明 gradient_override_map的使用 多输入与多输出op 利用stop_gradient 参考 [fishing-pan:https://blog.csdn. ...
- Ubuntu tensorflow自定义GPU版本op节点
参考:https://blog.csdn.net/qq_27637315/article/details/79114633 windows增加op节点: https://github.com/tens ...
- Tensorflow利用函数修饰符@tf.custom_gradients自定义函数梯度
Tensorflow学习笔记(1) 利用函数修饰符@tf.custom_gradients自定义函数梯度_寂乐居士的博客-CSDN博客_tf.custom_gradient python中的修饰符以及 ...
- TensorFlow实现自定义Op
『写在前面』 以CTC Beam search decoder为例,简单整理一下TensorFlow实现自定义Op的操作流程. 基本的流程 1. 定义Op接口 #include "tenso ...
- Pytorch1.1.0 入门 自定义op(python)
因为需求,需要调研tensorRT与ONNX关于自定义层的方法.经过之前的调研,首先,关于onnx,开发者手册中的介绍有限,在已知的demo中没有关于onnx自定义层的,详情见TensorRT 5.1 ...
最新文章
- ***小程序wx.getUserInfo不能弹出授权窗口后的解决方案
- 验证url 地址是否是图片
- 【机器学习】Apriori 算法进行关联分析和FP-growth算法
- Python入门100题 | 第068题
- Linux下的iwpriv(iwlist、iwconfig)的简单应用
- python3将列表当作队列使用
- 第九章 限制性图谱和正则表达式
- Java读取.mdb文件
- AngularJS - uib-datepicker-popup - 日期控件
- 查看dmp文件oracle版本,Oracle的DMP文件修改版本号
- ORACLE和MYSQL一些函数和实现效果的对比、替换
- Zabbix-Sender 增加自定义监控项-- Ping 到目的地链路监控--bat脚本循环运行
- 为何现在的90后员工的离职率越来越高了?
- SuperMap 产品安全白皮书
- 用计算机弹一笑倾城简谱,一笑倾城歌词,一笑倾城歌词简谱
- artemis mq配置开机启动 (centos7)(artemis Init Script)
- Python制作发票扫描系统
- Java变态题目(持续更新)
- MATLAB多径衰落信道仿真程序,基于Matlab的移动通信中多径衰落信道的仿真
- Chapter 19 稳恒磁场
热门文章
- 2.SpringBoot整合Mybatis(一对一)
- Beyond Compare v3.3.13 中文版
- C语言程序设计-p163例7-9
- 面对封号潮,跨境收款如何做到不受牵连?
- 汉语言文学专业c学校,理科生能报汉语言文学专业吗?哪些学校找理科生
- 学会这道题,解决位运算,布莱恩·克尼根算法!
- python:实现布赖恩·克尼汉法算法(附完整源码)
- HUAWEI P20系列国内发布 徕卡三摄+AI开启智慧摄影新时代
- vue H5app plus调取手机相册,限制图片大小,图片转base64
- MyBatis-Plus 代码生成器最新版配置方案