web-dev-qa-db-ja.com

カスタム損失関数でテンソルを反復する方法は?

私はテンソルフローバックエンドでケラスを使用しています。私の目標は、現在のバッチのbatchsizeカスタムロス関数でクエリすることです。これは、特定の観測のインデックスに依存するカスタム損失関数の値を計算するために必要です。以下の再現可能な最小限の例を考えると、これをより明確にすることが好きです。

(BTW:もちろん、カスタム損失関数を定義するときに、その値であるトレーニングプロシージャとプラグインに定義されたバッチサイズを使用できますが、特に_epochsize % batchsize_(epochsize modulo batchsize)が等しくないゼロの場合、エポックの最後のバッチのサイズが異なります。スタックオーバーフローで適切なアプローチが見つかりませんでした。特に、たとえば カスタムロス関数のテンソルインデックス および Tensorflowカスタムロス関数Keras-テンソルでループ および テンソルでループ 損失関数の場合であるグラフを構築するとき、明らかにテンソルの形状を推測できないため-形状の推定のみが可能です与えられたデータを評価するとき、これはグラフでのみ可能であるため、カスタム損失関数に、次元の長さを知らなくても、特定の次元に沿った特定の要素で何かを行うように指示する必要があります。

(これはすべての例で同じです)

_from keras.models import Sequential
from keras.layers import Dense, Activation

# Generate dummy data
import numpy as np
data = np.random.random((1000, 100))
labels = np.random.randint(2, size=(1000, 1))

model = Sequential()
model.add(Dense(32, activation='relu', input_dim=100))
model.add(Dense(1, activation='sigmoid'))
_

例1:問題なく特別なものはなく、カスタム損失はありません

_model.compile(optimizer='rmsprop',
              loss='binary_crossentropy',
              metrics=['accuracy'])    

# Train the model, iterating on the data in batches of 32 samples
model.fit(data, labels, epochs=10, batch_size=32)
_

(出力は省略され、完全に正常に実行されます)

例2:特別なものはなく、かなり単純なカスタム損失

_def custom_loss(yTrue, yPred):
    loss = np.abs(yTrue-yPred)
    return loss

model.compile(optimizer='rmsprop',
              loss=custom_loss,
              metrics=['accuracy'])

# Train the model, iterating on the data in batches of 32 samples
model.fit(data, labels, epochs=10, batch_size=32)
_

(出力は省略され、完全に正常に実行されます)

例3:問題

_def custom_loss(yTrue, yPred):
    print(yPred) # Output: Tensor("dense_2/Sigmoid:0", shape=(?, 1), dtype=float32)
    n = yPred.shape[0]
    for i in range(n): # TypeError: __index__ returned non-int (type NoneType)
        loss = np.abs(yTrue[i]-yPred[int(i/2)])
    return loss

model.compile(optimizer='rmsprop',
              loss=custom_loss,
              metrics=['accuracy'])

# Train the model, iterating on the data in batches of 32 samples
model.fit(data, labels, epochs=10, batch_size=32)
_

もちろん、テンソルにはまだ形状情報がありません。これは、グラフの作成時に推測できないため、トレーニング時のみです。したがって、for i in range(n)はエラーを発生させます。これを実行する方法はありますか?

出力のトレースバック:enter image description here

-------

ところで、ここに質問がある場合の私の真のカスタム損失関数があります。わかりやすく簡単にするために、上記の説明はスキップしました。

_def neg_log_likelihood(yTrue,yPred):
    yStatus = yTrue[:,0]
    yTime = yTrue[:,1]    
    n = yTrue.shape[0]    
    for i in range(n):
        s1 = K.greater_equal(yTime, yTime[i])
        s2 = K.exp(yPred[s1])
        s3 = K.sum(s2)
        logsum = K.log(y3)
        loss = K.sum(yStatus[i] * yPred[i] - logsum)
    return loss
_

これはcoxプロポーショナルハザードモデルの部分的な負の対数尤度の画像です。

enter image description here

これは、混乱を避けるためにコメントの質問を明確にするためです。質問に答えるためにこれを詳細に理解する必要はないと思います。

19
Thomas

いつものように、ループしないでください。重大なパフォーマンスの欠点とバグもあります。完全にやむを得ない場合を除き、バックエンド関数のみを使用します(通常、不可避ではありません)。


例3のソリューション:

だから、そこには非常に奇妙なことがある...

モデルの予測の半分を単に無視したいですか? (例3)

これが真実であると仮定して、最後の次元でテンソルを複製し、その半分を平坦化して破棄するだけです。あなたはあなたが望む正確な効果を持っています。

_def custom_loss(true, pred):
    n = K.shape(pred)[0:1]

    pred = K.concatenate([pred]*2, axis=-1) #duplicate in the last axis
    pred = K.flatten(pred)                  #flatten 
    pred = K.slice(pred,                    #take only half (= n samples)
                   K.constant([0], dtype="int32"), 
                   n) 

    return K.abs(true - pred)
_

損失関数の解決策:

時間を大きい順に並べ替えた場合は、累積合計を計算します。

警告:サンプルごとに1つの時間がある場合、ミニバッチでトレーニングすることはできません!!!
batch_size = len(labels)

再帰的および1D変換ネットワークで行われているように、追加の次元(サンプルごとに何度も)で時間をとることは理にかなっています。とにかく、あなたの例が表現されていると考えると、それは_yTimeの形_(samples_equal_times,)_です:

_def neg_log_likelihood(yTrue,yPred):
    yStatus = yTrue[:,0]
    yTime = yTrue[:,1]    
    n = K.shape(yTrue)[0]    


    #sort the times and everything else from greater to lower:
    #obs, you can have the data sorted already and avoid doing it here for performance

    #important, yTime will be sorted in the last dimension, make sure its (None,) in this case
    # or that it's (None, time_length) in the case of many times per sample
    sortedTime, sortedIndices = tf.math.top_k(yTime, n, True)    
    sortedStatus = K.gather(yStatus, sortedIndices)
    sortedPreds = K.gather(yPred, sortedIndices)

    #do the calculations
    exp = K.exp(sortedPreds)
    sums = K.cumsum(exp)  #this will have the sum for j >= i in the loop
    logsums = K.log(sums)

    return K.sum(sortedStatus * sortedPreds - logsums)
_
2
Daniel Möller