Il test unitario è tuo amico
C'è un detto tra gli scrittori che "Tutta la scrittura è riscrittura", cioè la maggior parte della scrittura è la revisione. Per i programmatori (o almeno i data scientist) l'espressione potrebbe essere riformulata come "Tutto il codice è debug".
Ogni volta che scrivi un codice, devi verificare che funzioni come previsto. Il metodo migliore che abbia mai trovato per verificare la correttezza consiste nel suddividere il codice in piccoli segmenti e verificare che ciascun segmento funzioni. Questo può essere fatto confrontando l'output del segmento con quello che sai essere la risposta corretta. Questo si chiama unit test . Scrivere buoni test unitari è un elemento chiave per diventare un buon statistico / scienziato di dati / esperto di apprendimento automatico / professionista della rete neurale. Semplicemente non c'è sostituto.
Devi verificare che il tuo codice sia privo di bug prima di poter ottimizzare le prestazioni della rete! Altrimenti, potresti anche riorganizzare le sedie a sdraio sul Titanic RMS .
Esistono due caratteristiche delle reti neurali che rendono la verifica ancora più importante rispetto ad altri tipi di apprendimento automatico o modelli statistici.
Le reti neurali non sono algoritmi "standardizzati" come lo sono la regressione logistica o forestale casuale. Anche per le reti semplici, feed-forward, l'onere spetta in gran parte all'utente prendere numerose decisioni su come la rete è configurata, connessa, inizializzata e ottimizzata. Ciò significa scrivere codice e scrivere codice significa debug.
Anche quando un codice di rete neurale viene eseguito senza sollevare un'eccezione, la rete può ancora avere bug! Questi bug potrebbero persino essere il tipo insidioso per il quale la rete si allenerà, ma rimarranno bloccati in una soluzione non ottimale, oppure la rete risultante non avrà l'architettura desiderata. ( Questo è un esempio della differenza tra un errore sintattico e semantico .)
Questo post Medium , " Come testare il codice di apprendimento automatico delle unità ", di Chase Roberts, esamina in dettaglio i test di unità per i modelli di apprendimento automatico. Ho preso in prestito questo esempio di codice errato dall'articolo:
def make_convnet(input_image):
net = slim.conv2d(input_image, 32, [11, 11], scope="conv1_11x11")
net = slim.conv2d(input_image, 64, [5, 5], scope="conv2_5x5")
net = slim.max_pool2d(net, [4, 4], stride=4, scope='pool1')
net = slim.conv2d(input_image, 64, [5, 5], scope="conv3_5x5")
net = slim.conv2d(input_image, 128, [3, 3], scope="conv4_3x3")
net = slim.max_pool2d(net, [2, 2], scope='pool2')
net = slim.conv2d(input_image, 128, [3, 3], scope="conv5_3x3")
net = slim.max_pool2d(net, [2, 2], scope='pool3')
net = slim.conv2d(input_image, 32, [1, 1], scope="conv6_1x1")
return net
Vedi l'errore? Molte delle diverse operazioni non vengono effettivamente utilizzate perché i risultati precedenti vengono sovrascritti con nuove variabili. L'uso di questo blocco di codice in una rete continuerà ad allenarsi e i pesi si aggiorneranno e la perdita potrebbe persino diminuire, ma il codice sicuramente non sta facendo ciò che era previsto. (L'autore è anche incoerente sull'uso di virgolette singole o doppie, ma è puramente stilistico.)
Gli errori di programmazione più comuni relativi alle reti neurali sono
- Le variabili vengono create ma mai utilizzate (in genere a causa di errori di copia e incolla);
- Le espressioni per gli aggiornamenti del gradiente non sono corrette;
- Gli aggiornamenti del peso non vengono applicati;
- Le funzioni di perdita non sono misurate sulla scala corretta (ad esempio, la perdita di entropia può essere espressa in termini di probabilità o logit)
- La perdita non è appropriata per l'attività (ad esempio, utilizzando la perdita categorica incrociata entropia per un'attività di regressione).
Striscia prima di camminare; Cammina prima di correre
Reti neurali ampie e profonde e reti neurali con cablaggi esotici, sono la cosa calda in questo momento nell'apprendimento automatico. Ma queste reti non sono nate pienamente formate nell'esistenza; i loro progettisti li costruirono da unità più piccole. Innanzitutto, crea una piccola rete con un singolo livello nascosto e verifica che funzioni correttamente. Quindi aggiungere in modo incrementale ulteriore complessità del modello e verificare che anche ognuna di queste funzioni.
Troppo pochi neuroni in uno strato può limitare la rappresentazione che la rete apprende, causando sotto-montaggio. Troppi neuroni possono causare un eccesso di adattamento perché la rete "memorizzerà" i dati di allenamento.
Anche se puoi dimostrare che, matematicamente, è necessario solo un piccolo numero di neuroni per modellare un problema, spesso accade che avere "pochi" neuroni rende più facile per l'ottimizzatore trovare una "buona" configurazione. (Ma non credo che nessuno comprenda appieno il motivo per cui questo è il caso.) Fornisco qui un esempio di questo nel contesto del problema XOR: le mie iterazioni non sono necessarie per addestrare NN per XOR con MSE <0,001 troppo alto? .
La scelta del numero di livelli nascosti consente alla rete di apprendere un'astrazione dai dati grezzi. Il deep learning è di gran moda in questi giorni e le reti con un gran numero di livelli hanno mostrato risultati impressionanti. Ma l'aggiunta di troppi layer nascosti può comportare rischi di overfitting o rendere molto difficile l'ottimizzazione della rete.
Scegliere un cablaggio di rete intelligente può fare molto del lavoro per te. La tua fonte di dati è adatta a architetture di rete specializzate? Le reti neurali convoluzionali possono ottenere risultati impressionanti su fonti di dati "strutturate", dati di immagini o audio. Le reti neurali ricorrenti possono fare bene su tipi di dati sequenziali, come il linguaggio naturale o i dati di serie temporali. Le connessioni residue possono migliorare le reti di feed-forward profonde.
L'allenamento della rete neurale è come il lock picking
Per ottenere risultati allo stato dell'arte, o anche semplicemente buoni, è necessario aver impostato tutte le parti configurate per funzionare bene insieme . Impostare una configurazione di rete neurale che apprende in realtà è un po 'come scegliere un lucchetto: tutti i pezzi devono essere allineati nel modo giusto. Così come non è sufficiente avere un solo bicchiere nel posto giusto, né è sufficiente che solo l'architettura, o solo l'ottimizzatore, siano impostati correttamente.
Ottimizzare le scelte di configurazione non è così semplice come dire che un tipo di scelta di configurazione (ad es. Velocità di apprendimento) è più o meno importante di un'altra (ad es. Numero di unità), poiché tutte queste scelte interagiscono con tutte le altre scelte, quindi uno la scelta può fare bene in combinazione con un'altra scelta fatta altrove .
Questo è un elenco non esaustivo delle opzioni di configurazione che non sono anche opzioni di regolarizzazione o opzioni di ottimizzazione numerica.
Tutti questi argomenti sono aree attive di ricerca.
L'ottimizzazione non convessa è difficile
La funzione oggettiva di una rete neurale è convessa solo quando non ci sono unità nascoste, tutte le attivazioni sono lineari e la matrice di progettazione è a pieno titolo, poiché questa configurazione è identicamente un normale problema di regressione.
In tutti gli altri casi, il problema dell'ottimizzazione non è convesso e l'ottimizzazione non convessa è difficile. Le sfide della formazione delle reti neurali sono ben note (vedi: Perché è difficile addestrare reti neurali profonde? ). Inoltre, le reti neurali hanno un numero molto grande di parametri, il che ci limita ai soli metodi del primo ordine (vedi: Perché il metodo di Newton non è ampiamente usato nell'apprendimento automatico? ). Questa è un'area di ricerca molto attiva.
Impostando una velocità di apprendimento troppo grande, l'ottimizzazione divergerà, poiché salterai da un lato del "canyon" all'altro. L'impostazione di un valore troppo basso ti impedirà di compiere progressi reali e probabilmente consentirà al rumore inerente a SGD di sopraffare le stime del gradiente.
Il ritaglio del gradiente ridimensiona la norma del gradiente se supera una certa soglia. Pensavo che si trattasse di un parametro imposta e dimentica, in genere a 1.0, ma ho scoperto che avrei potuto migliorare notevolmente un modello di linguaggio LSTM impostandolo su 0,25. Non so perché.
La pianificazione della frequenza di apprendimento può ridurre la frequenza di apprendimento nel corso della formazione. Nella mia esperienza, provare a usare la pianificazione è molto simile a regex : sostituisce un problema ("Come posso imparare a continuare dopo una certa epoca?") Con due problemi ("Come posso imparare a continuare dopo una certa epoca? ? "e" Come faccio a scegliere un buon programma? "). Altre persone insistono sul fatto che la programmazione è essenziale. Ti lascio decidere.
La scelta di una buona dimensione del minibatch può influenzare indirettamente il processo di apprendimento, dal momento che un mini-batch più grande tenderà ad avere una varianza minore ( legge dei numeri grandi ) rispetto a un mini-batch più piccolo. Volete che il mini-batch sia abbastanza grande da essere informativo sulla direzione del gradiente, ma abbastanza piccolo da consentire a SGD di regolarizzare la vostra rete.
Ci sono una serie di varianti sulla discesa gradiente stocastica che usano lo slancio, i tassi di apprendimento adattivo, gli aggiornamenti di Nesterov e così via per migliorare su SGD vaniglia. Progettare un ottimizzatore migliore è molto un'area di ricerca attiva. Qualche esempio:
Quando è uscito per la prima volta, l'ottimizzatore Adam ha suscitato molto interesse. Ma alcune ricerche recenti hanno scoperto che SGD con slancio può superare i metodi adattivi di gradiente per le reti neurali. " Il valore marginale dei metodi del gradiente adattivo nell'apprendimento automatico " di Ashia C. Wilson, Rebecca Roelofs, Mitchell Stern, Nathan Srebro, Benjamin Recht
D'altro canto, questo documento molto recente propone un nuovo ottimizzatore adattativo del tasso di apprendimento che presumibilmente colma il divario tra i metodi del tasso adattivo e la SGD. " Colmare il divario di generalizzazione dei metodi adattivi di gradiente nella formazione di reti neurali profonde " di Jinghui Chen, Quanquan Gu
È stato osservato che i metodi di gradiente adattivo, che adottano informazioni storiche sul gradiente per regolare automaticamente il tasso di apprendimento, sono peggiori della discesa gradiente stocastica (SGD) con slancio nell'allenamento delle reti neurali profonde. Questo lascia un problema aperto per colmare il divario di generalizzazione dei metodi di gradiente adattivo. In questo lavoro, mostriamo che i metodi di gradiente adattivo come Adam, Amsgrad, sono talvolta "troppo adattati". Progettiamo un nuovo algoritmo, chiamato metodo di stima del momento parzialmente adattivo (Padam), che unifica Adam / Amsgrad con SGD per ottenere il meglio da entrambi i mondi. Esperimenti su benchmark standard mostrano che Padam è in grado di mantenere un rapido tasso di convergenza come Adam / Amsgrad mentre si generalizza e SGD nella formazione di reti neurali profonde.
Normalizzazione
La scala dei dati può fare una grande differenza sulla formazione.
[ - 0,5 , 0,5 ]
La normalizzazione dei livelli può migliorare l'allenamento della rete mantenendo una media attiva e una deviazione standard per le attivazioni dei neuroni. Non è ben noto il motivo per cui ciò aiuta la formazione e rimane un'area di ricerca attiva.
- " Capire la normalizzazione in lotti " di Johan Bjorck, Carla Gomes, Bart Selman
- " Verso una comprensione teorica della normalizzazione in lotti " di Jonas Kohler, Hadi Daneshmand, Aurelien Lucchi, Ming Zhou, Klaus Neymeyr, Thomas Hofmann
- "In che modo la normalizzazione in lotti aiuta l'ottimizzazione? (No, non si tratta dello spostamento interno della covariata) " di Shibani Santurkar, Dimitris Tsipras, Andrew Ilyas, Aleksander Madry
regolarizzazione
La scelta e l'ottimizzazione della regolarizzazione della rete è una parte fondamentale della costruzione di un modello che si generalizza bene (vale a dire, un modello che non si adatta ai dati di addestramento). Tuttavia, nel momento in cui la tua rete sta lottando per ridurre la perdita dei dati di addestramento - quando la rete non sta imparando - la regolarizzazione può oscurare qual è il problema.
Quando la mia rete non impara, spengo tutta la regolarizzazione e verifico che la rete non regolarizzata funzioni correttamente. Quindi aggiungo nuovamente ogni pezzo di regolarizzazione e verifico che ciascuno di essi funzioni lungo il percorso.
Questa tattica può individuare dove una certa regolarizzazione potrebbe essere impostata male. Alcuni esempi sono
L2L1
Due parti della regolarizzazione sono in conflitto. Ad esempio, è ampiamente osservato che la normalizzazione dei livelli e il dropout sono difficili da usare insieme. Poiché uno dei due è molto utile da solo, capire come utilizzare entrambi è un'area attiva di ricerca.
Tieni un diario degli esperimenti
Quando installo una rete neurale, non codifico in modo rigido nessuna impostazione di parametro. Invece, lo faccio in un file di configurazione (ad es. JSON) che viene letto e utilizzato per popolare i dettagli della configurazione di rete in fase di esecuzione. Conservo tutti questi file di configurazione. Se apporto qualche modifica ai parametri, creo un nuovo file di configurazione. Infine, aggiungo come commenti tutte le perdite per epoca per formazione e validazione.
K
Ad esempio, volevo conoscere i modelli di linguaggio LSTM, quindi ho deciso di creare un bot Twitter che scrive nuovi tweet in risposta ad altri utenti di Twitter. Ci ho lavorato nel tempo libero, tra la scuola di specializzazione e il mio lavoro. Ci sono voluti circa un anno e ho ripetuto circa 150 modelli diversi prima di arrivare a un modello che faceva quello che volevo: generare un nuovo testo in lingua inglese che (in un certo senso) ha senso. (Un punto critico, e parte del motivo per cui sono stati fatti così tanti tentativi, è che non era sufficiente ottenere semplicemente una perdita fuori campione, poiché i primi modelli a bassa perdita erano riusciti a memorizzare i dati di addestramento, quindi stava solo riproducendo alla lettera i blocchi di testo germano in risposta ai prompt - ci sono voluti alcuni ritocchi per rendere il modello più spontaneo e avere comunque una perdita ridotta).