3030-------------- | :----------------------
3131OpProtoMake定义 | ` .cc ` 文件,Backward Op不需要定义OpProtoMake
3232Op定义 | ` .cc ` 文件
33- Kernel实现 | CPU、GPU共享Kernel实现在 ` .h ` 文件中,否则,CPU 实现在` .cc ` 文件中,GPU 实现在` .cu ` 文件中。
34- 注册Op | Op注册实现在` .cc ` 文件;Kernel注册CPU实现在` .cc ` 文件中,GPU实现在 ` .cu ` 文件中
33+ Kernel实现 | CPU、CUDA共享Kernel实现在 ` .h ` 文件中,否则,CPU 实现在` .cc ` 文件中,CUDA 实现在` .cu ` 文件中。
34+ 注册Op | Op注册实现在` .cc ` 文件;Kernel注册CPU实现在` .cc ` 文件中,CUDA实现在 ` .cu ` 文件中
3535
3636
3737实现新的op都添加至目录[ paddle/operators] ( https://github.com/PaddlePaddle/Paddle/tree/develop/paddle/operators ) 下,文件命名以` *_op.h ` (如有) 、 ` *_op.cc ` 、` *_op.cu ` (如有)结尾。** 系统会根据文件名自动构建op和其对应的Python扩展。**
@@ -153,7 +153,7 @@ MulOp(const std::string &type, const framework::VariableNameMap &inputs,
153153
154154`MulKernel`继承自`framework::OpKernel`,带有下面两个模板参数:
155155
156- - `typename Place `: 表示设备类型,不同设备(CPU、GPU )共享同一个Kernel时,需加该模板参数,不共享则不加,一个不共享的例子是[`OnehotCrossEntropyOpKernel`](https://github.com/PaddlePaddle/Paddle/blob/develop/paddle/operators/cross_entropy_op.h#L43)。
156+ - `typename DeviceContext `: 表示设备类型,不同设备(CPU、CUDA )共享同一个Kernel时,需加该模板参数,不共享则不加,一个不共享的例子是[`OnehotCrossEntropyOpKernel`](https://github.com/PaddlePaddle/Paddle/blob/develop/paddle/operators/cross_entropy_op.h#L43)。
157157
158158- `typename T` : 表示数据类型,如`float`, `double`等。
159159
@@ -165,26 +165,24 @@ MulOp(const std::string &type, const framework::VariableNameMap &inputs,
165165下面是 `MulKernel` `Compute`的实现:
166166
167167 ```cpp
168- template <typename Place , typename T>
168+ template <typename DeviceContext , typename T>
169169 class MulKernel : public framework::OpKernel {
170170 public:
171171 void Compute(const framework::ExecutionContext& context) const override {
172172 auto* X = context.Input<Tensor>("X");
173173 auto* Y = context.Input<Tensor>("Y");
174174 auto* Z = context.Output<Tensor>("Out");
175175 Z->mutable_data<T>(context.GetPlace());
176- auto* device_context =
177- const_cast<platform::DeviceContext*>(context.device_context_);
178- math::matmul<Place, T>(*X, false, *Y, false, 1, Z, 0, device_context);
176+ auto& device_context = context.template device_context<DeviceContext>();
177+ math::matmul<DeviceContext, T>(*X, false, *Y, false, 1, Z, 0, device_context);
179178 }
180179 };
181- ```
182180
183- 需要注意:** 不同设备(CPU、GPU )共享一个Op定义,是否则共享同一个` OpKernel ` ,取决于` Compute ` 调用的函数是否支持不同设备。**
181+ 需要注意:**不同设备(CPU、CUDA )共享一个Op定义,是否则共享同一个`OpKernel`,取决于`Compute`调用的函数是否支持不同设备。**
184182
185- ` MulOp ` 的CPU、GPU实现共享同一个 ` Kernel ` 。` OpKernel ` 不共享的例子可以参考:[ ` OnehotCrossEntropyOpKernel ` ] ( https://github.com/PaddlePaddle/Paddle/blob/develop/paddle/operators/cross_entropy_op.h#L43 ) 。
183+ `MulOp`的CPU、CUDA实现共享同一个 `Kernel`。`OpKernel`不共享的例子可以参考:[`OnehotCrossEntropyOpKernel`](https://github.com/PaddlePaddle/Paddle/blob/develop/paddle/operators/cross_entropy_op.h#L43)。
186184
187- 为了使` OpKernel ` 的计算过程书写更加简单,并且CPU、GPU的代码可以复用 ,我们通常借助 Eigen unsupported Tensor模块来实现` Compute ` 接口。关于在PaddlePaddle中如何使用Eigen库,请参考[ 使用文档] ( https://github.com/PaddlePaddle/Paddle/blob/develop/doc/howto/dev/use_eigen_cn.md ) 。
185+ 为了使`OpKernel`的计算过程书写更加简单,并且CPU、CUDA的代码可以复用 ,我们通常借助 Eigen unsupported Tensor模块来实现`Compute`接口。关于在PaddlePaddle中如何使用Eigen库,请参考[使用文档](https://github.com/PaddlePaddle/Paddle/blob/develop/doc/howto/dev/use_eigen_cn.md)。
188186
189187
190188到此,前向Op实现完成。接下来,需要在`.cc`文件中注册该op和kernel。
@@ -197,9 +195,9 @@ MulOp(const std::string &type, const framework::VariableNameMap &inputs,
197195 ```cpp
198196 namespace ops = paddle::operators;
199197 REGISTER_OP(mul, ops::MulOp, ops::MulOpMaker, mul_grad, ops::MulOpGrad);
200- REGISTER_OP_CPU_KERNEL(mul, ops::MulKernel<paddle::platform::CPUPlace , float>);
198+ REGISTER_OP_CPU_KERNEL(mul, ops::MulKernel<paddle::platform::CPUDeviceContext , float>);
201199 REGISTER_OP_CPU_KERNEL(mul_grad,
202- ops::MulGradKernel<paddle::platform::CPUPlace , float>);
200+ ops::MulGradKernel<paddle::platform::CPUDeviceContext , float>);
203201 ```
204202
205203 在上面的代码中:
@@ -209,17 +207,17 @@ MulOp(const std::string &type, const framework::VariableNameMap &inputs,
209207 - `REGISTER_OP_CPU_KERNEL` :注册`ops::MulKernel`类,并特化模板参数为`paddle::platform::CPUPlace`和`float`类型,同理,注册`ops::MulGradKernel`类。
210208
211209
212- - 在 ` .cu ` 文件中注册GPU Kernel。
213- - 请注意,如果GPU Kernel的实现基于Eigen unsupported模块,那么在 ` .cu ` 的开始请加上宏定义 ` #define EIGEN_USE_GPU ` ,代码示例如下:
210+ - 在 `.cu`文件中注册CUDA Kernel。
211+ - 请注意,如果CUDA Kernel的实现基于Eigen unsupported模块,那么在 `.cu`的开始请加上宏定义 `#define EIGEN_USE_GPU`,代码示例如下:
214212
215213 ```cpp
216214 // if use Eigen unsupported module before include head files
217- // #define EIGEN_USE_GPU
215+ #define EIGEN_USE_GPU
218216
219217 namespace ops = paddle::operators;
220- REGISTER_OP_GPU_KERNEL (mul, ops::MulKernel<paddle::platform::GPUPlace , float>);
221- REGISTER_OP_GPU_KERNEL (mul_grad,
222- ops::MulGradKernel<paddle::platform::GPUPlace , float>);
218+ REGISTER_OP_CUDA_KERNEL (mul, ops::MulKernel<paddle::platform::CUDADeviceContext , float>);
219+ REGISTER_OP_CUDA_KERNEL (mul_grad,
220+ ops::MulGradKernel<paddle::platform::CUDADeviceContext , float>);
223221 ```
224222
225223### 5. 编译
@@ -236,71 +234,55 @@ make mul_op
236234
237235## 实现单元测试
238236
239- 单测包括对比前向Op不同设备(CPU、GPU )的实现、对比反向OP不同设备(CPU、GPU )的实现、反向Op的梯度测试。下面介绍介绍[ ` MulOp ` 的单元测试] ( https://github.com/PaddlePaddle/Paddle/blob/develop/python/paddle/v2/framework/tests/test_mul_op.py ) 。
237+ 单测包括对比前向Op不同设备(CPU、CUDA )的实现、对比反向OP不同设备(CPU、CUDA )的实现、反向Op的梯度测试。下面介绍介绍[`MulOp`的单元测试](https://github.com/PaddlePaddle/Paddle/blob/develop/python/paddle/v2/framework/tests/test_mul_op.py)。
240238
241- ### 前向Operator单元测试
242239
243- 前向Op单元测试继承自 ` unittest.TestCase ` ,并定义元类 ` __metaclass__ = OpTestMeta ` 。各项更加具体的单元测试在` OpTestMeta ` 里完成。测试前向Operator ,需要:
240+ Op单元测试继承自`OpTest` 。各项更加具体的单元测试在`TestMulOp `里完成。测试Operator ,需要:
244241
2452421. 在`setUp`函数定义输入、输出,以及相关的属性参数。
2462432. 生成随机的输入数据。
2472443. 在Python脚本中实现与前向operator相同的计算逻辑,得到输出值,与operator前向计算的输出进行对比。
245+ 4. 反向计算已经自动集成进测试框架,直接调用相应接口即可。
248246
249247
250248 ```python
251249 import unittest
252250 import numpy as np
253- from gradient_checker import GradientChecker, create_op
254- from op_test_util import OpTestMeta
251+ from op_test import OpTest
255252
256- class TestMulOp (unittest .TestCase ):
257- __metaclass__ = OpTestMeta
258253
254+ class TestMulOp(OpTest):
259255 def setUp(self):
260- self .type = " mul"
256+ self.op_type = "mul"
261257 self.inputs = {
262258 'X': np.random.random((32, 84)).astype("float32"),
263259 'Y': np.random.random((84, 100)).astype("float32")
264260 }
265261 self.outputs = {'Out': np.dot(self.inputs['X'], self.inputs['Y'])}
266- ```
267262
268- 上面的代码首先导入依赖的包,下面是对` setUp ` 函数中操作的重要变量的详细解释:
269-
270- - ` self.type = "mul" ` : 定义类型,与operator注册时注册的类型一致。
271- - ` self.inputs ` : 定义输入,类型为` numpy.array ` ,并初始化。
272- - ` self.outputs ` : 定义输出,并在Python脚本中完成与operator同样的计算逻辑,返回Python端的计算结果。
273-
274-
275- ### 反向Operator单元测试
263+ def test_check_output(self):
264+ self.check_output()
276265
277- 反向Op单元测试继承自` GradientChecker ` ,而` GradientChecker ` 继承自` unittest.TestCase ` ,因此,** 反向单元测试函数需要以` test_ ` 开头** 。
266+ def test_check_grad_normal(self):
267+ self.check_grad(['X', 'Y'], 'Out', max_relative_error=0.5)
278268
279- ``` python
280- class TestMulGradOp (GradientChecker ):
281- def setUp (self ):
282- self .op = create_op(" mul" )
283- self .inputs = {
284- ' X' : np.random.random((32 , 84 )).astype(" float32" ),
285- ' Y' : np.random.random((84 , 100 )).astype(" float32" )
286- }
269+ def test_check_grad_ingore_x(self):
270+ self.check_grad(
271+ ['Y'], 'Out', max_relative_error=0.5, no_grad_set=set("X"))
287272
288- def test_check_grad_normal (self ):
289- # mul op will enlarge the relative error
290- self .check_grad( [' X' , ' Y ' ], ' Out' , max_relative_error = 0.5 )
273+ def test_check_grad_ingore_y (self):
274+ self.check_grad(
275+ ['X'], 'Out', max_relative_error=0.5, no_grad_set=set('Y') )
291276
292- def test_check_grad_ingore_x (self ):
293- self .check_grad(
294- [' Y' ], ' Out' , max_relative_error = 0.5 , no_grad_set = set (" X" ))
277+ ```
295278
296- def test_check_grad_ingore_y (self ):
297- self .check_grad(
298- [' X' ], ' Out' , max_relative_error = 0.5 , no_grad_set = set (' Y' ))
299- ```
279+ 上面的代码首先导入依赖的包,下面是对`setUp`函数中操作的重要变量的详细解释:
300280
301- 下面解释代码中一些关键的地方:
281+ - `self.op_type = "mul" ` : 定义类型,与operator注册时注册的类型一致。
282+ - `self.inputs` : 定义输入,类型为`numpy.array`,并初始化。
283+ - `self.outputs` : 定义输出,并在Python脚本中完成与operator同样的计算逻辑,返回Python端的计算结果。
302284
303- - 调用 ` create_op("mul") ` 创建反向Op对应的前向Op。
285+ 而反向测试中:
304286- `test_check_grad_normal`中调用`check_grad`使用数值法检测梯度正确性和稳定性。
305287 - 第一个参数`["X", "Y"]` : 指定对输入变量`X`、`Y`做梯度检测。
306288 - 第二个参数`"Out"` : 指定前向网络最终的输出目标变量`Out`。
@@ -328,5 +310,5 @@ ctest -R test_mul_op
328310
329311- 为每个Op创建单独的` *_op.h ` (如有)、` *_op.cc ` 和` *_op.cu ` (如有)。不允许一个文件中包含多个Op,这将会导致编译出错。
330312- 注册Op时的类型名,需要和该Op的名字一样。即不允许在` A_op.cc ` 里面,注册` REGISTER_OP(B, ...) ` 等,这将会导致单元测试出错。
331- - 如果Op没有实现GPU Kernel,请不要创建空的` *_op.cu ` ,这将会导致单元测试出错。
313+ - 如果Op没有实现CUDA Kernel,请不要创建空的` *_op.cu ` ,这将会导致单元测试出错。
332314- 如果多个Op依赖一些共用的函数,可以创建非` *_op.* ` 格式的文件来存放,如` gather.h ` 文件。
0 commit comments