本篇文章记录了一次从源代码之中探寻程序报错的原因并修复程序相应bug的思路的过程
今天发现了在使用tensorflow的框架之中具体的一个报错的内容:
为了找寻错误发生的原因,我们顺着源代码一点一点地去找寻并解读相应的错误:
首先我们顺藤摸瓜,查看最底层的调用错误内容这里我们查看相应的输出,发现这个位置的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这个函数实际上只调用了一次,而就是调用的这一次之中出现了
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()
内容的调用情况,发现调用情况只有下面的对应这一处
进一步往上面进行内容的调用,可以发现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