提交 5fbf03d6 编写于 作者: L Leo Chen 提交者: Zeng Jinle

Enhance OpTest to support double grad inplace check (#19826)

* update OpTest to support double grad inplace check, test=develop

* keep consistency of _calc_output function, test=develop
上级 6045541e
...@@ -316,25 +316,51 @@ class OpTest(unittest.TestCase): ...@@ -316,25 +316,51 @@ class OpTest(unittest.TestCase):
inputs=inputs, inputs=inputs,
outputs=outputs, outputs=outputs,
attrs=self.attrs) attrs=self.attrs)
return outputs return outputs
def _compare_expect_and_actual_outputs(self,
place,
fetch_list,
expect_outs,
actual_outs,
inplace_atol=None):
# compare expect_outs and actual_outs
for i, name in enumerate(fetch_list):
if inplace_atol is not None:
self.assertTrue(
np.allclose(
np.array(expect_outs[i]),
np.array(actual_outs[i]),
atol=inplace_atol),
"Output (" + name + ") has diff at " + str(place) +
" when using and not using inplace" + "\nExpect " +
str(expect_outs[i]) + "\n" + "But Got" + str(actual_outs[i])
+ " in class " + self.__class__.__name__)
else:
self.assertTrue(
np.array_equal(
np.array(expect_outs[i]), np.array(actual_outs[i])),
"Output (" + name + ") has diff at " + str(place) +
" when using and not using inplace" + "\nExpect " +
str(expect_outs[i]) + "\n" + "But Got" + str(actual_outs[i])
+ " in class " + self.__class__.__name__ + '\n')
def _calc_output(self, def _calc_output(self,
place, place,
parallel=False, parallel=False,
no_check_set=None, no_check_set=None,
loss=None, loss=None,
enable_inplace=None, enable_inplace=None,
for_inplace_grad_test=None): for_inplace_test=None):
program = Program() program = Program()
block = program.global_block() block = program.global_block()
self._append_ops(block) op = self._append_ops(block)
inputs = self._get_inputs(block) inputs = self._get_inputs(block)
outputs = self._get_outputs(block) outputs = self._get_outputs(block)
feed_map = self.feed_var(inputs, place) feed_map = self.feed_var(inputs, place)
if for_inplace_grad_test is not None: if for_inplace_test:
# Some variables' tensors hold no buffer (tensor's _holder is NULL), like XShape in reshape2 op, # Some variables' tensors hold no buffer (tensor's _holder is NULL), like XShape in reshape2 op,
# and the shapes of those variables contain 0 (eg. Xshape.shape = [0, 2, 5]). # and the shapes of those variables contain 0 (eg. Xshape.shape = [0, 2, 5]).
# Set persistable for those variables in order to get them from global_scope for inplace grad test directly other than feed them, # Set persistable for those variables in order to get them from global_scope for inplace grad test directly other than feed them,
...@@ -342,6 +368,7 @@ class OpTest(unittest.TestCase): ...@@ -342,6 +368,7 @@ class OpTest(unittest.TestCase):
for name, var in block.vars.items(): for name, var in block.vars.items():
if 0 in var.shape: if 0 in var.shape:
var.persistable = True var.persistable = True
original_program = program
if parallel: if parallel:
use_cuda = False use_cuda = False
if isinstance(place, fluid.CUDAPlace): if isinstance(place, fluid.CUDAPlace):
...@@ -358,16 +385,13 @@ class OpTest(unittest.TestCase): ...@@ -358,16 +385,13 @@ class OpTest(unittest.TestCase):
continue continue
if isinstance(var, list): if isinstance(var, list):
for v in var: for v in var:
fetch_list.append(v) fetch_list.append(v.name)
else: else:
fetch_list.append(var) fetch_list.append(var.name)
# if the fetch_list still empty, fill the fetch_list by the operator output. # if the fetch_list still empty, fill the fetch_list by the operator output.
if len(fetch_list) == 0: if len(fetch_list) == 0:
for out_name, out_dup in Operator.get_op_outputs(self.op_type): for out_name, out_dup in Operator.get_op_outputs(self.op_type):
fetch_list.append(str(out_name)) fetch_list.append(str(out_name))
# fetch_list = map(block.var, fetch_list)
if not isinstance(fetch_list[0], fluid.framework.Variable):
fetch_list = list(map(block.var, fetch_list))
if enable_inplace is not None: if enable_inplace is not None:
build_strategy = fluid.BuildStrategy() build_strategy = fluid.BuildStrategy()
...@@ -382,7 +406,10 @@ class OpTest(unittest.TestCase): ...@@ -382,7 +406,10 @@ class OpTest(unittest.TestCase):
feed=feed_map, feed=feed_map,
fetch_list=fetch_list, fetch_list=fetch_list,
return_numpy=False) return_numpy=False)
return outs, fetch_list if for_inplace_test:
return outs, fetch_list, feed_map, original_program, op.desc
else:
return outs, fetch_list
def check_inplace_output_with_place(self, def check_inplace_output_with_place(self,
place, place,
...@@ -391,60 +418,85 @@ class OpTest(unittest.TestCase): ...@@ -391,60 +418,85 @@ class OpTest(unittest.TestCase):
# can`t enable inplace # can`t enable inplace
if not fluid.core.has_infer_inplace(self.op_type): if not fluid.core.has_infer_inplace(self.op_type):
return return
expect_outs, fetch_list = self._calc_output( expect_res = self._calc_output(
place, no_check_set=no_check_set, enable_inplace=False) place,
actual_outs, fetch_list = self._calc_output( no_check_set=no_check_set,
place, no_check_set=no_check_set, enable_inplace=True) enable_inplace=False,
for_inplace_test=True)
actual_res = self._calc_output(
place,
no_check_set=no_check_set,
enable_inplace=True,
for_inplace_test=True)
# compare expect_outs and actual_outs # compare expect_outs and actual_outs
for i, out in enumerate(fetch_list): self._compare_expect_and_actual_outputs(
if inplace_atol is not None: place,
self.assertTrue( expect_res[1],
np.allclose( expect_res[0],
np.array(expect_outs[i]), actual_res[0],
np.array(actual_outs[i]), inplace_atol=inplace_atol)
atol=inplace_atol),
"Output (" + out.name + ") has diff at " + str(place) + # check grad
" when using and not using inplace" + "\nExpect " + # TODO(zhiqiu): enhance inplace_grad test for ops (sum and activation) using mkldnn
str(expect_outs[i]) + "\n" + "But Got" + str(actual_outs[i]) # skip use_mkldnn currently
+ " in class " + self.__class__.__name__) flags_use_mkldnn = fluid.core.get_flags_use_mkldnn()
else: attrs_use_mkldnn = hasattr(
self.assertTrue( self, 'attrs') and bool(self.attrs.get('use_mkldnn', False))
np.array_equal( if flags_use_mkldnn or attrs_use_mkldnn:
np.array(expect_outs[i]), np.array(actual_outs[i])), warnings.warn(
"Output (" + out.name + ") has diff at " + str(place) + "check inplace_grad for ops using mkldnn is not supported")
" when using and not using inplace" + "\nExpect " + return
str(expect_outs[i]) + "\n" + "But Got" + str(actual_outs[i]) use_ngraph = fluid.core.is_compiled_with_ngraph(
+ " in class " + self.__class__.__name__ + '\n') ) and fluid.core.get_flags_use_ngraph()
if use_ngraph:
def check_inplace_grad_output_with_place(self, warnings.warn(
place, "check inplace_grad for ops using ngraph is not supported")
no_check_set=None, return
inplace_atol=None):
# create forward program to get forward vars
program = Program()
block = program.global_block()
op = self._append_ops(block)
inputs = self._get_inputs(block)
outputs = self._get_outputs(block)
feed_map = self.feed_var(inputs, place)
fwd_outs = expect_res[0]
fwd_fetch_list = expect_res[1]
fwd_feed_map = expect_res[2]
fwd_program = expect_res[3]
fwd_op_desc = expect_res[4]
self.check_inplace_grad_output_using_fwd_inputs_outputs(
place,
fwd_feed_map,
fwd_fetch_list,
fwd_outs,
fwd_program,
fwd_op_desc,
no_check_set=no_check_set,
inplace_atol=inplace_atol,
depth=0)
def check_inplace_grad_output_using_fwd_inputs_outputs(self,
place,
fwd_feed_map,
fwd_fetch_list,
fwd_outs,
fwd_program,
fwd_op_desc,
no_check_set=None,
inplace_atol=None,
depth=0):
# depth=0 means grad
# depth=1 means double_grad
# depth=2 means triple_grad, which is not supported yet
if depth >= 2:
return
# get grad_op # get grad_op
if not fluid.core.has_grad_op_maker(op.desc.type()): if not fluid.core.has_grad_op_maker(fwd_op_desc.type()):
return return
grad_op_desc_list, op_grad_to_var = core.get_grad_op_desc(op.desc, grad_op_desc_list, op_grad_to_var = core.get_grad_op_desc(fwd_op_desc,
set(), []) set(), [])
# has grad_op_maker but no grad_op # has grad_op_maker but no grad_op
if not grad_op_desc_list: if not grad_op_desc_list:
return return
for i, grad_op_desc in enumerate(grad_op_desc_list): for i, grad_op_desc in enumerate(grad_op_desc_list):
# grad_op can not inplace # grad_op can not inplace
if not fluid.core.has_infer_inplace(grad_op_desc.type()): if not fluid.core.has_infer_inplace(grad_op_desc.type()):
continue continue
# get forward outs
forward_outs, fetch_list = self._calc_output(
place, no_check_set=no_check_set, for_inplace_grad_test=True)
# create grad program # create grad program
grad_program = Program() grad_program = Program()
...@@ -453,20 +505,20 @@ class OpTest(unittest.TestCase): ...@@ -453,20 +505,20 @@ class OpTest(unittest.TestCase):
new_op_desc.copy_from(grad_op_desc) new_op_desc.copy_from(grad_op_desc)
grad_program._sync_with_cpp() grad_program._sync_with_cpp()
# create grad vars based on forward vars (shape and dtype) # create grad vars based on fwd vars (shape and dtype)
for arg in grad_op_desc.input_arg_names( for arg in grad_op_desc.input_arg_names(
) + grad_op_desc.output_arg_names(): ) + grad_op_desc.output_arg_names():
forward_var_name = op_grad_to_var.get(arg, None) fwd_var_name = op_grad_to_var.get(arg, None)
if forward_var_name is None: if fwd_var_name is None:
forward_var_name = arg fwd_var_name = arg
forward_var = block.vars.get(forward_var_name) fwd_var = fwd_program.global_block().vars.get(fwd_var_name)
assert forward_var is not None, "{} cannot be found".format( assert fwd_var is not None, "{} cannot be found".format(
forward_var_name) fwd_var_name)
grad_var = grad_block.create_var( grad_var = grad_block.create_var(
name=arg, name=arg,
dtype=forward_var.dtype, dtype=fwd_var.dtype,
shape=forward_var.shape, shape=fwd_var.shape,
type=forward_var.type, type=fwd_var.type,
persistable=False) persistable=False)
# some variables' tensors hold no buffer (tensor's _holder is NULL), like XShape in reshape2 op, # some variables' tensors hold no buffer (tensor's _holder is NULL), like XShape in reshape2 op,
# and the shapes of those variables contain 0 (eg. Xshape.shape = [0, 2, 5]). # and the shapes of those variables contain 0 (eg. Xshape.shape = [0, 2, 5]).
...@@ -477,30 +529,31 @@ class OpTest(unittest.TestCase): ...@@ -477,30 +529,31 @@ class OpTest(unittest.TestCase):
grad_program._sync_with_cpp() grad_program._sync_with_cpp()
grad_fetch_list = grad_op_desc.output_arg_names() grad_fetch_list = grad_op_desc.output_arg_names()
def _calc_grad_output(enable_inplace=None): # generate grad_feed_map for grad_program
# generate feed_map for grad_program # since we don`t really check gradient accuracy, but the consistency when using and not using inplace
# since we don`t really check gradient accuracy, but the consistency when using and not using inplace # we use fwd outs (also inputs sometimes) as grad (fake) feeds
# we use forward outs (also inputs sometimes) as grad (fake) feeds p = core.Place()
p = core.Place() p.set_place(place)
p.set_place(place) grad_feed_map = {}
grad_feed_map = {} for arg in grad_op_desc.input_arg_names():
for arg in grad_op_desc.input_arg_names(): if arg in fwd_feed_map.keys():
if arg in feed_map.keys(): grad_feed_map[arg] = fwd_feed_map[arg]._copy(p)
grad_feed_map[arg] = feed_map[arg]._copy(p) else:
else: fwd_var_name = op_grad_to_var.get(arg, None)
forward_var_name = op_grad_to_var.get(arg, None) if fwd_var_name is None:
if forward_var_name is None: fwd_var_name = arg
forward_var_name = arg
for i, out in enumerate(fetch_list): for i, out_name in enumerate(fwd_fetch_list):
if out.name == forward_var_name: if out_name == fwd_var_name:
# don't feed variables whose tensors hold no buffer (shape contains 0 like shape = [0,2,5] and holder_ is NULL), like XShape in reshape2 op. # don't feed variables whose tensors hold no buffer (shape contains 0 like shape = [0,2,5] and holder_ is NULL), like XShape in reshape2 op.
# get them from global_scope directly since we have set them persistable in forward execution # get them from global_scope directly since we have set them persistable in fwd execution
if 0 in out.shape: if 0 in fwd_program.global_block().var(
continue out_name).shape:
else: continue
grad_feed_map[arg] = forward_outs[i]._copy( else:
p) grad_feed_map[arg] = fwd_outs[i]._copy(p)
def _calc_grad_output(enable_inplace=None):
exe = Executor(place) exe = Executor(place)
build_strategy = fluid.BuildStrategy() build_strategy = fluid.BuildStrategy()
build_strategy.enable_inplace = enable_inplace build_strategy.enable_inplace = enable_inplace
...@@ -519,27 +572,24 @@ class OpTest(unittest.TestCase): ...@@ -519,27 +572,24 @@ class OpTest(unittest.TestCase):
actual_outs = _calc_grad_output(enable_inplace=True) actual_outs = _calc_grad_output(enable_inplace=True)
# compare expect_outs and actual_outs # compare expect_outs and actual_outs
for i, out_name in enumerate(grad_fetch_list): self._compare_expect_and_actual_outputs(
if inplace_atol is not None: place,
self.assertTrue( grad_fetch_list,
np.allclose( expect_outs,
np.array(expect_outs[i]), actual_outs,
np.array(actual_outs[i]), inplace_atol=inplace_atol)
atol=inplace_atol),
"Output (" + out_name + ") has diff at " + str(place) + # check grad of grad, recursively
" when using and not using inplace" + "\nExpect " + self.check_inplace_grad_output_using_fwd_inputs_outputs(
str(expect_outs[i]) + "\n" + "But Got" + place,
str(actual_outs[i]) + " in class " + grad_feed_map,
self.__class__.__name__) grad_fetch_list,
else: expect_outs,
self.assertTrue( grad_program,
np.array_equal( grad_op_desc,
np.array(expect_outs[i]), np.array(actual_outs[i])), no_check_set=no_check_set,
"Output (" + out_name + ") has diff at " + str(place) + inplace_atol=inplace_atol,
" when using and not using inplace" + "\nExpect " + depth=depth + 1)
str(expect_outs[i]) + "\n" + "But Got" +
str(actual_outs[i]) + " in class " +
self.__class__.__name__)
def check_output_with_place(self, def check_output_with_place(self,
place, place,
...@@ -552,7 +602,6 @@ class OpTest(unittest.TestCase): ...@@ -552,7 +602,6 @@ class OpTest(unittest.TestCase):
dygraph_outs = self._calc_dygraph_output( dygraph_outs = self._calc_dygraph_output(
place, no_check_set=no_check_set) place, no_check_set=no_check_set)
outs, fetch_list = self._calc_output(place, no_check_set=no_check_set) outs, fetch_list = self._calc_output(place, no_check_set=no_check_set)
for out_name, out_dup in Operator.get_op_outputs(self.op_type): for out_name, out_dup in Operator.get_op_outputs(self.op_type):
if out_name not in self.outputs: if out_name not in self.outputs:
continue continue
...@@ -561,8 +610,8 @@ class OpTest(unittest.TestCase): ...@@ -561,8 +610,8 @@ class OpTest(unittest.TestCase):
def find_actual(target_name, fetch_list): def find_actual(target_name, fetch_list):
found = [ found = [
i for i, var in enumerate(fetch_list) i for i, var_name in enumerate(fetch_list)
if var.name == target_name if var_name == target_name
] ]
self.assertTrue( self.assertTrue(
len(found) == 1, "Found {} {}".format( len(found) == 1, "Found {} {}".format(
...@@ -655,24 +704,6 @@ class OpTest(unittest.TestCase): ...@@ -655,24 +704,6 @@ class OpTest(unittest.TestCase):
self.check_inplace_output_with_place( self.check_inplace_output_with_place(
place, no_check_set=no_check_set, inplace_atol=inplace_atol) place, no_check_set=no_check_set, inplace_atol=inplace_atol)
# TODO(zhiqiu): enhance inplace_grad test for ops (sum and activation) using mkldnn
# skip use_mkldnn currently
flags_use_mkldnn = fluid.core.get_flags_use_mkldnn()
attrs_use_mkldnn = hasattr(
self, 'attrs') and bool(self.attrs.get('use_mkldnn', False))
if flags_use_mkldnn or attrs_use_mkldnn:
warnings.warn(
"check inplace_grad for ops using mkldnn is not supported")
return
use_ngraph = fluid.core.is_compiled_with_ngraph(
) and fluid.core.get_flags_use_ngraph()
if use_ngraph:
warnings.warn(
"check inplace_grad for ops using ngraph is not supported")
return
self.check_inplace_grad_output_with_place(
place, no_check_set=no_check_set, inplace_atol=inplace_atol)
def _get_places(self): def _get_places(self):
if self.dtype == np.float16: if self.dtype == np.float16:
if core.is_compiled_with_cuda() and core.op_support_gpu( if core.is_compiled_with_cuda() and core.op_support_gpu(
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册