web-dev-qa-db-fra.com

Comment parcourir les tenseurs dans la fonction de perte personnalisée?

J'utilise des keras avec un backend tensorflow. Mon objectif est d'interroger le batchsize du lot actuel dans une fonction de perte personnalisée . Cela est nécessaire pour calculer les valeurs des fonctions de perte personnalisées qui dépendent de l'indice des observations particulières. J'aime rendre cela plus clair étant donné les exemples reproductibles minimum ci-dessous.

(BTW: Bien sûr, je pourrais utiliser la taille de lot définie pour la procédure de formation et le plugin c'est une valeur lors de la définition de la fonction de perte personnalisée, mais il y a quelques raisons pour lesquelles cela peut varier, surtout si epochsize % batchsize (Epochsize modulo batchsize) est égal à zéro, le dernier lot d'une époque a une taille différente. Je n'ai pas trouvé d'approche appropriée dans stackoverflow, en particulier par exemple indexation du tenseur dans la fonction de perte personnalisée et fonction de perte personnalisée de Tensorflow en Keras - boucle sur le tenseur et Boucle sur un tenseur car il est évident que la forme d'un tenseur ne peut pas être déduite lors de la construction du graphique, ce qui est le cas pour une fonction de perte - l'inférence de forme n'est que possible lors de l'évaluation étant donné les données, ce qui n'est possible que dans le graphique. Par conséquent, je dois dire à la fonction de perte personnalisée de faire quelque chose avec des éléments particuliers le long d'une certaine dimension sans connaître la longueur de la dimension.

(c'est la même chose dans tous les exemples)

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'))

exemple 1: rien de spécial sans problème, pas de perte personnalisée

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)

(Sortie omise, cela fonctionne parfaitement bien)

exemple 2: rien de spécial, avec une perte personnalisée assez simple

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)

(Sortie omise, cela fonctionne parfaitement bien)

exemple 3: le problème

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)

Bien sûr, le tenseur n'a pas encore mis en forme les informations qui ne peuvent pas être déduites lors de la construction du graphique, uniquement au moment de la formation. Par conséquent, for i in range(n) génère une erreur. Existe-t-il un moyen d'effectuer cela?

La trace de la sortie: enter image description here

-------

BTW voici ma véritable fonction de perte personnalisée en cas de questions. Je l'ai sauté ci-dessus pour plus de clarté et de simplicité.

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

Voici une image de la log-vraisemblance négative partielle du modèle des coz proportionnels au risque.

enter image description here

Il s'agit de clarifier une question dans les commentaires pour éviter toute confusion. Je ne pense pas qu'il soit nécessaire de comprendre cela en détail pour répondre à la question.

19
Thomas

Comme d'habitude, ne bouclez pas. Il y a de graves inconvénients en termes de performances et également des bogues. N'utilisez que des fonctions backend, sauf si elles sont totalement inévitables (généralement ce n'est pas inévitable)


Solution pour l'exemple 3:

Donc, il y a une chose très bizarre là-bas ...

Voulez-vous vraiment ignorer la moitié des prédictions de votre modèle? (Exemple 3)

En supposant que cela soit vrai, dupliquez simplement votre tenseur dans la dernière dimension, aplatissez-en et jetez-en la moitié. Vous avez l'effet exact que vous souhaitez.

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)

Solution pour votre fonction de perte:

Si vous avez trié les temps du plus grand au plus bas, faites simplement une somme cumulative.

Attention: Si vous avez une fois par échantillon, vous ne pouvez pas vous entraîner avec des mini-lots !!!
batch_size = len(labels)

Il est logique d'avoir du temps dans une dimension supplémentaire (plusieurs fois par échantillon), comme cela se fait dans les réseaux de travail récurrents et 1D conv. Quoi qu'il en soit, compte tenu de votre exemple exprimé, c'est-à-dire la forme (samples_equal_times,) Pour yTime:

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