Come applicare il gradiente di softmax nel backprop


8

Di recente ho fatto i compiti in cui ho dovuto imparare un modello per la classificazione a 10 cifre MNIST. L'HW aveva del codice per le impalcature e avrei dovuto lavorare nel contesto di questo codice.

I miei compiti funzionano / superano i test, ma ora sto provando a fare tutto da zero (il mio framework nn, nessun codice di impalcature hw) e sono bloccato ad applicare il grandient di softmax nella fase di backprop, e penso anche a cosa hw il codice dell'impalcatura potrebbe non essere corretto.

L'hw mi fa usare quella che chiamano "una perdita di softmax" come l'ultimo nodo di nn. Ciò significa che, per qualche motivo, hanno deciso di unire un'attivazione di softmax con la perdita di entropia crociata in un'unica soluzione, invece di trattare la softmax come una funzione di attivazione e l'entropia crociata come una funzione di perdita separata.

La funzione di perdita hw si presenta così (minimamente modificata da me):

class SoftmaxLoss:
    """
    A batched softmax loss, used for classification problems.
    input[0] (the prediction) = np.array of dims batch_size x 10
    input[1] (the truth) = np.array of dims batch_size x 10
    """
    @staticmethod
    def softmax(input):
        exp = np.exp(input - np.max(input, axis=1, keepdims=True))
        return exp / np.sum(exp, axis=1, keepdims=True)

    @staticmethod
    def forward(inputs):
        softmax = SoftmaxLoss.softmax(inputs[0])
        labels = inputs[1]
        return np.mean(-np.sum(labels * np.log(softmax), axis=1))

    @staticmethod
    def backward(inputs, gradient):
        softmax = SoftmaxLoss.softmax(inputs[0])
        return [
            gradient * (softmax - inputs[1]) / inputs[0].shape[0],
            gradient * (-np.log(softmax)) / inputs[0].shape[0]
        ]

Come puoi vedere, in avanti fa softmax (x) e quindi attraversa la perdita di entropia.

Ma su backprop, sembra fare solo la derivata dell'entropia incrociata e non della softmax. Softmax viene lasciato come tale.

Non dovrebbe anche prendere la derivata di softmax rispetto all'input di softmax?

Supponendo che dovrebbe prendere la derivata del softmax, non sono sicuro di come questo hw superi effettivamente i test ...

Ora, nella mia implementazione da zero, ho creato softmax e cross entropy nodi separati, in questo modo (p e t stanno per predire e verità):

class SoftMax(NetNode):
    def __init__(self, x):
        ex = np.exp(x.data - np.max(x.data, axis=1, keepdims=True))
        super().__init__(ex / np.sum(ex, axis=1, keepdims=True), x)

    def _back(self, x):
        g = self.data * (np.eye(self.data.shape[0]) - self.data)
        x.g += self.g * g
        super()._back()

class LCE(NetNode):
    def __init__(self, p, t):
        super().__init__(
            np.mean(-np.sum(t.data * np.log(p.data), axis=1)),
            p, t
        )

    def _back(self, p, t):
        p.g += self.g * (p.data - t.data) / t.data.shape[0]
        t.g += self.g * -np.log(p.data) / t.data.shape[0]
        super()._back()

Come puoi vedere, la mia perdita di entropia crociata (LCE) ha lo stesso derivato di quello in hw, perché quello è il derivato della perdita stessa, senza ancora entrare nella softmax.

Ma poi, dovrei ancora fare la derivata del softmax per incatenarla con il derivato della perdita. Qui è dove rimango bloccato.

Per softmax definito come:

un'

La derivata è generalmente definita come:

B

Ma ho bisogno di una derivata che si traduca in un tensore della stessa dimensione dell'input di softmax, in questo caso, batch_size x 10. Quindi non sono sicuro di come applicare quanto sopra a soli 10 componenti, poiché implica che io differirebbe per tutti gli input rispetto a tutti gli output (tutte le combinazioni) o cin forma di matrice.


Penso che dovresti pubblicarlo in codereview o
stackoverflow

perché? è una rete neurale, domanda di backprop. Appartiene allo scambio di stack AI.

chiunque mi abbia votato verso il basso probabilmente non è molto esperto in AI ... vediamo, la domanda riguarda l'applicazione di una derivata parziale nel contesto della propagazione posteriore, nel contesto delle reti neurali, nel contesto dell'apprendimento automatico, nel contesto dell'apprendimento supervisionato, nel contesto dell '"AI". Quale parte di questo: 1- mostra la mancanza di ricerca 2-non è correlata a "AI", 3- è una domanda "inviami il codez", 4- è una domanda di opinione 5- è troppo ampia di una domanda ?

Da ai.se faq "e non riguarda ... l'implementazione dell'apprendimento automatico"
mico

@mico ok vedo, sì, per quanto riguarda le domande frequenti hai ragione. Ma lo trovo inaspettato. Voglio dire, discutere della matematica e dell'implementazione di algos di intelligenza artificiale è una pratica comune nel settore (anche a livello accademico).
SaldaVonSchwartz

Risposte:


5

Dopo aver lavorato ulteriormente su questo, ho capito che:

  1. L'implementazione di compiti a casa combina softmax con perdita di entropia incrociata come una questione di scelta, mentre è valida anche la mia scelta di tenere separato softmax come funzione di attivazione.

  2. L'implementazione dei compiti manca davvero della derivata di softmax per il passaggio di backprop.

  3. Il gradiente di softmax rispetto ai suoi input è in realtà il parziale di ogni output rispetto a ciascun input:

gradient1

Quindi per la forma vettoriale (gradiente): gradient2

Che nel mio codice numpy vettoriale è semplicemente:

self.data * (1. - self.data)

Dov'è self.datail softmax dell'input, precedentemente calcolato dal passaggio in avanti.


3
Non penso sia corretto. Devi anche calcolare smax (x_i) / x_j, dove j ≠ i e sommare tutti i singoli gradienti. Questo perché durante il calcolo di softmax per x_i, tutti gli altri parametri vengono utilizzati anche per determinare il valore di softmax.
harveyslash,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.