Kayıp işlevinde GradientTape hesaplama belirginliği

Dec 14 2020

Cümleleri sınıflandırmak ve belirginlik kullanarak sınıflandırma için açıklama sağlamak için bir LSTM ağı oluşturmaya çalışıyorum . Bu ağ, gerçek sınıftan y_trueve hangi kelimelere dikkat etmemesi gerektiğini Z(ikili maske) öğrenmelidir .

Bu makale , kayıp fonksiyonumuzu bulmamız için bize ilham verdi. İşte kayıp fonksiyonumun nasıl görünmesini istediğim:

Coût de classificationçevirir classification_lossve Coût d'explication (saillance)üzere saliency_lossaşağıdaki kod (giriş wrt çıkış gradyanı ile aynıdır) . Bunu, arka uç olarak Tensorflow ile Keras'ta özel bir Model ile uygulamaya çalıştım:

loss_tracker = metrics.Mean(name="loss")
classification_loss_tracker = metrics.Mean(name="classification_loss")
saliency_loss_tracker = metrics.Mean(name="saliency_loss")
accuracy_tracker = metrics.CategoricalAccuracy(name="accuracy")

class CustomSequentialModel(Sequential):
        
    def _train_test_step(self, data, training):
        # Unpack the data
        X = data[0]["X"]
        Z = data[0]["Z"] # binary mask (1 for important words)
        y_true = data[1]
        
        # gradient tape requires "float32" instead of "int32"
        # X.shape = (None, MAX_SEQUENCE_LENGTH, EMBEDDING_DIM)
        X = tf.cast(X, tf.float32)

        # Persitent=True because we call the `gradient` more than once
        with GradientTape(persistent=True) as tape:
            # The tape will record everything that happens to X
            # for automatic differentiation later on (used to compute saliency)
            tape.watch(X)
            # Forward pass
            y_pred = self(X, training=training) 
            
            # (1) Compute the classification_loss
            classification_loss = K.mean(
                categorical_crossentropy(y_true, y_pred)
            )
 
            # (2) Compute the saliency loss
            # (2.1) Compute the gradient of output wrt the maximum probability
            log_prediction_proba = K.log(K.max(y_pred))
            
        # (2.2) Compute the gradient of the output wrt the input
        # saliency.shape is (None, MAX_SEQUENCE_LENGTH, None)
        # why isn't it (None, MAX_SEQUENCE_LENGTH, EMBEDDING_DIM) ?!
        saliency = tape.gradient(log_prediction_proba, X)
        # (2.3) Sum along the embedding dimension
        saliency = K.sum(saliency, axis=2)
        # (2.4) Sum with the binary mask
        saliency_loss = K.sum(K.square(saliency)*(1-Z))
        # =>  ValueError: No gradients provided for any variable
        loss = classification_loss + saliency_loss 
        
        trainable_vars = self.trainable_variables
        # ValueError caused by the '+ saliency_loss'
        gradients = tape.gradient(loss, trainable_vars) 
        del tape # garbage collection
        
        if training:
            # Update weights
            self.optimizer.apply_gradients(zip(gradients, trainable_vars))
        
        # Update metrics
        saliency_loss_tracker.update_state(saliency_loss)
        classification_loss_tracker.update_state(classification_loss)
        loss_tracker.update_state(loss)
        accuracy_tracker.update_state(y_true, y_pred)
        
        # Return a dict mapping metric names to current value
        return {m.name: m.result() for m in self.metrics}
    
    def train_step(self, data):
        return self._train_test_step(data, True)
    
    def test_step(self, data):
        return self._train_test_step(data, False)
    
    @property
    def metrics(self):
        return [
            loss_tracker,
            classification_loss_tracker,
            saliency_loss_tracker,
            accuracy_tracker
        ]

Ben hesaplamak için yönetmek classification_lossyanı sıra saliency_lossve bir skaler değer elde. Ancak bu işe yarar: tape.gradient(classification_loss, trainable_vars)ama bu işe yaramaztape.gradient(classification_loss + saliency_loss, trainable_vars) ve atmazValueError: No gradients provided for any variable .

Yanıtlar

1 xdurch0 Dec 14 2020 at 07:21

Teyp bağlamının dışında (ilk gradientaramadan sonra ) hesaplamalar yapıyorsunuz ve ardından daha fazla gradyan almaya çalışıyorsunuz. Bu çalışmıyor; farklılaştırmak için tüm işlemlerin bağlam yöneticisi içinde gerçekleştirilmesi gerekir. Kodunuzu iki iç içe bant kullanarak aşağıdaki şekilde yeniden yapılandırmanızı öneririm:

with GradientTape() as loss_tape:
    with GradientTape() as saliency_tape:
        # The tape will record everything that happens to X
        # for automatic differentiation later on (used to compute saliency)
        saliency_tape.watch(X)
        # Forward pass
        y_pred = self(X, training=training) 
        
        # (2) Compute the saliency loss
        # (2.1) Compute the gradient of output wrt the maximum probability
        log_prediction_proba = K.log(K.max(y_pred))
        
    # (2.2) Compute the gradient of the output wrt the input
    # saliency.shape is (None, MAX_SEQUENCE_LENGTH, None)
    # why isn't it (None, MAX_SEQUENCE_LENGTH, EMBEDDING_DIM) ?!
    saliency = saliency_tape.gradient(log_prediction_proba, X)
    # (2.3) Sum along the embedding dimension
    saliency = K.sum(saliency, axis=2)
    # (2.4) Sum with the binary mask
    saliency_loss = K.sum(K.square(saliency)*(1-Z))

    # (1) Compute the classification_loss
    classification_loss = K.mean(
        categorical_crossentropy(y_true, y_pred)
    )

    loss = classification_loss + saliency_loss 
    
trainable_vars = self.trainable_variables
gradients = loss_tape.gradient(loss, trainable_vars)

Şimdi, göze çarpan girdiyi oluşturan gradyanları hesaplamaktan sorumlu bir kasetimiz var. Biz başka bu işlemleri izler ve daha sonra degrade eğimi hesaplayabilir etrafında bandı (yani gradyan belirginlik arasında). Bu bant aynı zamanda sınıflandırma kaybı için gradyanları hesaplar. Sınıflandırma kaybını dış bant bağlamında taşıdım çünkü iç bant buna ihtiyaç duymuyor. Ayrıca, iki kaybın toplamının bile dış bandın bağlamı içinde olduğuna dikkat edin - her şeyin orada olması gerekir, aksi takdirde hesaplama grafiği kaybolur / eksiktir ve gradyanlar hesaplanamaz.

Andrey Dec 14 2020 at 00:31

Süslemek için deneyin train_step()ile@tf.function