web-dev-qa-db-ja.com

条件付きのケラスにカスタム損失関数を実装する

ケラスロス機能の助けが必要です。 Tensorflowバックエンドを使用して、ケラスにカスタム損失関数を実装しています。

私はnumpyでカスタム損失関数を実装しましたが、ケラス損失関数に変換できればすばらしいでしょう。損失関数は、データフレームと一連のユーザー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

ケラスロス機能を実装してみました。 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"]()]]

どんな種類の助けも本当にいただければ幸いです。

6
Black Mask

まず、Keras損失関数で "_y_true_および_y_pred_からnumpy配列を抽出"することはできません。損失を計算するには、Kerasバックエンド関数(またはTF関数)でテンソルを操作する必要があります。

言い換えると、if-elseとループを使用せずに、損失を計算する「ベクトル化された」方法について考えることをお勧めします。

損失関数は次の手順で計算できます。

  1. encodingsのすべてのベクトルのペア間のペアワイズユークリッド距離の行列を生成します。
  2. 要素_I_ij_が_user_i == user_j_の場合は1、_user_i != user_j_の場合は-1の行列Iを生成します。
  3. 要素ごとに2つの行列を乗算し、要素を合計して最終的な損失を取得します。

ここに実装があります:

_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
_
7
Yu-Yang

Kerasでパラメーター化されたカスタム損失関数を実装するには、2つのステップがあります。最初に、係数/メトリックのメソッドを記述します。次に、ラッパー関数を記述して、Kerasが必要とするものをフォーマットします。

  1. DICEのような単純なカスタム損失関数に直接tensorflowの代わりにKerasバックエンドを使用することは実際にはかなりクリーンです。その方法で実装された係数の例を次に示します。

    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)
3
PyMatFlow