GradientTape는 손실 함수에서 두드러기를 계산합니다.

Dec 14 2020

문장을 분류 하고 saliency를 사용하여 분류에 대한 설명을 제공 하는 LSTM 네트워크 를 구축하려고합니다 . 이 네트워크는 실제 클래스 y_true와 그가주의를 기울이지 말아야 할 단어 Z(이진 마스크) 에서 배워야합니다 .

이 논문 은 우리가 손실 함수를 생각해 내도록 영감을주었습니다. 손실 함수를 다음과 같이 표시하고 싶습니다.

Coût de classification아래 코드에서 로 classification_lossCoût d'explication (saillance)saliency_loss(입력 wrt 출력의 기울기와 동일)로 변환됩니다 . 나는 Tensorflow를 백엔드로 사용하여 Keras의 사용자 정의 모델로 이것을 구현하려고했습니다.

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
        ]

나는 계산 classification_loss도 관리 saliency_loss하고 스칼라 값을 얻습니다. 그러나, 이 작품 : tape.gradient(classification_loss, trainable_vars)하지만이하지 않는tape.gradient(classification_loss + saliency_loss, trainable_vars) 및 발생합니다 ValueError: No gradients provided for any variable.

답변

1 xdurch0 Dec 14 2020 at 07:21

테이프 컨텍스트 외부에서 (첫 번째 gradient호출 후) 계산을 수행 한 다음 나중에 더 많은 그라디언트를 사용하려고합니다. 이것은 작동하지 않습니다. 차별화를위한 모든 작업은 컨텍스트 관리자 내에서 이루어져야합니다. 두 개의 중첩 테이프를 사용하여 다음과 같이 코드를 재구성하는 것이 좋습니다.

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)

이제 우리는 돌출에 대한 입력에 대한 기울기를 계산하는 하나의 테이프를 가지고 있습니다. 우리는 그 작업을 추적하고 나중에 그래디언트의 그래디언트를 계산할 수 있는 또 다른 테이프를 가지고 있습니다. 이 테이프는 분류 손실에 대한 기울기도 계산합니다. 내부 테이프가 필요하지 않기 때문에 외부 테이프 컨텍스트에서 분류 손실을 이동했습니다. 두 손실의 추가조차도 외부 테이프의 콘 텍스 내부에 있다는 점에 유의하십시오. 모든 것이 거기에서 발생해야합니다. 그렇지 않으면 계산 그래프가 손실 / 불완전하고 기울기를 계산할 수 없습니다.

Andrey Dec 14 2020 at 00:31

train_step()함께 꾸며보세요@tf.function