TensorFlow自定义资源Op两类异常问题排查与修复咨询
问题1:session.run(handle)抛出InternalError: ndarray was 1 bytes but TF_Tensor was 98 bytes的原因
这个错误本质是你试图直接读取资源句柄类型的张量,但它根本不是能转成NumPy数组的普通张量。
具体来说:
OpenFstLoad输出的handle是TensorFlow的资源句柄,它只是一个内部标识符,用来指向你创建的OpenFstInstance资源对象,本身不存储任何可序列化的数据。- 当你调用
session.run(handle)时,TensorFlow会尝试把这个句柄转成NumPy数组,但资源句柄的底层内存结构(98字节)和NumPy期望的数组结构(这里错误地识别为1字节)完全不兼容,所以触发了这个字节数不匹配的错误。
解决办法很简单:别直接打印或运行资源句柄,它只需要作为参数传给需要使用该资源的Op(比如你的OpenFstTransition)就好。
问题2:Session卸载时触发引用计数断言错误的原因及修复
原因分析
这个断言Check failed: ref_.load() == 0 (1 vs. 0)说明,TensorFlow尝试销毁OpenFstInstance对象时,它的引用计数还是1——意味着还有地方拿着这个对象的引用没释放,违反了ResourceBase的生命周期规则。
看你的代码,问题出在OpenFstTransitionOp的资源获取逻辑:
你用GetResourceFromContext(context, "handle", &fst)获取资源时,这个函数会自动给fst调用Ref()增加引用计数;虽然你用了core::ScopedUnref来自动减计数,但手动管理引用很容易出现边界问题,再加上旧版TensorFlow的ResourceOpKernel在引用计数管理上的小bug,就导致了Session销毁时计数没清零。
修复方案
推荐用更可靠的自动引用管理方式,两种方法任选:
方法1:用core::RefCountPtr自动管理引用
修改OpenFstTransitionOp的Compute函数,用RefCountPtr替代原始指针和ScopedUnref,它会自动处理引用计数的增减:
void Compute(OpKernelContext* context) override { core::RefCountPtr<OpenFstInstance> fst; OP_REQUIRES_OK(context, GetResourceFromContext(context, "handle", &fst)); // 下面是你原来的业务逻辑,不需要手动写ScopedUnref了 const Tensor& states_tensor = context->input(1); auto states_flat = states_tensor.flat<int32>(); const Tensor& inputs_tensor = context->input(2); auto inputs_flat = inputs_tensor.flat<int32>(); OP_REQUIRES( context, TensorShapeUtils::IsVector(states_tensor.shape()) && TensorShapeUtils::IsVector(inputs_tensor.shape()) && states_flat.size() == inputs_flat.size(), errors::InvalidArgument( "Shape mismatch. states ", states_tensor.shape().DebugString(), " vs inputs ", inputs_tensor.shape().DebugString())); Tensor* output_new_states_tensor = NULL; OP_REQUIRES_OK(context, context->allocate_output(0, states_tensor.shape(), &output_new_states_tensor)); auto output_new_states_flat = output_new_states_tensor->flat<int32>(); Tensor* output_scores_tensor = NULL; OP_REQUIRES_OK(context, context->allocate_output(1, states_tensor.shape(), &output_scores_tensor)); auto output_scores_flat = output_scores_tensor->flat<float>(); for(int i = 0; i < inputs_flat.size(); ++i) { output_new_states_flat(i) = -1; // TODO output_scores_flat(i) = -1.; // TODO } }
RefCountPtr会在创建时自动调用Ref(),销毁时自动调用Unref(),彻底避免手动管理的失误。
方法2:检查并确保ScopedUnref的作用域(适合坚持用原始指针的场景)
如果不想改指针类型,确保ScopedUnref的作用域完全覆盖资源的使用周期——你的代码里这部分看起来没问题,但可以显式把资源相关逻辑都放在ScopedUnref的作用域内;另外建议升级到较新的TensorFlow稳定版,修复旧版本中ResourceMgr的引用计数bug。
修复后,Session关闭时OpenFstInstance的引用计数会正确降到0,断言错误就不会再出现了。
内容的提问来源于stack exchange,提问作者Albert

