如何在keras中生成可变大小的距离矩阵?

How to produce a variable size distance matrix in keras?

我现在想要实现的是在 Keras 中创建一个自定义损失函数,它接受两个张量 (y_true, y_pred),形状分别为 (None, None, None)(None, None, 3)。但是,None 是这样的,对于每个 (y_true, y_pred),这两个形状总是相等的。我想从这些张量中生成两个距离矩阵,其中包含每个可能的点对之间的平方距离(第三个,长度为 3 的维度包含 x、y 和 z 空间值),然后 return 这些之间的差异距离矩阵。我尝试的第一个代码是这样的:

def distanceMatrixLoss1(y_true, y_pred):
    distMatrix1 = [[K.sum(K.square(y_true[i] - y_true[j])) for j in range(i + 1, y_true.shape[1])] for j in range(y_true.shape[1])]
    distMatrix2 = [[K.sum(K.square(y_pred[i] - y_pred[j])) for j in range(i + 1, y_pred.shape[1])] for j in range(y_pred.shape[1])]
    return K.mean(K.square(K.flatten(distMatrix1) - K.flatten(distMatrix2)))

(K是TensorFlow后端。)不用说,我得到了以下错误:

'NoneType' object cannot be interpreted as an integer

这是可以理解的,因为 range(None) 没有多大意义,而 y_true.shape[0]y_pred.shape[0]None。我搜索了其他人是否遇到了同样的问题,我发现我可以使用 TensorFlow 的 scan 函数:

def distanceMatrixLoss2(y_true, y_pred):

    subtractYfromXi = lambda x, y: tf.scan(lambda xi: K.sum(K.square(xi - y)), x)
    distMatrix = lambda x, y: K.flatten(tf.scan(lambda yi: subtractYfromXi(x, yi), y))

    distMatrix1 = distMatrix(y_true, y_true)
    distMatrix2 = distMatrix(y_pred, y_pred)

    return K.mean(K.square(distMatrix1-distMatrix2))

我从中得到的是一个不同的错误,我不完全理解。

TypeError: <lambda>() takes 1 positional argument but 2 were given

所以这个也进了垃圾箱。我最后一次尝试是使用后端的 map_fn 函数:

def distanceMatrixLoss3(y_true, y_pred):

    subtractYfromXi = lambda x, y: K.map_fn(lambda xi: K.sum(K.square(xi - y)), x)
    distMatrix = lambda x, y: K.flatten(K.map_fn(lambda yi: subtractYfromXi(x, yi), y))

    distMatrix1 = distMatrix(y_true, y_true)
    distMatrix2 = distMatrix(y_pred, y_pred)

    return K.mean(K.square(distMatrix1-distMatrix2))

这并没有引发错误,但是当训练开始时,损失是恒定的 0 并保持这种状态。所以现在我没有主意了,我恳请你帮我解决这个问题。我已经尝试在 Mathematica 中做同样的事情但也失败了(here 是相应问题的 link,如果有帮助的话)。

假设维度 0 是通常的批量大小,并且您不想混合样本。
假设维度 1 是您要配对的维度
假设所有情况下的最后一个维度都是 3,尽管您的模型 returns None.

迭代张量是个坏主意。尽管具有重复值,但仅从原始 1D 制作 2D 矩阵可能会更好。

def distanceMatrix(true, pred): #shapes (None1, None2, 3)

    #------ creating the distance matrices 1D to 2D -- all vs all

    true1 = K.expand_dims(true, axis=1) #shapes (None1, 1, None2, 3)
    pred1 = K.expand_dims(pred, axis=1)

    true2 = K.expand_dims(true, axis=2) #shapes (None1, None2, 1, 3)
    pred2 = K.expand_dims(pred, axis=2) 

    trueMatrix = true1 - true2 #shapes (None1, None2, None2, 3)
    predMatrix = pred1 - pred2

    #--------- euclidean x, y, z distance

       #maybe needs a sqrt?
    trueMatrix = K.sum(K.square(trueMatrix), axis=-1) #shapes (None1, None2, None2)
    predMatrix = K.sum(K.square(predMatrix), axis=-1)


    #-------- loss for each pair

    loss = K.square(trueMatrix - predMatrix)  #shape (None1, None2, None2)

    #----------compensate the duplicated non-diagonals

    diagonal = K.eye(K.shape(true)[1])  #shape (None2, None2)
        #if Keras complains because the input is a tensor, use `tf.eye`

    diagonal = K.expand_dims(diagonal, axis=0) #shape (1, None2, None2)
    diagonal = 0.5 + (diagonal / 2.)

    loss = loss * diagonal

    #--------------

    return K.mean(loss, axis =[1,2])  #or just K.mean(loss)