2014-09-29 10 views
12

Una rete neurale è in realtà solo una funzione enorme con molti parametri, quindi potreste pensare che sarebbe bello scrivere una tale funzione in un linguaggio funzionale, ma avendo lavorato su alcune librerie NN per altre lingue, ho alcuni dubbi su come implementarli efficientemente in questo paradigma.Come implementare una rete neurale generica in modo efficiente in Haskell?

passando intorno informazioni: Se si fa un grafico delle dipendenze di ogni neurone o di livello, si ottiene qualcosa di simile

enter image description here

dove x è l'ingresso, e f è l'uscita. Mentre sembra piuttosto semplice nel grafico, se davvero trattare la rete in funzione poi f deve passare all'ingresso (più un sottoinsieme dei parametri di peso) per g1 e g2, ciascuno di essi ha per passare questi a h1, h2 e h3 chi finalmente invia il risultato allo strato sopra. Tenendo conto che l'insieme di input più l'insieme di pesi potrebbe essere un migliaio di variabili, questo sembra altamente inefficiente. In altre lingue non dovresti farlo poiché ogni neurone potrebbe preservare i suoi parametri, e gli input potrebbero essere passati direttamente al livello di input.

Uniti: Se guardate il grafico, sia g1 e g2 stanno per chiamare separatamente h2, ma dal momento che h2 deve produrre lo stesso valore per entrambi, non è così ha senso calcolare la sua produzione due volte. Dal momento che non si hanno effetti collaterali o stati, questo diventa complicato, e se si dispone di una rete davvero enorme, anche con alcuni calcoli paralleli, questo sta per perdere un sacco di tempo.

Infine, quando dico che la rete è generica, voglio dire che può avere una forma arbitraria purché la sua struttura non contenga cicli. La maggior parte delle librerie che ho visto usano una pila di livelli, quindi devi solo definire il numero di strati e il numero di neuroni in ogni strato, ma la sua forma è un grafico lineare; questo va bene per le applicazioni semplici, ma le cose veramente difficili richiedono reti con architetture più complesse

Id come qualche consiglio su come attaccare questi problemi poiché vorrei implementare una libreria per le mie esigenze.

Nota:

io non sono totalmente nuovo per il linguaggio. Ho usato una discreta quantità di Functional e Monads (principalmente nella mia libreria FP per C# basata su API haskells), ma non ho mai usato haskell per un'applicazione reale prima.

Aggiornamento

Il State monade sembra molto promettente!

+2

Avete studiato come funziona il pacchetto HNN? –

+0

Lo esaminerò. So che usano hmatrices e non aspirano a qualcosa di così generico, ma forse hanno anche affrontato questi problemi. –

+0

Se funziona come un grafico di chiamata di funzione, il mio primo istinto è che probabilmente si potrebbe migliorare l'efficienza usando una struttura di tipo nexus, come in questo documento http://www.cs.ox.ac.uk/ralf.hinze/ pubblicazioni/HW03.pdf. Sfortunatamente, non conosco abbastanza bene il problema per dare una risposta. "Hylomorphism" potrebbe anche essere una parola chiave da cercare. Modifica: In realtà, ora che ci penso, potresti aver bisogno di più informazioni strutturali di quelle fornite normalmente. –

risposta

1

Voglio dire, dal momento che Haskell ha mutua ricorsione probabilmente il modo più semplice per scrivere il grafico che hai costruito è in termini di una grande reciprocamente ricorsivo clausola where:

run_graph x = run_f g1 g2 where 
    g1 = run_g1 h1 h2 
    g2 = run_g2 h2 h3 
    h1 = run_h1 x 
    h2 = run_h2 x 
    h3 = run_h3 x 

Dando questi numeri più ricco tipi che, ad esempio, Double è possibile creare una sorta di struttura di dati astratta corrispondente al grafico, su cui è possibile utilizzare backpropagation.

Potrebbe non essere necessario preoccuparsi di ottimizzare prematuramente il grafico per la valutazione ancora: Haskell memorizza automaticamente nella cache le risposte alle funzioni sui propri input; questo è possibile perché nelle funzioni Haskell non dovrebbero avere effetti collaterali. C'è una buona probabilità che quando le tue funzioni run_* creano nodi astratti in una struttura dati, il eval_g1 h2 h3 finirà per utilizzare il valore memorizzato nella cache da eval_h2 x che viene quando si torna indietro a h2. Se questo diventa troppo noioso, probabilmente è ancora possibile passare a una passeggiata dei nodi con uno stato IntMap, prima di iniziare esplicitamente a specificare i livelli e propagare lo Data.Array.Unboxed attraverso i livelli del grafico.

Problemi correlati