Die Schönheit funktionaler Sprachen im Deep Learning – Clojure und Haskell

Deep Learning ist eine Teilmenge von Methoden des maschinellen Lernens, die auf künstlichen neuronalen Netzen basieren. Diese sind inspiriert von Informationsverarbeitung und verteilten Kommunikationsknoten in biologischen Systemen wie dem Gehirn. Beim Deep Learning lernt jede Ebene, die Eingabedaten in eine etwas abstraktere und zusammengesetzte Darstellung umzuwandeln. In einem Gesichtserkennungssystem könnten beispielsweise Pixel eine Schicht des Systems sein, während Kanten eine andere sein könnten, Augen eine andere sein könnten und das Gesicht eine andere sein könnte. Die Komplexität von Deep-Learning-Methoden macht die Verwendung vorhandener Pakete in der Programmiergemeinschaft beliebt. TensorFlow und PyTorch in Python sind beliebt, ebenso wie das Keras-Paket in R. Bei der Produktion von Deep-Learning-Systemen sind Leistung und Sicherheit jedoch zwei Probleme, die Unternehmen dazu veranlassen, stattdessen funktionale Programmiersprachen wie Clojure und Haskell zu wählen.

image

Die Schwierigkeiten von Deep-Learning-Implementierungen

Bei der Produktion von Deep-Learning-Systemen können neuronale Netze eine Million Parameter enthalten. Daten können schnell explodieren, um diese Parameter zu trainieren. Diese Datenexplosion erfordert eine Leistung, die nur durch eine effiziente Programmiersprache mit sicheren Parallelitäts- und Parallelitätsfunktionen erreicht werden kann. Aufgrund der Komplexität neuronaler Netze, bei denen Daten von Schicht zu Schicht weitergegeben werden, ist die Einfachheit und Konsistenz der Art und Weise, wie die Programmiersprache mit diesen Daten umgeht, wichtig. Sicherheit bedeutet in diesem Fall die Fähigkeit, den Zustand der Originaldaten konsistent beizubehalten, während Einfachheit bedeutet, die Codebasis einfach lesen und pflegen zu können und gleichzeitig die Leistung zu maximieren.

Warum funktionale Programmierung besser für Deep Learning geeignet ist

Um einige der Schwierigkeiten zu lösen, die bei der Implementierung von Deep Learning auftreten können, stellen Programmierer fest, dass funktionale Programmiersprachen Lösungen bieten können.

In der Informatik ist die funktionale Programmierung ein Programmierparadigma, das die Berechnung als Auswertung mathematischer Funktionen behandelt und Zustandsänderungen und veränderliche Daten vermeidet. Es ist ein Programmiermuster, das dem mathematischen Denken näher kommt.

Deep-Learning-Modelle sind im Wesentlichen mathematische Modelle. Zum Beispiel umfassen künstliche neuronale Netze verbundene Knoten, von denen jeder einfache mathematische Operationen ausführt. Durch die Verwendung einer funktionalen Programmiersprache können Programmierer diese mathematischen Operationen in einer Sprache beschreiben, die den Operationen selbst näher kommt. Die explizite Art und Weise, wie diese Programme geschrieben werden, erleichtert das Lesen und Verwalten der Codebasis erheblich.

Gleichzeitig bedeutet die kompositorische Natur von Deep-Learning-Algorithmen, dass die Schichten oder Funktionen auf jeder Ebene der neuronalen Arbeit dazu neigen, sich zu verketten, um Aufgaben auszuführen. Dies kann leicht mit der funktionalen Verkettung einer funktionalen Programmiersprache implementiert werden.

image

Wenn beim Deep Learning Funktionen auf die Daten angewendet werden, ändern sich die Daten nicht. Neue Werte werden möglicherweise nacheinander ausgegeben, aber die Daten selbst bleiben konsistent. Die Unveränderlichkeitsfunktion einer funktionalen Programmiersprache ermöglicht es dem Programmierer, jedes Mal, wenn neue Werte generiert werden, einen neuen Datensatz zu erstellen, ohne den ursprünglichen unveränderlichen Datensatz zu ändern. Dies macht es einfacher, die Konsistenz der Daten im gesamten neuronalen Netzwerk aufrechtzuerhalten.

Schließlich bedeutet die große Anzahl von Parametern und Trainingsdaten, die an der Implementierung von Deep Learning beteiligt sind, dass Parallelität und Parallelität die Schlüssel zur Schaffung von Deep-Learning-Systemen auf Produktionsebene sind. Parallelität bedeutet, Threads auf verschiedenen CPUs auszuführen, um den Lernprozess zu beschleunigen. Parallelität bedeutet die Fähigkeit, Threads zu verwalten, um Konflikte zu vermeiden. Funktionale Programmierung ermöglicht Parallelität und Parallelität ohne Kosten. Dies bedeutet, dass funktionale Programmierung, bei der die reine Funktion zustandslos ist, von Natur aus immer die gleiche Ausgabe für eine bestimmte Eingabe erzeugt, dazu führt, dass jede Funktion isoliert und ausgeführt werden kann, wann immer Sie möchten. Dies macht Parallelität und Parallelität viel einfacher zu verwalten. Sie müssen sich nicht mit Problemen wie Deadlocks und Rennbedingungen befassen. Verschiedene Threads, die auf verschiedene CPUs zugreifen, können unabhängig voneinander ohne Konflikte ausgeführt werden.

Clojure

Mit der zunehmenden Popularität der funktionalen Programmierung im Deep Learning und den robusten Paketen für Deep Learning wird Clojure jetzt von Unternehmen wie Walmart und Facebook bevorzugt. Es ist eine dynamische funktionale Programmiersprache auf hoher Ebene, die auf der Programmiersprache LISP basiert, und verfügt über Compiler, die es ermöglichen, sowohl auf der Java- als auch auf der .NET-Laufzeitumgebung ausgeführt zu werden.

Die Möglichkeiten der gleichzeitigen Programmierung in Clojure

Clojure ersetzt das Java-Thread-System nicht, sondern arbeitet damit. Da die Kerndatenstrukturen unveränderlich sind, können sie problemlos zwischen Threads geteilt werden. Gleichzeitig sind Statusänderungen im Programm möglich, aber Clojure bietet Mechanismen, um sicherzustellen, dass Zustände konsistent bleiben. Wenn Konflikte zwischen 2 Transaktionen auftreten, die versuchen, dieselbe Referenz zu ändern, wird eine von ihnen zurückgezogen. Es ist kein explizites Sperren erforderlich.

(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)

Quelle

Parallelität in Clojure ist billig

Beim Deep Learning müssen Modelle mit großen Datenmengen trainieren. Parallelität bedeutet, dass mehrere Threads auf verschiedenen CPUs ausgeführt werden. Parallelität, die billig ist, bedeutet signifikante Leistungsverbesserungen. Durch die Verwendung von Partition in Verbindung mit map kann eine kostengünstigere Parallelität erreicht werden.

(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))))

Quelle

Verkettungsfunktionen in Clojure bedeuten Klarheit

In Clojure gibt es viele Funktionen für sehr wenige Datentypen. Funktionen können auch als Argumente an andere Funktionen übergeben werden, was die Verkettung von Funktionen im Deep Learning ermöglicht. Mit einer Implementierung, die näher am tatsächlichen mathematischen Modell liegt, kann Clojure-Code einfach zu lesen und zu warten sein.

;; pipe arg to function(-> "x" f1) ; "x1";; pipe. function chaining(-> "x" f1 f2) ; "x12"

Quelle

Identität und Status in Clojure bieten Sicherheit

In Clojure hat die Identität jedes Modells zu jedem Zeitpunkt einen Status. Dieser Zustand ist ein wahrer Wert, der sich nie ändert. Wenn sich die Identität zu ändern scheint, liegt dies daran, dass sie einem anderen Status zugeordnet ist. Neue Werte sind Funktionen des Alten. Innerhalb jeder Schicht des neuronalen Netzwerks bleibt der Zustand der Originaldaten immer erhalten. Jeder Datensatz mit neuen Werten, die Ausgänge von Funktionen sind, kann unabhängig voneinander arbeiten. Dies bedeutet, dass Aktionen für diese Datensätze sicher oder ohne Rücksicht auf Konflikte ausgeführt werden können. Wir können jederzeit auf den ursprünglichen Zustand der Daten zurückgreifen. Konsistenz bedeutet in diesem Fall Sicherheit.

Bibliotheken und Einschränkungen

Historisch gesehen enthält die Cortex Machine Learning Library alles, was Sie benötigen, um Algorithmen für maschinelles Lernen in Clojure zu implementieren. Mit der jüngsten steigenden Popularität des Open-Source-MXNet-Frameworks für Deep Learning ist es einfacher, Deep Learning mit der MXNet-Clojure-API zu implementieren.

Obwohl es jetzt verschiedene APIs und maschinelle Lernbibliotheken für Clojure gibt, gibt es immer noch eine steile Lernkurve, um fließend darin zu werden. Fehlermeldungen können kryptisch sein, und Unternehmen müssen bereit sein, im Voraus zu investieren, um ihre Systeme für maschinelles Lernen zu skalieren. Da mehr Beispiele für produktionsfertige Systeme in Clojure geschrieben werden, wird die Sprache in den kommenden Jahren an Popularität gewinnen, aber nur, wenn die Anzahl und Größe der Bibliotheken, die die Verwendung von Clojure begleiten, konstant wächst.

Haskell

Haskell ist eine funktionale Sprache, die statisch mit Typinferenz und verzögerter Auswertung typisiert ist. Es basiert auf der Semantik der Programmiersprache Miranda und gilt als expressiver, schneller und sicherer für die Implementierung von maschinellem Lernen.

Typensicherheit in Haskell bietet Sicherheit und Flexibilität

Typensicherheit definiert Einschränkungen für die Wertetypen, die eine Variable enthalten kann. Dies hilft, illegale Vorgänge zu verhindern, eine bessere Speichersicherheit zu gewährleisten und zu weniger Logikfehlern zu führen. Lazy evaluation bedeutet, dass Haskell die Auswertung eines Ausdrucks verzögert, bis sein Wert benötigt wird. Es vermeidet auch wiederholte Auswertungen, die Laufzeit sparen. Gleichzeitig können mit Lazy Evaluation unendlich viele Datenstrukturen definiert werden. Dies gibt einem Programmierer unbegrenzte mathematische Möglichkeiten.

Einfacher expliziter Code in Haskell bietet klare Implementierungen

Einer der größten Vorteile von Haskell ist, dass es Algorithmen in sehr expliziten mathematischen Konstrukten beschreiben kann. Sie können ein Modell in wenigen Codezeilen darstellen. Sie können den Code auch auf die gleiche Weise lesen, wie Sie eine mathematische Gleichung lesen können. Dies kann in komplexen Algorithmen wie Deep-Learning-Algorithmen im maschinellen Lernen sehr leistungsfähig sein. Die folgende Implementierung einer einzelnen Schicht eines Feed-Forward-neuronalen Netzwerks zeigt beispielsweise, wie lesbar der Code sein kann.

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)

Quelle

Multicore-Parallelität in Haskell bietet Leistung

Beim tiefen Lernen enthalten typische neuronale Netze eine Million Parameter, die das Modell definieren. Außerdem ist eine große Datenmenge erforderlich, um diese Parameter zu lernen, was rechnerisch sehr zeitaufwändig ist. Auf einem einzelnen Computer kann die parallele Verwendung mehrerer Kerne zur gemeinsamen Nutzung von Speicher und Prozess sehr leistungsfähig sein, wenn es um die Implementierung von Deep Learning geht. In Haskell ist die Implementierung von Multicore-Parallelität jedoch einfach.

Bibliotheken und Einschränkungen

Die HLearn-Bibliothek von Haskell enthält Implementierungen von Algorithmen für maschinelles Lernen, während die Tensor-Flow-Bindung für Haskell für Deep Learning verwendet werden kann. Parallel und Concurrent werden unterdessen für Parallelität und Parallelität verwendet.

Obwohl in Haskell einige Bibliotheken für maschinelles Lernen entwickelt wurden, müssen für produktionsreife Haskell-Implementierungen noch grundlegende Implementierungen durchgeführt werden. Während öffentliche Bibliotheken, die für bestimmte Deep-Learning- und Machine-Learning-Aufgaben verfügbar sind, begrenzt sind, wird auch die Verwendung von Haskell in der KI begrenzt sein. Unternehmen wie Ati Technologies und Credit Suisse Global Modeling and Analytics Group verwenden Haskell in ihren Implementierungen — hier ist eine vollständige Liste der Organisationen, die es verwenden.

Fazit

Deep-Learning-Modelle sind komplexe mathematische Modelle, die eine spezifische Schichtung von Funktionen erfordern. Funktionale Programmiersprachen wie Clojure und Haskell können die Komplexität oft mit saubererem Code darstellen, der näher an der Mathematik des Modells liegt. Dies führt zu Zeitersparnis, Effizienz und einfacher Verwaltung der Codebasis. Spezifische Eigenschaften der funktionalen Programmierung ermöglichen es, dass die Implementierungen in diesen Sprachen sicherer sind als in anderen Sprachen. Mit fortschreitender Entwicklung der KI-Technologie wird die Bewertung dieser Sprachen für die Anforderungen großer Systementwicklungsprojekte in der KI immer häufiger.

Dieser Artikel ist Teil von Behind the Code, die Medien für Entwickler, von Entwicklern. Entdecken Sie weitere Artikel und Videos unter Behind the Code!

Möchten Sie beitragen? Veröffentlicht werden!

Folgen Sie uns auf Twitter, um auf dem Laufenden zu bleiben!

Illustration von Victoria Roussel

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.