En guide til deeplearning4j

1. Introduktion

I denne artikel opretter vi et simpelt neuralt netværk med deeplearning4j (dl4j) biblioteket - et moderne og kraftfuldt værktøj til maskinindlæring.

Før vi kommer i gang, ikke at denne vejledning ikke kræver en dyb viden om lineær algebra, statistik, maskinlæringsteori og mange andre emner, der er nødvendige for en velbegrundet ML-ingeniør.

2. Hvad er dyb læring?

Neurale netværk er beregningsmodeller, der består af sammenkoblede lag af noder.

Noder er neuronlignende processorer af numeriske data. De tager data fra deres input, anvender nogle vægte og funktioner på disse data og sender resultaterne til output. Et sådant netværk kan trænes med nogle eksempler på kildedataene.

Træning er i det væsentlige at gemme en numerisk tilstand (vægte) i noderne, som senere påvirker beregningen. Træningseksempler kan indeholde dataelementer med funktioner og visse kendte klasser af disse emner (for eksempel “dette sæt med 16 × 16 pixels indeholder et håndskrevet bogstav“ a ”).

Når træningen er afsluttet, et neuralt netværk kanudlede information fra nye data, selvom den ikke har set disse bestemte dataelementer før. Et velmodelleret og veluddannet netværk kan genkende billeder, håndskrevne breve, tale, behandle statistiske data for at producere resultater til forretningsinformation og meget mere.

Dybe neurale netværk blev mulige i de seneste år med fremskridt med højtydende og parallel computing. Sådanne netværk adskiller sig fra simple neurale netværkde består af flere mellemliggende (eller skjulte) lag. Denne struktur giver netværk mulighed for at behandle data på en meget mere kompliceret måde (på en rekursiv, tilbagevendende, konvolutionsmåde osv.) Og udtrække meget mere information fra den.

3. Opsætning af projektet

For at bruge biblioteket har vi brug for mindst Java 7. På grund af nogle indbyggede komponenter fungerer det kun med 64-bit JVM-versionen.

Før vi starter med guiden, lad os kontrollere, om kravene er opfyldt:

$ java -version java version "1.8.0_131" Java (TM) SE Runtime Environment (build 1.8.0_131-b11) Java HotSpot (TM) 64-bit Server VM (build 25.131-b11, blandet tilstand)

Lad os først tilføje de nødvendige biblioteker til vores Maven pom.xml fil. Vi udtrækker versionen af ​​biblioteket til en ejendomspost (for den nyeste version af bibliotekerne skal du tjekke Maven Central-arkivet):

 0.9.1 org.nd4j nd4j-native-platform $ {dl4j.version} org.deeplearning4j deeplearning4j-core $ {dl4j.version} 

Noter det nd4j-native-platform afhængighed er en af ​​de mange tilgængelige implementeringer.

Det er afhængigt af indfødte biblioteker, der er tilgængelige for mange forskellige platforme (macOS, Windows, Linux, Android osv.). Vi kunne også skifte backend til nd4j-cuda-8.0-platform, hvis vi ville udføre beregninger på et grafikkort, der understøtter CUDA-programmeringsmodel.

4. Klargøring af data

4.1. Forberedelse af datasætfilen

Vi skriver “Hello World” for maskinindlæring - klassificering af datasættet med irisblomster. Dette er et sæt data, der blev samlet fra blomster af forskellige arter (Iris setosa, Iris versicolorog Iris virginica).

Disse arter adskiller sig i længder og bredder af kronblade og bægerblade. Det ville være svært at skrive en præcis algoritme, der klassificerer et inputdataelement (dvs. bestemmer hvilken art hører en bestemt blomst til). Men et veluddannet neuralt netværk kan klassificere det hurtigt og med små fejl.

Vi skal bruge en CSV-version af disse data, hvor kolonner 0..3 indeholder de forskellige funktioner i arten, og kolonne 4 indeholder klassen for posten eller arten kodet med værdien 0, 1 eller 2:

5.1,3.5,1.4,0.2,0 4.9,3.0,1.4,0.2,0 4.7,3.2,1.3,0.2,0 … 7.0,3.2,4.7,1.4,1 6.4,3.2,4.5,1.5,1 6.9,3.1,4.9,1.5,1 …

4.2. Vektorisering og læsning af data

Vi koder klassen med et tal, fordi neurale netværk arbejder med tal. Transformation af virkelige dataelementer til serie af tal (vektorer) kaldes vektorisering - deeplearning4j bruger datavec-biblioteket til at gøre dette.

Lad os først bruge dette bibliotek til at indtaste filen med de vektoriserede data. Når du opretter CSVRecordReader, kan vi specificere antallet af linjer, der skal springes over (for eksempel hvis filen har en overskriftslinje) og separatorsymbolet (i vores tilfælde et komma):

prøv (RecordReader recordReader = ny CSVRecordReader (0, ',')) {recordReader.initialize (ny FileSplit (ny ClassPathResource ("iris.txt"). getFile ())); //…}

For at gentage arkiverne kan vi bruge en hvilken som helst af de mange implementeringer af DataSetIterator interface. Datasættene kan være ret massive, og evnen til at side eller cache værdierne kan komme til nytte.

Men vores lille datasæt indeholder kun 150 poster, så lad os læse alle data i hukommelsen på én gang med et opkald af iterator.next ().

Vi specificerer også indekset for klassekolonnen som i vores tilfælde er det samme som antal funktioner (4) og det samlede antal klasser (3).

Bemærk også, at vi er nødt til at blande datasættet for at slippe af med klassebestilling i den originale fil.

Vi specificerer et konstant tilfældigt frø (42) i stedet for standard System.currentTimeMillis () ring, så resultatet af blandingen altid vil være den samme. Dette giver os mulighed for at få stabile resultater hver gang vi kører programmet:

DataSetIterator iterator = ny RecordReaderDataSetIterator (recordReader, 150, FEATURES_COUNT, CLASSES_COUNT); DataSet allData = iterator.next (); allData.shuffle (42);

4.3. Normalisering og opdeling

En anden ting, vi skal gøre med dataene inden træning, er at normalisere dem. Normaliseringen er en tofaseproces:

  • indsamling af nogle statistikker om dataene (fit)
  • ændre (transformere) dataene på en eller anden måde for at gøre det ensartet

Normalisering kan variere for forskellige typer data.

For eksempel, hvis vi vil behandle billeder i forskellige størrelser, skal vi først indsamle størrelsesstatistikkerne og derefter skalere billederne til en ensartet størrelse.

Men for tal betyder normalisering normalt at omdanne dem til en såkaldt normalfordeling. Det Normaliser Standardiser klasse kan hjælpe os med det:

DataNormalization normalizer = ny NormalizerStandardize (); normalizer.fit (allData); normalizer.transform (allData);

Nu hvor dataene er klargjort, er vi nødt til at opdele sættet i to dele.

Den første del vil blive brugt i et træningssession. Vi bruger den anden del af dataene (som netværket slet ikke ser) til at teste det uddannede netværk.

Dette giver os mulighed for at kontrollere, at klassificeringen fungerer korrekt. Vi tager 65% af dataene (0,65) til træningen og efterlader resten 35% til testningen:

SplitTestAndTrain testAndTrain = allData.splitTestAndTrain (0,65); DataSet trainingData = testAndTrain.getTrain (); DataSet testData = testAndTrain.getTest ();

5. Forberedelse af netværkskonfigurationen

5.1. Flydende konfigurationsbygger

Nu kan vi oprette en konfiguration af vores netværk med en fancy flydende bygherre:

MultiLayerConfiguration konfiguration = ny NeuralNetConfiguration.Builder () .iterations (1000) .aktivering (Activation.TANH) .weightInit (WeightInit.XAVIER) .learningRate (0.1) .regularization (true) .l2 (0.0001) .list () .layer ( 0, nyt DenseLayer.Builder (). NIn (FEATURES_COUNT) .nOut (3) .build ()) .layer (1, nyt DenseLayer.Builder (). NIn (3) .nOut (3) .build ()). lag (2, nyt OutputLayer.Builder (LossFunctions.LossFunction.NEGATIVELOGLIKELIHOOD) .aktivering (Activation.SOFTMAX) .nIn (3) .nOut (CLASSES_COUNT) .build ()). backprop (true). pretrain (false) .build (build) );

Selv med denne forenklede flydende måde at opbygge en netværksmodel på, er der meget at fordøje og mange parametre at tilpasse. Lad os nedbryde denne model.

5.2. Indstilling af netværksparametre

Det iterationer () builder-metoden angiver antallet af optimerings-iterationer.

Den iterative optimering betyder at udføre flere pass på træningssættet, indtil netværket konvergerer til et godt resultat.

Normalt, når vi træner på ægte og store datasæt, bruger vi flere epoker (komplette overførsler af data gennem netværket) og en iteration for hver periode. Men da vores oprindelige datasæt er minimalt, bruger vi en epoke og flere iterationer.

Det aktivering () er en funktion, der kører inde i en node for at bestemme dens output.

Den enkleste aktiveringsfunktion ville være lineær f (x) = x. Men det viser sig, at kun ikke-lineære funktioner tillader netværk at løse komplekse opgaver ved hjælp af et par noder.

Der er mange forskellige aktiveringsfunktioner til rådighed, som vi kan slå op i org.nd4j.linalg.activations.Activation enum. Vi kunne også skrive vores aktiveringsfunktion, hvis det var nødvendigt. Men vi bruger den medfølgende hyperbolske tangens (tanh) funktion.

Det vægtInit () metode angiver en af ​​de mange måder at indstille de oprindelige vægte til netværket på. Korrekte startvægte kan dybt påvirke træningsresultaterne. Uden at gå for meget ind i matematikken, lad os indstille den til en form for gaussisk fordeling (WeightInit.XAVIER), da dette normalt er et godt valg til en start.

Alle andre metoder til initialisering af vægt kan slås op i org.deeplearning4j.nn.weights.WeightInit enum.

Læringsgrad er en afgørende parameter, der dybt påvirker netværkets evne til at lære.

Vi kunne bruge meget tid på at tilpasse denne parameter i en mere kompleks sag. Men til vores enkle opgave bruger vi en ret betydelig værdi på 0,1 og sætter den op med learningRate () byggemetode.

Et af problemerne med træning af neurale netværk er et tilfælde af overmontering når et netværk "husker" træningsdataene.

Dette sker, når netværket indstiller for høje vægte til træningsdataene og producerer dårlige resultater på andre data.

For at løse dette problem skal vi konfigurere l2-regulering med linjen .regularization (true) .l2 (0.0001). Regularisering “straffer” netværket for for store vægte og forhindrer overmontering.

5.3. Opbygning af netværkslag

Dernæst opretter vi et netværk af tætte (også kendt som fuldt forbundne) lag.

Det første lag skal indeholde den samme mængde noder som kolonnerne i træningsdataene (4).

Det andet tætte lag indeholder tre noder. Dette er den værdi, vi kan variere, men antallet af output i det forrige lag skal være det samme.

Det endelige outputlag skal indeholde antallet af noder, der matcher antallet af klasser (3). Netværkets struktur er vist på billedet:

Efter vellykket træning har vi et netværk, der modtager fire værdier via sine indgange og sender et signal til en af ​​sine tre udgange. Dette er en simpel klassifikator.

Endelig, for at afslutte opbygningen af ​​netværket, satte vi tilbage propagering (en af ​​de mest effektive træningsmetoder) og deaktiverer præ-træning med linjen .backprop (true) .pretrain (false).

6. Oprettelse og træning af et netværk

Lad os nu oprette et neuralt netværk fra konfigurationen, initialisere og køre det:

MultiLayerNetwork model = nyt MultiLayerNetwork (konfiguration); model.init (); model.fit (trainingData);

Nu kan vi teste den uddannede model ved hjælp af resten af ​​datasættet og kontrollere resultaterne med evalueringsmålinger for tre klasser:

INDArray output = model.output (testData.getFeatureMatrix ()); Evalueringseval = ny evaluering (3); eval.eval (testData.getLabels (), output);

Hvis vi nu udskriver eval.stats (), vi ser, at vores netværk er ret godt til at klassificere irisblomster, selvom det fejlagtigt tog klasse 1 til klasse 2 tre gange.

Eksempler mærket som 0 klassificeret efter model som 0: 19 gange Eksempler mærket som 1 klassificeret efter model som 1: 16 gange Eksempler mærket som 1 klassificeret efter model som 2: 3 gange Eksempler mærket som 2 klassificeret efter model som 2: 15 gange == ========================= Resultater ========================== =============== Antal klasser: 3 Nøjagtighed: 0,9434 Præcision: 0,9444 Tilbagekaldelse: 0,9474 F1 Score: 0,9411 Præcision, tilbagekaldelse & F1: makro-gennemsnit (lige vægtet gennemsnit af 3 klasser ) ==================================================== =========================

Den flydende konfigurationsbygger giver os mulighed for hurtigt at tilføje eller ændre lag på netværket eller tilpasse nogle andre parametre for at se, om vores model kan forbedres.

7. Konklusion

I denne artikel har vi bygget et simpelt, men alligevel kraftigt neuralt netværk ved hjælp af deeplearning4j-biblioteket.

Som altid er kildekoden til artiklen tilgængelig på GitHub.


$config[zx-auto] not found$config[zx-overlay] not found