C++ tf.cond的行为不符合预期

C++ tf.cond的行为不符合预期,c++,tensorflow,C++,Tensorflow,我有一个问题与tensorflow如何计算tf.cond语句中的表达式有关 我使用一个自定义操作,它为我提供类型a或类型B的成批数据 REGISTER_OP("GetData") .Attr("path: string") .Output("data: int32") .Output("type: int32") .SetIsStateful() type输出为1表示类型A,2表示类型B。根据类型,我希望运行不同的(自定义)操作,例如opA或opB。当然,它们的输出具有相同的类型。为了表示此数

我有一个问题与tensorflow如何计算
tf.cond
语句中的表达式有关

我使用一个自定义操作,它为我提供类型a或类型B的成批数据

REGISTER_OP("GetData")
.Attr("path: string")
.Output("data: int32")
.Output("type: int32")
.SetIsStateful()
type
输出为1表示类型A,2表示类型B。根据类型,我希望运行不同的(自定义)操作,例如
opA
opB
。当然,它们的输出具有相同的类型。为了表示此数据流,我使用
tf.cond
如下所示:

(data, type) = get_data(path = "...")

opA = op_a(data)
opB = op_b(dat

def perfromA():  return opA
def perfromB():  return opB

joined_op = tf.cond(tf.equal(type, tf.constant(1, dtype=tf.int32)), perfromA, perfromB)
我在
getData
opA
opB
中添加了一些调试语句。对于数据序列ABA,我希望得到

getData: returning type A
opA:     got some data
getData: returning type B
opB:     got some data   
getData: returning type A
opA:     got some data
然而,我确实得到了

getData: returning type A
opA:     got some data
opB:     got some data
getData: returning type B
opA:     got some data
opB:     got some data   
getData: returning type A
opA:     got some data
opB:     got some data
这是预期的行为吗?
joined_op
的结果正确地考虑了if-else,但始终会计算这两个操作。如果
opA
opA
执行影响变量的操作(如优化步骤),这不仅是一个计算负担,而且也会破坏目的

正确的解决方案正如@Yaroslav所指出的

def perfromA():  return op_a(data)
def perfromB():  return op_b(data)
有关更详细的示例,请参见以下三个操作的虚拟实现:

#包括“tensorflow/core/framework/op.h”
#包括“tensorflow/core/framework/op_kernel.h”
#包括“tensorflow/core/lib/random/philox_random.h”
#包括“tensorflow/core/lib/random/simple\u philox.h”
使用名称空间std;
使用名称空间tensorflow;
注册操作(“获取数据”)
.Attr(“路径:字符串”)
.输出(“数据:int32”)
.输出(“类型:int32”)
.SetIsStateful();
类GetData:公共操作内核{
公众:
显式GetData(OpKernelConstruction*ctx):OpKernel(ctx){}
无效计算(OpKernelContext*ctx)覆盖
{
张量数据(DT_INT32,张量形状({}));
张量型(DT_INT32,张量形({}));
if(rng_uu.均匀(2)==0){
type.scalar()()=1;
data.scalar()()=100;
日志(信息)设置输出(1,类型);
}
私人:
random::PhiloxRandom philox_uz=random::PhiloxRandom(10);
random::SimplePhilox rng_=random::SimplePhilox(&philox_);
};
注册内核构建器(名称(“GetData”)。设备(设备CPU),GetData;
登记册(OpA)
.Input(“Input:int32”)
.输出(“输出:int32”);
OpA类:公共操作内核{
公众:
显式OpA(OpKernelConstruction*ctx):OpKernel(ctx){}
无效计算(OpKernelContext*ctx)覆盖
{
常量张量&数据=ctx->输入(0);
日志(信息)设置输出(0,数据);
}
};
注册内核构建器(名称(“OpA”)。设备(设备CPU),OpA;
注册操作(“OpB”)
.Input(“Input:int32”)
.输出(“输出:int32”);
类OpB:公共OpKernel{
公众:
显式OpB(OpKernelConstruction*ctx):OpKernel(ctx){}
无效计算(OpKernelContext*ctx)覆盖
{
常量张量&数据=ctx->输入(0);
日志(信息)设置输出(0,数据);
}
};
注册内核构建器(名称(“OpB”)。设备(设备CPU),OpB;

您必须在tf.cond内创建opA/opB


请参阅讨论

后续:因此,如果opA是一个完整的操作图,而不是一个单独的自定义操作,我需要在operationA函数中定义整个图,而不仅仅是最后一个节点?是的。TensorFlow executor的工作方式是,它只在计算了节点的依赖项后执行节点。“cond”节点依赖项与任何其他节点的依赖项相同,executor没有惰性计算的概念。因此,我将尽可能多地将图移动到opA/opB可调用项中,以进行实际的条件计算。
#include "tensorflow/core/framework/op.h"
#include "tensorflow/core/framework/op_kernel.h"
#include "tensorflow/core/lib/random/philox_random.h"
#include "tensorflow/core/lib/random/simple_philox.h"

using namespace std;
using namespace tensorflow;


REGISTER_OP("GetData")
.Attr("path: string")
.Output("data: int32")
.Output("type: int32")
.SetIsStateful();


class GetData : public OpKernel {
    public:
        explicit GetData(OpKernelConstruction* ctx) : OpKernel(ctx) {}

        void Compute(OpKernelContext* ctx) override
        {
            Tensor data(DT_INT32, TensorShape({}));
            Tensor type(DT_INT32, TensorShape({}));
            if(rng_.Uniform(2) == 0){
                type.scalar<int32>()() = 1;
                data.scalar<int32>()() = 100;
                LOG(INFO) << "returning type A";
            }
            else{
                type.scalar<int32>()() = 2;
                data.scalar<int32>()() = 200;
                LOG(INFO) << "returning type B";
            }

            ctx->set_output(0, data);
            ctx->set_output(1, type);
        }

    private:
        random::PhiloxRandom philox_ =  random::PhiloxRandom(10) ;
        random::SimplePhilox rng_ = random::SimplePhilox(&philox_);

};
REGISTER_KERNEL_BUILDER(Name("GetData").Device(DEVICE_CPU), GetData);


REGISTER_OP("OpA")
.Input("input: int32")
.Output("output: int32");

class OpA : public OpKernel {
    public:
        explicit OpA(OpKernelConstruction* ctx) : OpKernel(ctx) {}

        void Compute(OpKernelContext* ctx) override
        {
              const Tensor& data = ctx->input(0);
              LOG(INFO) << "A: got some data";
              ctx->set_output(0, data);
        }
};
REGISTER_KERNEL_BUILDER(Name("OpA").Device(DEVICE_CPU), OpA);

REGISTER_OP("OpB")
.Input("input: int32")
.Output("output: int32");

class OpB : public OpKernel {
    public:
        explicit OpB(OpKernelConstruction* ctx) : OpKernel(ctx) {}

        void Compute(OpKernelContext* ctx) override
        {
              const Tensor& data = ctx->input(0);
              LOG(INFO) << "B: got some data";
              ctx->set_output(0, data);
        }
};
REGISTER_KERNEL_BUILDER(Name("OpB").Device(DEVICE_CPU), OpB);