Реализация пользовательской функции потерь в кератах с условием - PullRequest
0 голосов
/ 29 июня 2018

Мне нужна помощь с функцией потери кераса. Я реализовал пользовательскую функцию потерь на керах с бэкэндом Tensorflow.

Я реализовал пользовательскую функцию потерь в numpy, но было бы здорово, если бы она могла быть преобразована в функцию потерь keras. Функция потерь принимает фрейм данных и серию идентификаторов пользователей. Евклидово расстояние для одного и того же user_id является положительным и отрицательным, если user_id отличается. Функция возвращает суммированное скалярное расстояние кадра данных.

def custom_loss_numpy (encodings, user_id):
# user_id: a pandas series of users
# encodings: a pandas dataframe of encodings

    batch_dist = 0

    for i in range(len(user_id)):
         first_row = encodings.iloc[i,:].values
         first_user = user_id[i]

         for j in range(i+1, len(user_id)):
              second_user = user_id[j]
              second_row = encodings.iloc[j,:].values

        # compute distance: if the users are same then Euclidean distance is positive otherwise negative.
            if first_user == second_user:
                tmp_dist = np.linalg.norm(first_row - second_row)
            else:
                tmp_dist = -np.linalg.norm(first_row - second_row)

            batch_dist += tmp_dist

    return batch_dist

Я попытался реализовать функцию потери кераса. Я извлек массив numpy из тензорных объектов y_true и y_pred.

def custom_loss_keras(y_true, y_pred):
    # session of my program
    sess = tf_session.TF_Session().get()

    with sess.as_default():
        array_pred = y_pred.eval()
        print(array_pred)

Но я получаю следующую ошибку.

tensorflow.python.framework.errors_impl.InvalidArgumentError: You must feed a value for placeholder tensor 'dense_1_input' with dtype float and shape [?,102]
 [[Node: dense_1_input = Placeholder[dtype=DT_FLOAT, shape=[?,102], _device="/job:localhost/replica:0/task:0/device:CPU:0"]()]]

Любая помощь будет очень признательна.

Ответы [ 2 ]

0 голосов
/ 03 июля 2018

Прежде всего, невозможно "извлечь массив numpy из y_true и y_pred" в функциях потери Keras. Вы должны работать с тензорами с внутренними функциями Keras (или функциями TF) для расчета потерь.

Другими словами, было бы лучше подумать о «векторизованном» способе расчета потерь, без использования if-else и циклов.

Ваша функция потерь может быть вычислена в следующих шагах:

  1. Генерация матрицы парных евклидовых расстояний между всеми парами векторов в encodings.
  2. Генерация матрицы I, элемент I_ij которой равен 1, если user_i == user_j, и -1, если user_i != user_j.
  3. Поэлементно умножьте две матрицы и суммируйте элементы, чтобы получить окончательную потерю.

Вот реализация:

def custom_loss_keras(user_id, encodings):
    # calculate pairwise Euclidean distance matrix
    pairwise_diff = K.expand_dims(encodings, 0) - K.expand_dims(encodings, 1)
    pairwise_squared_distance = K.sum(K.square(pairwise_diff), axis=-1)

    # add a small number before taking K.sqrt for numerical safety
    # (K.sqrt(0) sometimes becomes nan)
    pairwise_distance = K.sqrt(pairwise_squared_distance + K.epsilon())

    # this will be a pairwise matrix of True and False, with shape (batch_size, batch_size)
    pairwise_equal = K.equal(K.expand_dims(user_id, 0), K.expand_dims(user_id, 1))

    # convert True and False to 1 and -1
    pos_neg = K.cast(pairwise_equal, K.floatx()) * 2 - 1

    # divide by 2 to match the output of `custom_loss_numpy`, but it's not really necessary
    return K.sum(pairwise_distance * pos_neg, axis=-1) / 2

Я предположил, что user_id являются целыми числами в коде выше. Хитрость здесь в том, чтобы использовать K.expand_dims для реализации парных операций. На первый взгляд это, наверное, немного сложно понять, но это весьма полезно.

Это должно дать примерно то же значение потерь, что и custom_loss_numpy (из-за K.epsilon() будет небольшая разница):

encodings = np.random.rand(32, 10)
user_id = np.random.randint(10, size=32)

print(K.eval(custom_loss_keras(K.variable(user_id), K.variable(encodings))).sum())
-478.4245

print(custom_loss_numpy(pd.DataFrame(encodings), pd.Series(user_id)))
-478.42953553795815

Я допустил ошибку в функции потерь.

Когда эта функция используется в обучении, поскольку Keras автоматически меняет y_true на минимум 2D, аргумент user_id больше не является 1D-тензором. Форма его будет (batch_size, 1).

Чтобы использовать эту функцию, необходимо удалить лишнюю ось:

def custom_loss_keras(user_id, encodings):
    pairwise_diff = K.expand_dims(encodings, 0) - K.expand_dims(encodings, 1)
    pairwise_squared_distance = K.sum(K.square(pairwise_diff), axis=-1)
    pairwise_distance = K.sqrt(pairwise_squared_distance + K.epsilon())

    user_id = K.squeeze(user_id, axis=1)  # remove the axis added by Keras
    pairwise_equal = K.equal(K.expand_dims(user_id, 0), K.expand_dims(user_id, 1))

    pos_neg = K.cast(pairwise_equal, K.floatx()) * 2 - 1
    return K.sum(pairwise_distance * pos_neg, axis=-1) / 2
0 голосов
/ 29 июня 2018

Существует два этапа реализации параметризованной пользовательской функции потерь в Keras. Во-первых, написание метода для коэффициента / метрики. Во-вторых, написание функции-оболочки для форматирования вещей так, как это нужно Keras.

  1. На самом деле немного чище использовать бэкэнд Keras вместо тензорного потока для простых пользовательских функций потерь, таких как DICE. Вот пример коэффициента, реализованного таким образом:

    import keras.backend as K
    def dice_coef(y_true, y_pred, smooth, thresh):
        y_pred = y_pred > thresh
        y_true_f = K.flatten(y_true)
        y_pred_f = K.flatten(y_pred)
        intersection = K.sum(y_true_f * y_pred_f)
        return (2. * intersection + smooth) / (K.sum(y_true_f) + K.sum(y_pred_f) + smooth)
    
  1. Теперь для хитрой части. Функции потери Keras должны принимать только (y_true, y_pred) в качестве параметров. Поэтому нам нужна отдельная функция, которая возвращает другую функцию:

    def dice_loss(smooth, thresh):
        def dice(y_true, y_pred)
            return -dice_coef(y_true, y_pred, smooth, thresh)
        return dice
    

Наконец, вы можете использовать его в Keras следующим образом compile:

# build model 
model = my_model()
# get the loss function
model_dice = dice_loss(smooth=1e-5, thresh=0.5)
# compile model
model.compile(loss=model_dice)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...