Tensorflow 在keras中为y_true和y_pred实现不同大小的自定义损失函数

Tensorflow 在keras中为y_true和y_pred实现不同大小的自定义损失函数,tensorflow,keras,Tensorflow,Keras,我对凯拉斯不熟悉。我需要一些帮助,在keras中使用TensorFlow后端为以下损失方程编写自定义损失函数 传递给损失函数的参数为: y\u true的形状应为(批次大小,N,2)。在这里,我们传递批次中每个样本的N(x,y)坐标 y\u pred的形状为(批量大小,256,256,N)。在这里,我们在批处理中的每个样本中传递N个预测的热图(256 x 256像素) i∈ [0255] j∈ [0255] Mn(i,j)表示第n个预测热图的像素位置的值(i,j) Mn∼(i,j)=Guas

我对凯拉斯不熟悉。我需要一些帮助,在keras中使用TensorFlow后端为以下损失方程编写自定义损失函数

传递给损失函数的参数为:

  • y\u true
    的形状应为
    (批次大小,N,2)
    。在这里,我们传递批次中每个样本的N
    (x,y)
    坐标
  • y\u pred
    的形状为
    (批量大小,256,256,N)
    。在这里,我们在批处理中的每个样本中传递N个预测的热图(256 x 256像素)
    i
    ∈ <代码>[0255]

    j
    ∈ <代码>[0255]

    Mn(i,j)
    表示第n个预测热图的像素位置的值
    (i,j)

    Mn∼(i,j)=Guassian2D((i,j),y_truen,std)
    其中

    std=标准偏差
    ,两个尺寸的标准偏差相同(5 px)

    是第n(x,y)个坐标。这是平均数

    有关详细信息,请查看本文中描述的l2损失

    注:我提到的批次大小为y_true和y_pred。我假设Keras对整个批次调用损失函数,而不是对批次中的单个样本调用损失函数。如果我错了,请纠正我

    def l2_loss(y_true, y_pred):
         loss = 0
         n = y_true.shape[0]
         for j in range(n):
            for i in range(num_joints):
                yv, xv = tf.meshgrid(tf.arange(0, im_height), tf.arange(0, im_width))
                z = np.array([xv, yv]).transpose(1, 2, 0)
                ground = np.exp(-0.5*(((z - y_true[j, i, :])**2).sum(axis=2))/(sigma**2))
                loss = loss + np.sum((ground - y_pred[j,:, :, i])**2)
         return loss/num_joints
    

    这是我到目前为止写的代码。我知道这不会运行,因为我们不能在keras损失函数中使用直接numpy ndarrays。此外,我需要消除循环

    您几乎可以将numpy函数转换为Keras后端函数。唯一需要注意的是设置正确的广播形状

    def l2_loss_keras(y_true,y_pred):
    #设置网格:(高度、宽度、2)
    网格网格=K.tf.网格网格(K.arange(im_高度),K.arange(im_宽度))
    meshgrid=K.cast(K.transpose(K.stack(meshgrid)),K.floatx())
    #设置广播形状:(批量大小、高度、宽度、接头数量,2)
    meshgrid_broadcast=K.expand_dims(K.expand_dims(meshgrid,0),-2)
    y_-true_广播=K.expand_dims(K.expand_dims(y_-true,1),2)
    diff=网格网格广播-y网格广播
    #计算损失:首先求和(高度、宽度),然后取平均数
    地面=K.exp(-0.5*K.sum(K.square(diff),轴=-1)/sigma**2)
    损耗=K.和(K.平方(地面-y_pred),轴=[1,2])
    返回K.平均值(损失,轴=-1)
    
    要验证它,请执行以下操作:

    def l2_loss_numpy(y_true,y_pred):
    损失=0
    n=y_真。形状[0]
    对于范围(n)内的j:
    对于范围内的i(num_关节):
    yv,xv=np.meshgrid(np.arange(0,im_高度),np.arange(0,im_宽度))
    z=np.堆栈([xv,yv]).转置(1,2,0)
    地面=np.exp(-0.5*((z-y_真[j,i,:])**2.sum(轴=2))/(σ**2))
    损失=损失+np.和((地面-y_pred[j,:,:,i])**2)
    返回损耗/接头数量
    批量大小=32
    接头数量=10
    西格玛=5
    im_宽度=256
    im_高度=256
    y_true=255*np.random.rand(批量大小,接头数,2)
    y_pred=255*np.random.rand(批次大小、im_高度、im_宽度、接缝数量)
    打印(l2\u丢失\u numpy(y\u真,y\u pred))
    45448272129
    打印(K.eval(l2_损失_keras(K.variable(y_true)、K.variable(y_pred))).sum())
    4.5448e+10
    
    该数字在默认的
    dtype
    float32下被截断。如果运行时将
    dtype
    设置为float64:

    y\u true=255*np.random.rand(批量大小,接头数量,2)
    y_pred=255*np.random.rand(批次大小、im_高度、im_宽度、接缝数量)
    打印(l2\u丢失\u numpy(y\u真,y\u pred))
    45460126940.6
    打印(K.eval(l2_损失_keras(K.variable(y_true)、K.variable(y_pred))).sum())
    45460126940.6
    

    编辑:

    Keras似乎要求
    y_true
    y_pred
    具有相同数量的维度。例如,在以下测试模型上:

    X=np.random.rand(批量大小,256,256,3)
    模型=顺序([密集(10,输入_形=(256,256,3)))
    compile(loss=l2\u loss\u keras,优化器='adam')
    模型拟合(X,y_为真,批量大小=8)
    ValueError:无法为具有形状“(?,?,?,?,?)”的张量“密集_2_目标:0”馈送形状(8,10,2)的值
    
    要解决此问题,可以在将
    y\u true
    输入模型之前,使用
    expand\u dims
    添加虚拟维度:

    def l2_loss_keras(y_true,y_pred):
    ...
    y_-true_-broadcast=K.expand_-dims(y_-true,1)#更改此行
    ...
    模型拟合(X,np.展开尺寸(y为真,轴=1),批量大小=8)
    
    最近版本的Keras实际上支持不同形状的
    y\u pred
    y\u true
    损失。内置损失
    sparse\u categorical\u crossentropy
    就是一个例子。该损失的TensorFlow实现如下:

    注意它是如何表示
    target:integer张量的。
    而不是
    target:output形状相同的张量。
    和其他张量一样。我尝试了一个自定义的损失,我自己,它似乎工作良好


    我使用的是Keras 2.2.4

    余的回答是正确的,但我想分享我的经验。每当您想编写自定义损失函数时,请注意某些事项:

  • 在编译时,Keras不会抱怨大小不匹配。例如,如果来自模型输出层的
    y\u pred
    具有三维形状,
    y\u true
    将默认为三维形状。但是,在运行时,即在
    fit
    期间,如果像许多人那样以二维形式传递目标数据,则可能最终会在损失函数中得到错误。例如,如果您正在使用logits计算sigmoid交叉熵,它会抱怨。因此,一定要通过np.expand_dims以三维形式传递目标
  • 此外,在自定义丢失中,请确保使用
    y_true
    y_pred
    作为参数(如果有人知道我们可以使用任何其他名称作为参数,请单击)
  • 因此,这不是一个代码编写服务;请展示您迄今为止所尝试的内容以及具体的编程问题