Как получить матрицу расстояний переменного размера в керасах? - PullRequest
0 голосов
/ 30 января 2020

То, чего я сейчас пытаюсь достичь, - это создать пользовательскую функцию потерь в Keras, которая принимает два тензора (y_true, y_pred) с формами (None, None, None) и (None, None, 3) соответственно. Однако None таковы, что две фигуры всегда равны для каждого (y_true, y_pred). Из этих тензоров я хочу получить две матрицы расстояний, которые содержат квадраты расстояний между каждой возможной парой точек (третье измерение длины 3 содержит пространственные значения x, y и z) внутри них, а затем вернуть разницу между этими матрицами расстояний. Первый код, который я попробовал, был такой:

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. Я искал, сталкивались ли другие люди с той же проблемой или нет, и я обнаружил, что могу использовать функцию scan TensorFlow:

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

Так что это пошло в тра sh тоже. Моей последней попыткой было использование функции 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 и тоже не смог ( здесь - ссылка на соответствующий вопрос, если это помогает).

1 Ответ

0 голосов
/ 30 января 2020

Предполагая, что размер 0 - это размер партии, как обычно, и вы не хотите смешивать выборки.
Предполагая, что размер 1 - это тот, который вы хотите создать пары
Предполагая, что последнее измерение равно 3 для всех случаи, когда ваша модель возвращает None.

Итерация тензоров - плохая идея. Может быть, лучше просто сделать 2D-матрицу из исходного 1D, хотя и с повторяющимися значениями.

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) 
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...