învățarea profundă este un subset de metode de învățare automată care se bazează pe rețele neuronale artificiale. Acestea sunt inspirate de procesarea informațiilor și nodurile de comunicare distribuite în sisteme biologice, cum ar fi creierul. În învățarea profundă, fiecare nivel învață să transforme datele de intrare într-o reprezentare puțin mai abstractă și mai compusă. De exemplu, într-un sistem de recunoaștere facială, pixelii ar putea fi un strat al sistemului, în timp ce marginile ar putea fi altul, ochii ar putea fi altul, iar fața ar putea fi alta. Complexitatea metodelor de învățare profundă face ca utilizarea pachetelor existente să fie populară în comunitatea de programare. TensorFlow și PyTorch în Python sunt populare, la fel ca pachetul Keras din R. Cu toate acestea, în producția de sisteme de învățare profundă, performanța și siguranța sunt două probleme care determină companiile să aleagă limbaje de programare funcționale, cum ar fi Clojure și Haskell în schimb.
- dificultățile implementărilor de învățare profundă
- de ce programarea funcțională este mai potrivită pentru învățarea profundă
- Clojure
- puterea de programare concurente în Clojure
- paralelismul în Clojure este ieftin
- înlănțuirea funcțiilor în Clojure înseamnă claritate
- identitatea și starea în Clojure oferă siguranță
- Biblioteci și limitări
- Haskell
- siguranța tipului în Haskell oferă siguranță și flexibilitate
- Codul explicit simplu din Haskell oferă implementări clare
- paralelismul Multicore în Haskell oferă performanță
- Biblioteci și limitări
- concluzie
dificultățile implementărilor de învățare profundă
în punerea sistemelor de învățare profundă în producție, rețelele neuronale ar putea conține un milion de parametri. Datele pot exploda rapid pentru a instrui acești parametri. Această explozie de date necesită performanțe care pot fi obținute numai printr-un limbaj de programare eficient, cu capacități de concurență și paralelism sigure. Datorită complexității rețelelor neuronale, cu date transmise de la strat la strat, simplitatea și consistența în modul în care limbajul de programare gestionează aceste date sunt importante. Siguranța, în acest caz, înseamnă capacitatea de a păstra starea datelor originale într-o manieră consecventă, în timp ce simplitatea înseamnă a putea citi și menține baza de cod cu ușurință, maximizând performanța.
de ce programarea funcțională este mai potrivită pentru învățarea profundă
în încercarea de a rezolva unele dintre dificultățile care pot apărea la implementarea învățării profunde, programatorii constată că limbajele de programare funcționale pot oferi soluții.
în informatică, programarea funcțională este o paradigmă de programare care tratează calculul ca evaluare a funcțiilor matematice și evită schimbarea stării și a datelor mutabile. Este un model de programare care este mai aproape de gândirea matematică.
modelele de învățare profundă sunt în esență modele matematice. De exemplu, rețelele neuronale artificiale cuprind noduri conectate, fiecare efectuând operații matematice simple. Folosind un limbaj de programare funcțional, programatorii sunt capabili să descrie aceste operații matematice într-un limbaj mai apropiat de operațiile în sine. Modul explicit în care sunt scrise aceste programe face citirea și menținerea bazei de cod mult mai ușoară.
în același timp, natura compozițională a algoritmilor de învățare profundă înseamnă că, la fiecare strat al lucrării neuronale, straturile sau funcțiile tind să se unească pentru a îndeplini sarcini. Acest lucru poate fi implementat cu ușurință folosind înlănțuirea funcțională a unui limbaj de programare funcțional.
mai mult, în învățarea profundă, atunci când funcțiile sunt aplicate datelor, datele nu se schimbă. Valorile noi pot fi afișate secvențial pe linie, dar datele în sine rămân consecvente. Caracteristica imutabilitate a unui limbaj de programare funcțional va permite programatorului să creeze un nou set de date de fiecare dată când sunt generate noi valori fără a modifica setul de date imuabil original. Acest lucru facilitează menținerea consistenței datelor în întreaga rețea neuronală.
în cele din urmă, numărul mare de parametri și date de instruire implicate în implementarea învățării profunde înseamnă că paralelismul și concurența sunt cheile creării sistemelor de învățare profundă la nivel de producție. Paralelismul înseamnă rularea firelor pe diferite procesoare pentru a accelera procesul de învățare. Concurența înseamnă capacitatea de a gestiona firele pentru a evita conflictele. Programarea funcțională permite concurența și paralelismul fără costuri. Aceasta înseamnă că, prin natura sa, programarea funcțională, unde funcția pură este apatridă, va produce întotdeauna aceeași ieșire pentru o anumită intrare, va duce la capacitatea de a izola orice funcție și de a executa oricând doriți. Acest lucru face mult mai ușor de gestionat concurența și paralelismul. Nu trebuie să vă confruntați cu probleme precum blocaje și condiții de cursă. Diferite fire care accesează diferite procesoare vor putea rula independent fără contestații.
Clojure
cu programarea funcțională câștigând popularitate în învățarea profundă și cu pachetele robuste disponibile pentru învățarea profundă, Clojure este acum favorizat de companii precum Walmart și Facebook. Este un limbaj de programare funcțional la nivel înalt, dinamic, bazat pe limbajul de programare LISP și are compilatoare care fac posibilă rularea atât pe Java, cât și pe mediul de rulare.net.
puterea de programare concurente în Clojure
Clojure nu înlocuiește sistemul de fir Java, mai degrabă funcționează cu ea. Deoarece structurile de date de bază sunt imuabile, ele pot fi partajate ușor între fire. În același timp, schimbările de stat în program sunt posibile, dar Clojure oferă mecanisme pentru a se asigura că statele rămân consecvente. Dacă apar conflicte între 2 tranzacții care încearcă să modifice aceeași referință, una dintre ele se va retrage. Nu este nevoie de blocare explicită.
(import '(java.util.concurrent Executors))(defn test-stm (let (fn (dotimes (dosync (doseq (alter r + 1 t)))))) (range nthreads))] (doseq (.get future)) (.shutdown pool) (map deref refs)))(test-stm 10 10 10000) -> (550000 550000 550000 550000 550000 550000 550000 550000 550000 550000)
Sursa
paralelismul în Clojure este ieftin
în învățarea profundă, modelele trebuie să se antreneze pe cantități mari de date. Paralelismul implică rularea mai multor fire pe diferite procesoare. Paralelismul care este ieftin va însemna îmbunătățiri semnificative ale performanței. Utilizarea partiției împreună cu harta poate realiza paralelism care este mai puțin costisitor.
(defn calculate-pixels-2 (let (doall (map (fn (let (get-color (process-pixel (/ row (double *width*)) (/ col (double *height*)))))) x))) work)] (doall (apply concat result))))
sursă
înlănțuirea funcțiilor în Clojure înseamnă claritate
în Clojure, există multe funcții pentru foarte puține tipuri de date. Funcțiile pot fi, de asemenea, transmise ca argumente către alte funcții, ceea ce face posibilă înlănțuirea funcțiilor în învățarea profundă. Cu implementarea mai aproape de modelul matematic real, codul Clojure poate fi simplu de citit și de întreținut.
;; pipe arg to function(-> "x" f1) ; "x1";; pipe. function chaining(-> "x" f1 f2) ; "x12"
Sursa
identitatea și starea în Clojure oferă siguranță
în Clojure, identitatea fiecărui model are o stare în orice moment. Această stare este o adevărată valoare care nu se schimbă niciodată. Dacă identitatea pare să se schimbe, aceasta se datorează faptului că este asociată cu o stare diferită. Noile valori sunt funcții vechi. În interiorul fiecărui strat al rețelei neuronale, starea datelor originale este întotdeauna păstrată. Fiecare set de date cu valori noi care sunt ieșiri ale funcțiilor poate funcționa independent. Aceasta înseamnă că acțiunile pot fi efectuate asupra acestor seturi de date în condiții de siguranță sau fără a ține cont de contestații. Ne putem referi la starea inițială a datelor în orice moment. Prin urmare, coerența, în acest caz, înseamnă siguranță.
Biblioteci și limitări
istoric, biblioteca cortex machine learning conține tot ce aveți nevoie pentru a implementa algoritmi de învățare automată în Clojure. Odată cu recenta popularitate în creștere a cadrului mxnet open-source pentru învățarea profundă, este mai ușor să implementați învățarea profundă folosind API-ul MXNET-Clojure.
deși există acum API-uri diferite și biblioteci de învățare automată disponibile pentru Clojure, există încă o curbă abruptă de învățare pentru a deveni fluent în ea. Mesajele de eroare pot fi criptice, iar companiile vor trebui să fie dispuse să investească în avans pentru a-l utiliza pentru a-și extinde sistemele de învățare automată. Pe măsură ce mai multe exemple de sisteme gata de producție sunt scrise în Clojure, limba va câștiga mai multă popularitate în următorii ani, dar numai dacă numărul și dimensiunea bibliotecilor care însoțesc utilizarea Clojure crește constant.
Haskell
Haskell este un limbaj funcțional care este tastat static cu inferență de tip și evaluare leneșă. Se bazează pe semantica limbajului de programare Miranda și este considerat a fi mai expresiv, mai rapid și mai sigur pentru implementarea învățării automate.
siguranța tipului în Haskell oferă siguranță și flexibilitate
siguranța tipului definește constrângerile asupra tipurilor de valori pe care le poate deține o variabilă. Acest lucru va ajuta la prevenirea operațiunilor ilegale, va oferi o mai bună siguranță a memoriei și va duce la mai puține erori logice. Evaluarea leneșă înseamnă că Haskell va întârzia evaluarea unei expresii până când este necesară valoarea acesteia. De asemenea, evită evaluările repetate, ceea ce va economisi timp de funcționare. În același timp, evaluarea leneșă permite definirea structurilor de date infinite. Acest lucru oferă unui programator posibilități matematice nelimitate.
Codul explicit simplu din Haskell oferă implementări clare
unul dintre cele mai mari beneficii ale Haskell este că poate descrie algoritmi în construcții matematice foarte explicite. Puteți reprezenta un model în câteva linii de cod. De asemenea, puteți citi codul în același mod în care puteți citi o ecuație matematică. Acest lucru poate fi foarte puternic în algoritmi complexi, cum ar fi algoritmi de învățare profundă în învățarea automată. De exemplu, implementarea de mai jos a unui singur strat al unei rețele neuronale feed-forward arată cât de lizibil poate fi codul.
import Numeric.LinearAlgebra.Static.Backproplogistic :: Floating a => a -> alogistic x = 1 / (1 + exp (-x))feedForwardLog :: (KnownNat i, KnownNat o) => Model (L o i :& R o) (R i) (R o)feedForwardLog (w :&& b) x = logistic (w #> x + b)
Sursa
paralelismul Multicore în Haskell oferă performanță
în învățarea profundă, rețelele neuronale tipice vor conține un milion de parametri care definesc modelul. De asemenea, este necesară o cantitate mare de date pentru a învăța acești parametri, ceea ce, din punct de vedere computațional, consumă foarte mult timp. Pe o singură mașină, utilizarea mai multor nuclee pentru a partaja memoria și procesul în paralel poate fi foarte puternică atunci când vine vorba de implementarea învățării profunde. Cu toate acestea, în Haskell, implementarea paralelismului multicore este ușoară.
Biblioteci și limitări
Biblioteca Haskell hlearn conține implementări de algoritmi de învățare automată, în timp ce legarea tensor-flux pentru Haskell poate fi utilizată pentru învățarea profundă. Paralel și Concurentîntre timp, sunt utilizate pentru paralelism și concurență.
deși există unele biblioteci de învățare automată dezvoltate în Haskell, implementările de bază vor trebui totuși făcute pentru implementările Haskell pregătite pentru producție. În timp ce bibliotecile publice disponibile pentru sarcini specifice de învățare profundă și învățare automată sunt limitate, utilizarea Haskell în AI va fi, de asemenea, limitată. Companii precum Aetion Technologies și Credit Suisse Global Modeling and Analytics Group folosesc Haskell în implementările lor—iată o listă completă a organizațiilor care o utilizează.
concluzie
modelele de învățare profundă sunt modele matematice complexe care necesită stratificarea specifică a funcțiilor. Limbajele de programare funcționale, cum ar fi Clojure și Haskell, pot reprezenta adesea complexitatea cu un cod mai curat, care este mai aproape de matematica modelului. Acest lucru duce la economii de timp, eficiență și gestionare ușoară a bazei de cod. Proprietățile specifice ale programării funcționale permit implementărilor în aceste limbi să fie mai sigure decât cele ale altor limbi. Pe măsură ce dezvoltarea tehnologiei ia progresează, evaluarea acestor limbi pentru nevoile proiectelor de dezvoltare a sistemelor la scară largă în IA va deveni mai răspândită.
acest articol face parte din Behind the Code, media pentru dezvoltatori, de către dezvoltatori. Descoperiți mai multe articole și videoclipuri vizitând Behind the Code!
vrei să contribui? Fii publicat!
Urmați-ne pe Twitter pentru a rămâne la curent!
Ilustrație de Victoria Roussel