首页 文章

用条件在keras中实现自定义丢失函数

提问于
浏览
4

我需要一些keras损失功能的帮助 . 我一直在使用Tensorflow后端在keras上实现自定义丢失功能 .

我已经在numpy中实现了自定义丢失函数,但如果它可以转换为keras损失函数则会很棒 . loss函数采用数据帧和一系列用户id . 如果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

我试图实现keras损失功能 . 我从y_true和y_pred张量对象中提取了numpy数组 .

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 回答

  • 2

    在Keras中实现参数化自定义丢失功能有两个步骤 . 首先,编写系数/度量的方法 . 其次,编写一个包装函数来按照Keras需要的方式格式化事物 .

    • 使用Keras后端而不是tensorflow直接用于简单的自定义丢失功能(如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)
    
    • 现在是棘手的部分 . 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)
    
  • 4

    首先,不可能在Keras损失函数中“从 y_truey_pred 中提取numpy数组” . 您必须使用Keras后端功能(或TF功能)来操作张量来计算损耗 .

    换句话说,最好不要使用if-else和循环来考虑计算损失的“矢量化”方法 .

    您的损失函数可以通过以下步骤计算:

    • encodings 中的所有矢量对之间生成成对欧几里德距离的矩阵 .

    • 生成矩阵 I ,如果 user_i == user_j ,则其元素 I_ij 为1,如果 user_i != user_j ,则为-1 .

    • 元素乘以两个矩阵,并总结元素以获得最终损失 .

    这是一个实现:

    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 来实现成对操作 . 它's probably a bit difficult to understand at a first glance, but it'非常有用 .

    它应该给出与 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
    

相关问题