1
- # Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserved.
1
+ # Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved.
2
2
#
3
3
# Licensed under the Apache License, Version 2.0 (the "License");
4
4
# you may not use this file except in compliance with the License.
15
15
from __future__ import print_function
16
16
17
17
import unittest
18
+ import paddle .fluid as fluid
19
+ import paddle .fluid .layers as layers
18
20
import numpy as np
19
- from op_test import OpTest
21
+ import six
20
22
import paddle .fluid .core as core
21
23
24
+ from paddle .fluid import ParamAttr
25
+ from paddle .fluid .framework import Program , grad_var_name
26
+ from paddle .fluid .executor import Executor
27
+ from paddle .fluid .backward import append_backward
22
28
23
- class TestArgsortOp (OpTest ):
24
- def setUp (self ):
25
- self .init_axis ()
26
- self .init_datatype ()
27
- self .init_direction ()
28
- x = np .random .random ((2 , 3 , 4 , 5 , 10 )).astype (self .dtype )
29
- self .attrs = {'axis' : self .axis , 'descending' : self .descending }
30
- if self .axis < 0 :
31
- self .axis = self .axis + len (x .shape )
29
+ np .random .seed (123 )
30
+
31
+
32
+ class PyArgsort (object ):
33
+ def __init__ (self , input_shape , axis , descending , dtype ):
34
+ self .x = np .random .random (input_shape ).astype (dtype )
35
+ self .label = np .random .random (input_shape ).astype (dtype )
36
+ if axis < 0 :
37
+ self .axis = axis + len (self .x .shape )
38
+ else :
39
+ self .axis = axis
40
+ self .descending = descending
41
+
42
+ def forward (self ):
32
43
if self .descending :
33
44
self .indices = np .flip (
34
45
np .argsort (
35
- x , kind = 'quicksort' , axis = self .axis ), self .axis )
36
- self .out = np .flip (
46
+ self . x , kind = 'quicksort' , axis = self .axis ), self .axis )
47
+ self .sorted_x = np .flip (
37
48
np .sort (
38
- x , kind = 'quicksort' , axis = self .axis ), self .axis )
49
+ self . x , kind = 'quicksort' , axis = self .axis ), self .axis )
39
50
else :
40
- self .indices = np .argsort (x , kind = 'quicksort' , axis = self .axis )
41
- self .out = np .sort (x , kind = 'quicksort' , axis = self .axis )
51
+ self .indices = np .argsort (self .x , kind = 'quicksort' , axis = self .axis )
52
+ self .sorted_x = np .sort (self .x , kind = 'quicksort' , axis = self .axis )
53
+ self .loss = self .sorted_x * self .label
54
+ self .loss = np .sum (self .loss )
55
+ out = (np .array (
56
+ self .indices , dtype = self .indices .dtype ), np .array (
57
+ self .sorted_x , dtype = self .sorted_x .dtype ), np .array (
58
+ [self .loss ], dtype = self .loss .dtype ))
59
+ return out
42
60
43
- self .op_type = "argsort"
44
- self .inputs = {'X' : x }
45
- self .outputs = {'Indices' : self .indices , 'Out' : self .out }
61
+
62
+ def create_tensor (np_data , place ):
63
+ tensor = core .LoDTensor ()
64
+ tensor .set (np_data , place )
65
+ return tensor
66
+
67
+
68
+ class TestArgsortOpCPU (unittest .TestCase ):
69
+ def setup_program (self ):
70
+ self .main_program = Program ()
71
+ self .startup_program = Program ()
72
+ self .init_place ()
73
+
74
+ def setUp (self ):
75
+ self .init_axis ()
76
+ self .init_datatype ()
77
+ self .init_direction ()
78
+ self .init_inputshape ()
79
+
80
+ self .setup_program ()
81
+ self .feed_data_field = {"x" , "label" }
82
+ self .grad_data_field = {"x" }
83
+
84
+ self .py_argsort = PyArgsort (self .input_shape , self .axis ,
85
+ self .descending , self .dtype )
86
+
87
+ with fluid .program_guard (self .main_program , self .startup_program ):
88
+ x = fluid .layers .data (
89
+ name = "x" , shape = self .input_shape , dtype = self .dtype )
90
+ x .stop_gradient = False
91
+ label = fluid .layers .data (
92
+ name = "label" , shape = self .input_shape , dtype = self .dtype )
93
+ self .sorted_x , self .index = fluid .layers .argsort (
94
+ input = x , axis = self .axis , descending = self .descending )
95
+ self .sorted_x .stop_gradient = False
96
+ loss = fluid .layers .elementwise_mul (self .sorted_x , label )
97
+ self .loss = fluid .layers .reduce_sum (loss )
98
+
99
+ def forward (self ):
100
+ self .feed_map = {
101
+ x : create_tensor (getattr (self .py_argsort , x ), self .place )
102
+ for x in self .feed_data_field
103
+ }
104
+ exe = Executor (self .place )
105
+ out = exe .run (self .main_program ,
106
+ feed = self .feed_map ,
107
+ fetch_list = [self .index , self .sorted_x , self .loss ])
108
+ return out
109
+
110
+ def backward (self ):
111
+ self .feed_map = {
112
+ x : create_tensor (getattr (self .py_argsort , x ), self .place )
113
+ for x in self .feed_data_field
114
+ }
115
+ fetch_list = [
116
+ self .main_program .global_block ().var (grad_var_name (x ))
117
+ for x in self .grad_data_field
118
+ ]
119
+ exe = Executor (self .place )
120
+ out = exe .run (self .main_program ,
121
+ feed = self .feed_map ,
122
+ fetch_list = fetch_list ,
123
+ return_numpy = False )
124
+ return out
125
+
126
+ def test_backward (self , numeric_grad_delta = 1e-5 , max_relative_error = 1e-7 ):
127
+ self .check_forward ()
128
+
129
+ with fluid .program_guard (self .main_program , self .startup_program ):
130
+ append_backward (self .loss )
131
+
132
+ ana_grad = [np .array (x ) for x in self .backward ()]
133
+
134
+ num_grad = self .get_numerical_gradient (delta = numeric_grad_delta )
135
+ self .assert_is_close (
136
+ num_grad ,
137
+ ana_grad ,
138
+ 'x' ,
139
+ max_relative_error = max_relative_error ,
140
+ msg_prefix = "Gradient Check On %s" % str (self .place ))
141
+
142
+ def check_forward (self ):
143
+ pd_outputs = self .forward ()
144
+ py_outputs = self .py_argsort .forward ()
145
+ for pd_output , py_output in zip (pd_outputs , py_outputs ):
146
+ self .assertEqual (pd_output .shape , py_output .shape )
147
+ self .assertTrue (
148
+ np .allclose (
149
+ pd_output , py_output , atol = 0 , equal_nan = False ))
150
+
151
+ def get_numerical_gradient (self , delta = 1e-7 ):
152
+ if self .dtype == 'float16' :
153
+ delta = np .array (delta ).astype (np .float16 )
154
+ feed_list = [getattr (self .py_argsort , x ) for x in self .grad_data_field ]
155
+ grad_list = [np .zeros_like (x ) for x in feed_list ]
156
+ for feed , grad in zip (feed_list , grad_list ):
157
+ for f , g in np .nditer ([feed , grad ], op_flags = ['readwrite' ]):
158
+ o = float (f )
159
+ f [...] = o + delta
160
+ y_pos = self .forward ()[2 ]
161
+
162
+ f [...] = o - delta
163
+ y_neg = self .forward ()[2 ]
164
+
165
+ f [...] = o
166
+ dout_dfeed = (y_pos - y_neg ) / (delta * 2 )
167
+ g [...] = dout_dfeed [0 ]
168
+
169
+ return grad_list
170
+
171
+ def assert_is_close (self , numeric_grads , analytic_grads , names ,
172
+ max_relative_error , msg_prefix ):
173
+ for a , b , name in six .moves .zip (numeric_grads , analytic_grads , names ):
174
+ abs_a = np .abs (a )
175
+ abs_a [abs_a < 1e-3 ] = 1
176
+
177
+ diff_mat = np .abs (a - b ) / abs_a
178
+ max_diff = np .max (diff_mat )
179
+
180
+ def err_msg ():
181
+ offset = np .argmax (diff_mat > max_relative_error )
182
+ return ("%s error, %s variable %s max gradient diff %f over limit %f, "
183
+ "the first error element is %d, expected %f, but got %f." ) \
184
+ % ('argsort' , msg_prefix , name , max_diff , max_relative_error ,
185
+ offset , a .flatten ()[offset ], b .flatten ()[offset ])
186
+
187
+ self .assertLessEqual (max_diff , max_relative_error , err_msg ())
46
188
47
189
def init_axis (self ):
48
190
self .axis = - 1
@@ -53,111 +195,127 @@ def init_datatype(self):
53
195
def init_direction (self ):
54
196
self .descending = False
55
197
56
- def test_check_output (self ):
57
- self .check_output ()
198
+ def init_inputshape (self ):
199
+ self .input_shape = (2 , 2 , 2 , 2 , 3 )
200
+
201
+ def init_place (self ):
202
+ self .place = core .CPUPlace ()
58
203
59
- def test_check_grad (self ):
60
- self .check_grad (['X' ], 'Out' )
61
204
205
+ class TestArgsortOpGPU (TestArgsortOpCPU ):
206
+ def init_place (self ):
207
+ if core .is_compiled_with_cuda ():
208
+ self .place = core .CUDAPlace (0 )
209
+ else :
210
+ self .place = core .CPUPlace ()
62
211
63
- class TestArgsortOpAxis0 (TestArgsortOp ):
212
+
213
+ class TestArgsortOpAxis0CPU (TestArgsortOpCPU ):
64
214
def init_axis (self ):
65
215
self .axis = 0
66
216
67
217
68
- class TestArgsortOpAxis1 ( TestArgsortOp ):
218
+ class TestArgsortOpAxis0GPU ( TestArgsortOpGPU ):
69
219
def init_axis (self ):
70
- self .axis = 1
220
+ self .axis = 0
71
221
72
222
73
- class TestArgsortOpAxis2 ( TestArgsortOp ):
223
+ class TestArgsortOpAxis1CPU ( TestArgsortOpCPU ):
74
224
def init_axis (self ):
75
- self .axis = 2
225
+ self .axis = 1
76
226
77
227
78
- class TestArgsortOpAxisNeg1 ( TestArgsortOp ):
228
+ class TestArgsortOpAxis1GPU ( TestArgsortOpGPU ):
79
229
def init_axis (self ):
80
- self .axis = - 1
230
+ self .axis = 1
81
231
82
232
83
- class TestArgsortOpAxisNeg2 ( TestArgsortOp ):
233
+ class TestArgsortOpAxis2CPU ( TestArgsortOpCPU ):
84
234
def init_axis (self ):
85
- self .axis = - 2
235
+ self .axis = 2
86
236
87
237
88
- class TestArgsortOpFP16 (TestArgsortOp ):
89
- def init_datatype (self ):
90
- if core .is_compiled_with_cuda ():
91
- self .dtype = 'float16'
238
+ class TestArgsortOpAxis2GPU (TestArgsortOpGPU ):
239
+ def init_axis (self ):
240
+ self .axis = 2
92
241
93
- def test_check_output (self ):
94
- pass
95
242
96
- def test_check_output_with_place (self ):
97
- if core .is_compiled_with_cuda ():
98
- place = core .CUDAPlace (0 )
99
- self .check_output_with_place (place , atol = 1e-5 )
243
+ class TestArgsortOpAxisNeg1CPU (TestArgsortOpCPU ):
244
+ def init_axis (self ):
245
+ self .axis = - 1
100
246
101
247
102
- class TestArgsortOpFP16Axis0 ( TestArgsortOpFP16 ):
248
+ class TestArgsortOpAxisNeg1GPU ( TestArgsortOpGPU ):
103
249
def init_axis (self ):
104
- self .axis = 0
250
+ self .axis = - 1
105
251
106
252
107
- class TestArgsortOpFP16Axis2 ( TestArgsortOpFP16 ):
253
+ class TestArgsortOpAxisNeg2CPU ( TestArgsortOpCPU ):
108
254
def init_axis (self ):
109
- self .axis = 2
255
+ self .axis = - 2
110
256
111
257
112
- class TestArgsortOpFP16AxisNeg2 ( TestArgsortOpFP16 ):
258
+ class TestArgsortOpAxisNeg2GPU ( TestArgsortOpGPU ):
113
259
def init_axis (self ):
114
260
self .axis = - 2
115
261
116
262
117
- class TestArgsortOpFP16Axis4Neg4 ( TestArgsortOpFP16 ):
118
- def init_axis (self ):
119
- self .axis = - 4
263
+ class TestArgsortOpDescendingAxisCPU ( TestArgsortOpCPU ):
264
+ def init_direction (self ):
265
+ self .descending = True
120
266
121
267
122
- class TestArgsortOpDescendingAxis ( TestArgsortOp ):
268
+ class TestArgsortOpDescendingAxisGPU ( TestArgsortOpGPU ):
123
269
def init_direction (self ):
124
270
self .descending = True
125
271
126
272
127
- class TestArgsortOpDescendingAxis0 ( TestArgsortOpAxis0 ):
273
+ class TestArgsortOpDescendingAxis0CPU ( TestArgsortOpAxis0CPU ):
128
274
def init_direction (self ):
129
275
self .descending = True
130
276
131
277
132
- class TestArgsortOpDescendingAxis1 ( TestArgsortOpAxis1 ):
278
+ class TestArgsortOpDescendingAxis0GPU ( TestArgsortOpAxis0GPU ):
133
279
def init_direction (self ):
134
280
self .descending = True
135
281
136
282
137
- class TestArgsortOpDescendingAxis2 ( TestArgsortOpAxis2 ):
283
+ class TestArgsortOpDescendingAxis1CPU ( TestArgsortOpAxis1CPU ):
138
284
def init_direction (self ):
139
285
self .descending = True
140
286
141
287
142
- class TestArgsortOpDescendingAxisNeg1 ( TestArgsortOpAxisNeg1 ):
288
+ class TestArgsortOpDescendingAxis1GPU ( TestArgsortOpAxis1GPU ):
143
289
def init_direction (self ):
144
290
self .descending = True
145
291
146
292
147
- class TestArgsortOpDescendingAxisNeg2 ( TestArgsortOpAxisNeg2 ):
293
+ class TestArgsortOpDescendingAxis2CPU ( TestArgsortOpAxis2CPU ):
148
294
def init_direction (self ):
149
295
self .descending = True
150
296
151
297
152
- class TestArgsortOpFP32Axis ( TestArgsortOp ):
153
- def init_datatype (self ):
154
- self .dtype = "float32"
298
+ class TestArgsortOpDescendingAxis2GPU ( TestArgsortOpAxis2GPU ):
299
+ def init_direction (self ):
300
+ self .descending = True
155
301
156
302
157
- class TestArgsortOpFP32DescendingAxis (TestArgsortOp ):
158
- def init_datatype (self ):
159
- self .dtype = "float32"
303
+ class TestArgsortOpDescendingAxisNeg1CPU (TestArgsortOpAxisNeg1CPU ):
304
+ def init_direction (self ):
305
+ self .descending = True
306
+
307
+
308
+ class TestArgsortOpDescendingAxisNeg1GPU (TestArgsortOpAxisNeg1GPU ):
309
+ def init_direction (self ):
310
+ self .descending = True
311
+
312
+
313
+ class TestArgsortOpDescendingAxisNeg2CPU (TestArgsortOpAxisNeg2CPU ):
314
+ def init_direction (self ):
315
+ self .descending = True
316
+
160
317
318
+ class TestArgsortOpDescendingAxisNeg2GPU (TestArgsortOpAxisNeg2GPU ):
161
319
def init_direction (self ):
162
320
self .descending = True
163
321
0 commit comments