排查程序错误The custom_gradient decorator currently supports keywords arguments only when eager execution

本篇文章记录了一次从源代码之中探寻程序报错的原因并修复程序相应bug的思路的过程
今天发现了在使用tensorflow的框架之中具体的一个报错的内容:
tensorflow中报出的ValueError为了找寻错误发生的原因,我们顺着源代码一点一点地去找寻并解读相应的错误:
首先我们顺藤摸瓜,查看最底层的调用错误内容最底层的调用内容这里我们查看相应的输出,发现这个位置的kwargs有具体的内容
kwargs具体的输出内容
查看相应的函数内容如下:

def _graph_mode_decorator(f, args, kwargs):
  """Implement custom gradient decorator for graph mode."""
  # TODO(rsepassi): Add support for kwargs
  print('_graph_mode_decorator')
  print('&&&args = &&&')
  print(args)
  print('&&&kwargs = &&&')
  print(kwargs)
  if kwargs:
    raise ValueError(
        "The custom_gradient decorator currently supports keywords "
        "arguments only when eager execution is enabled.")
  name = "CustomGradient-%s" % ops.uid()
  args = nest.map_structure(ops.convert_to_tensor, args)

从中我们可以发现,在_graph_mode_decorator函数之中调用了args以及kwargs的内容,通过排查函数的调用情况,我们发现有的函数调用了对应的_graph_mode_decorator函数,而有的函数没有调用对应的_graph_mode_decorator函数
搜索_graph_mode_decorator函数发现_graph_mode_decorator总共调用了三次,其中有两次内容是对应的报错提示,所以_graph_mode_decorator这个函数实际上只调用了一次,而就是调用的这一次之中出现了

if kwargs:

的提示情况,所以接下来我们分析从哪一个函数去调用_graph_mode_decorator的函数内容
具体的报错内容我们进入到custom_gradient.py文件之中去解读相应的函数

@Bind.decorator
def decorated(wrapped, args, kwargs):
  """Decorated function with custom gradient."""
  # raise ValueError("PW: trap")

  if context.executing_eagerly():
    print('context.executing_eagerly()')
    return _eager_mode_decorator(wrapped, args, kwargs)
  else:
    print('not context.executing_eagerly()')
    return _graph_mode_decorator(wrapped, args, kwargs)

这里继续观察

not context.executing_eagerly()

内容的调用情况,发现调用情况只有下面的对应这一处
函数调用情况函数调用情况2进一步往上面进行内容的调用,可以发现Bind __call__函数在整个的过程之中只被调用了一次,说明这里不是Bind类的初始化问题,还需要进一步向上进行寻找。
对应的Bind的初始化内容

def __init__(self, f, d):
  print('Bind __init__')
  self._f = f
  self._d = d
  print('self._f = ')
  print(self._f)
  print('self._d = ')
  print(self._d)

def __get__(self, instance, owner):
  if instance is not None:
    f = self._f.__get__(instance, owner)
    return tf_decorator.make_decorator(f, Bind(f, self._d))
  else:
    return self

def __call__(self, *a, **k):
  print('Bind __call__')
  return self._d(self._f, a, k)

这里的输出内容

###self = ###
<bert4keras.layers.LayerNormalization object at 0x7f7bf858a130>
Bind __init__
self._f = 
<bound method recompute_grad.<locals>.inner of <bert4keras.layers.LayerNormalization object at 0x7f7bf858a130>>
self._d = 
<function custom_gradient.<locals>.decorated at 0x7f7bff3f3f70>
$$$$$$call_fn1 = $$$$$$
<tensorflow.python.ops.custom_gradient.Bind object at 0x7f7bf858ab20>
******call_fn2 = ******
<tensorflow.python.ops.custom_gradient.Bind object at 0x7f7bf858ab20>

这些函数的内容仍然都是被调用多次的,我们继续接着往上去寻找

继续往上寻找可以看出

inputs = self._maybe_cast_inputs(inputs)
print('between inputs and outputs')
outputs = call_fn(inputs, *args, **kwargs)

往上继续查看call_fn函数的调用过程

call_fn = self.call
print('$$$$$$call_fn1 = $$$$$$')
print(call_fn1)
if (base_layer_utils.is_subclassed(self) and
    not base_layer_utils.from_saved_model(self)):
  call_fn = autograph.tf_convert(self.call, ag_ctx.control_status_ctx())
print('$$$$$call_fn2 = $$$$$$')
print(call_fn2)

发现有两次call_fn的操作,而这里LayerNormalization中的call_fn函数和其他类之中找寻到的call_fn函数内容有一定的不一致的地方。

1.call_fn = <bound method Embedding.call of 
<bert4keras.layers.Embedding object at 0x7fae5ccbbf10>>
2.call_fn = <bound method PositionEmbedding.call of 
<bert4keras.layers.PositionEmbedding object at 0x7fae5c1ae1f0>>
3.call_fn = <bound method Lambda.call of <tensorflow.python.keras
.layers.core.Lambda object at 0x7fae5c1c02e0>>

对应的输出内容这里我惊奇地发现,LayerNormalization之中的call_fn1以及call_fn2的画风和其他三个call_fn函数的画风不一样。
猜想:由于call_fn = self.call,所以实际上是由于LayerNormalization之中的call函数与其他类别的call函数的不同导致的结局的不同。
对比一下LayerNormalization之中的call函数与其他类别之中的call函数
LayerNormalization之中的call函数

@recompute_grad
def call(self, inputs,**kwargs):
    """如果是条件Layer Norm,则默认以list为输入,第二个是condition
    """
    print('LayerNormalization call')
    if self.conditional:
        inputs, cond = inputs
        if self.hidden_units is not None:
            cond = self.hidden_dense(cond)
        for _ in range(K.ndim(inputs) - K.ndim(cond)):
            cond = K.expand_dims(cond, 1)
        if self.center:
            beta = self.beta_dense(cond) + self.beta
        if self.scale:
            gamma = self.gamma_dense(cond) + self.gamma
    else:
        if self.center:
            beta = self.beta
        if self.scale:
            gamma = self.gamma

    outputs = inputs
    if self.center:
        mean = K.mean(outputs, axis=-1, keepdims=True)
        outputs = outputs - mean
    if self.scale:
        variance = K.mean(K.square(outputs), axis=-1, keepdims=True)
        std = K.sqrt(variance + self.epsilon)
        outputs = outputs / std
        outputs = outputs * gamma
    if self.center:
        outputs = outputs + beta
    return outputs

其他层之中的call函数

def call(self, inputs):
    """如果custom_position_ids,那么第二个输入为自定义的位置id
    """
    if self.custom_position_ids:
        inputs, position_ids = inputs
        if 'int' not in K.dtype(position_ids):
            position_ids = K.cast(position_ids, 'int32')
    else:
        input_shape = K.shape(inputs)
        batch_size, seq_len = input_shape[0], input_shape[1]
        position_ids = K.arange(0, seq_len, dtype='int32')[None]

    if self.hierarchical:
        alpha = 0.4 if self.hierarchical is True else self.hierarchical
        embeddings = self.embeddings - alpha * self.embeddings[:1]
        embeddings = embeddings / (1 - alpha)
        embeddings_x = K.gather(embeddings, position_ids // self.input_dim)
        embeddings_y = K.gather(embeddings, position_ids % self.input_dim)
        embeddings = alpha * embeddings_x + (1 - alpha) * embeddings_y
    else:
        if self.custom_position_ids:
            embeddings = K.gather(self.embeddings, position_ids)
        else:
            embeddings = self.embeddings[None, :seq_len]

    if self.merge_mode == 'add':
        return inputs + embeddings
    elif self.merge_mode == 'mul':
        return inputs * (embeddings + 1.0)
    elif self.merge_mode == 'zero':
        return embeddings
    else:
        if not self.custom_position_ids:
            embeddings = K.tile(embeddings, [batch_size, 1, 1])
        return K.concatenate([inputs, embeddings])

通过观察比较发现,LayerNormalization之中的call函数比PositionEmbedding之中的call函数多出了一个@recompute_grad内容
猜想:正是由于多出来的@recompute_grad的内容,导致Bind的调用发生
去除@recompute_grad之后,发现这部分类的程序运行正常,说明正是由于@recompute_grad的内容存在导致的错误的发生。

经历了上面的排查之后,仍然没有得到正确的结果,所以我们这里继续对相应的结果进行排查
继续排查可能发生的问题
1.可能发生参数配置错误的问题

**kwargs = ***
{'vocab_size': 30000, 'hidden_size': 2560, 'attention_probs_dropout_prob': 0.0, 'hidden_dropout_prob': 0.0, 
'hidden_act': 'gelu', 'initializer_range': 0.02, 'intermediate_size': 10240,
'max_position_embeddings': 1024, 'num_attention_heads': 32, 'num_hidden_layers': 32, 
'max_position': 1024, 'dropout_rate': 0.0, 'segment_vocab_size': 2}

这部分的参数有无发生配置错误
2.具体的网络层结构继续进行对照
对应的网络步数以及具体的概率值为

steps = 0时
probas = 
[[1.8954154e-09 1.5331458e-13 1.7278668e-13
...9.7066864e-13 1.8071902e-13 8.2345963e-13]]
states = None
step = 1时
probas = 
[[8.7498364e-08 1.5958133e-11 2.9840318e-11...
  1.8810269e-10 1.1375931e-10 7.4568900e-11]]
states = None
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值