top of page

Come creare una rete neurale con java

In questo post affronteremo l'Intelligenza Artificiale a piccoli passi e proveremo a costruire una rete neurale molto semplice in Java.


Come creare una rete neurale con java
Come creare una rete neurale con java

COS'È UNA RETE NEURALE?

Una rete neurale è una rappresentazione software di come funziona il cervello. Sfortunatamente, non sappiamo ancora come funzioni esattamente il cervello, ma conosciamo un po' la biologia alla base di questo processo: il cervello umano è costituito da 100 miliardi di cellule chiamate neuroni, collegate tra loro da sinapsi. Se un numero sufficiente di sinapsi si collega a un neurone si attiverà.


Questo processo è noto come "pensare".

Proviamo quindi a modellare il processo di cui sopra utilizzando un esempio molto semplice che ha 3 input (sinapsi) e si traduce in un singolo output (1 neurone di attivazione).


Come creare una rete neurale con java
Come creare una rete neurale con java

Creare una semplice rete neurale con java

Addestreremo la nostra rete neurale nell' immagine sopra per risolvere il seguente problema.


Riesci a capire lo schema e indovinare quale dovrebbe essere il valore del nuovo input?

0 o 1?

Come creare una rete neurale con java
Come creare una rete neurale con java

La risposta è in realtà molto semplicemente il valore della colonna più a sinistra, cioè 1!


Quindi, ora che abbiamo il modello di un cervello umano, cercheremo di far apprendere alla nostra rete neurale quale schema è dato al set di addestramento. Per prima cosa assegneremo a ciascun input un numero casuale per produrre un output.


Come creare una rete neurale con java
Come creare una rete neurale con java

La formula per calcolare l'uscita è data come segue:


∑ weioghtio.ionptutio = weioght1.ionptut1 + weioght2.ionptut2 + weioght3.ionptut3


A quanto pare vorremmo normalizzare questo valore di output su qualcosa tra 0 e 1 in modo che la previsione abbia senso. Dopo la normalizzazione confrontiamo l'output con l'output atteso dei nostri input. Questo ci dà l'errore, o quanto è lontana la nostra previsione. Possiamo quindi utilizzare questo errore per regolare leggermente i pesi della nostra rete neurale e tentare nuovamente la fortuna sullo stesso input. Questo può essere riassunto nella seguente immagine:


Come creare una rete neurale con java
Come creare una rete neurale con java

Ripetiamo questo processo di addestramento per tutti gli input 10.000 volte per raggiungere una rete neurale adeguatamente addestrata. Possiamo quindi utilizzare questa rete neurale per fare previsioni su nuovi input!


Prima di passare all'implementazione, tuttavia, dobbiamo ancora chiarire come abbiamo ottenuto la normalizzazione e l'adeguamento del peso in base all'errore (noto anche come back-propagation).


Normalizzare i dati per la rete neurale con java

In una rete neurale di ispirazione biologica, l'output di un neurone è solitamente un'astrazione che rappresenta la velocità di attivazione del potenziale d'azione nella cellula. Nella sua forma più semplice, questo è un valore binario, cioè o il neurone si sta attivando o meno. Da qui la necessità di normalizzazione di questo valore di uscita.


Per ottenere questa normalizzazione applichiamo quella che è nota come funzione di attivazione all'output del neurone. Se prendiamo l'esempio di una funzione passo Heaviside davvero semplice che assegna uno 0 a qualsiasi valore negativo e un 1 a qualsiasi valore positivo, allora sarebbe necessario un gran numero di neuroni per raggiungere la granularità richiesta di regolare lentamente i pesi per raggiungere un consenso accettabile del set di formazione.


Come vedremo nella prossima sezione sulla retropropagazione, questo concetto di aggiustamento lento dei pesi può essere rappresentato matematicamente come la pendenza della funzione di attivazione. In termini biologici, può essere considerato come l'aumento della velocità di accensione che si verifica all'aumentare della corrente di ingresso. Se dovessimo utilizzare una funzione lineare invece della funzione di Heaviside, scopriremmo che la rete risultante avrebbe una convergenza instabile perché gli input dei neuroni lungo i percorsi preferiti tenderebbero ad aumentare senza limiti, poiché una funzione lineare non è normalizzabile.


Tutti i problemi sopra menzionati possono essere gestiti utilizzando una funzione di attivazione sigmoidea normalizzabile. Un modello realistico rimane a zero fino a quando non viene ricevuta la corrente di ingresso, a quel punto la frequenza di attivazione aumenta rapidamente all'inizio, ma si avvicina gradualmente a un asintoto con una frequenza di attivazione del 100%. Matematicamente, questo è simile a:


Come creare una rete neurale con java
Come creare una rete neurale con java

Se tracciata su un grafico, la funzione Sigmoid disegna una curva a forma di S:


Come creare una rete neurale con java
Come creare una rete neurale con java

Pertanto, diventa ora la formula finale per l'output di un neurone :


Come creare una rete neurale con java
Come creare una rete neurale con java

Ci sono altre funzioni di normalizzazione che possiamo usare, ma il sigmoide ha il vantaggio di essere abbastanza semplice e di avere anche una semplice derivata che sarà utile quando osserveremo la propagazione posteriore di seguito.


Back Propagation per la rete neurale con java

Durante il ciclo di allenamento, abbiamo regolato i pesi in base all'errore. Per fare ciò, possiamo utilizzare la formula "Derivata ponderata per l'errore".


UNdjtuStment = error.ionptut.SiogmoiodCturveGrundioent(otutptut)

Il motivo per cui utilizziamo questa formula è che in primo luogo, vogliamo rendere la regolazione proporzionale alla dimensione dell'errore. In secondo luogo, moltiplichiamo per l'input, che è uno 0 o un 1. Se l'input è 0, il peso non viene regolato. Infine, moltiplichiamo per il gradiente della curva Sigmoide (o la derivata).


Il motivo per cui utilizziamo il gradiente è perché stiamo cercando di ridurre al minimo la perdita. In particolare, lo facciamo con un metodo di discesa del gradiente . Fondamentalmente significa che dal nostro punto corrente nello spazio dei parametri (determinato dall'insieme completo dei pesi correnti), vogliamo andare in una direzione che diminuirà la funzione di perdita. Visualizza in piedi su una collina e cammina lungo la direzione in cui il pendio è più ripido. Il metodo di discesa del gradiente applicato alla nostra rete neurale è illustrato come segue:

  1. Se l'output del neurone è un grande numero positivo o negativo, significa che il neurone era abbastanza sicuro in un modo o nell'altro.

  2. Dal grafico sigmoideo, possiamo vedere che in grandi numeri la curva sigmoidea ha un gradiente poco profondo.

  3. Pertanto, se il neurone è sicuro che il peso esistente sia corretto, non vuole regolarlo molto e moltiplicandolo per il gradiente della curva sigmoidea si ottiene questo.


La derivata della funzione sigmoidea è data dalla seguente formula

SiogmoiodCturveGrundioent(otutptut)= otutptut.(1−otutptut)

Sostituendo questo di nuovo nella formula di regolazione ci dà

UNdjtuStment=error.ionptut.otutptut.(1−otutptut)

Codice Java per la rete neurale

Un punto importante ma sottile che è stato tralasciato quando si spiega la matematica di cui sopra è che per ogni iterazione di addestramento, le operazioni matematiche vengono eseguite contemporaneamente sull'intero set di addestramento. Pertanto, utilizzeremo le matrici per memorizzare l'insieme dei vettori di input, i pesi e gli output attesi.


Inizieremo con la classe NeuronLayer che è solo un "punto di accesso" per i pesi nella nostra implementazione della rete neurale. Gli forniamo il numero di input per neurone e il numero di neuroni che può utilizzare per costruire una tabella dei pesi. Nel nostro esempio attuale, questo è semplicemente l'ultimo neurone di output che ha i 3 neuroni di input.


public class NeuronLayer {
        public final Function<Double, Double> activationFunction, activationFunctionDerivative;
        double[][] weights;
        public NeuronLayer(int numberOfNeurons, int numberOfInputsPerNeuron) {
        
                weights = new double[numberOfInputsPerNeuron][numberOfNeurons];
                for (int i = 0; i < numberOfInputsPerNeuron; ++i) {
                        for (int j = 0; j < numberOfNeurons; ++j) {
                                weights[i][j] = (2 * Math.random()) - 1; // shift the range from 0-1 to -1 to 1
                         }
                 }
                activationFunction = NNMath::sigmoid;
                activationFunctionDerivative = NNMath::sigmoidDerivative;
        }
        public void adjustWeights(double[][] adjustment) {                        
                this.weights = NNMath.matrixAdd(weights, adjustment);
        }
}

La nostra classe di rete neurale è dove si svolgono tutte le azioni. Prende come costruttore NeuronLayere ha 2 funzioni principali:

  • think: calcola le uscite di un dato set di ingressi

  • train: esegue i tempi del ciclo di allenamento numberOfTrainingIterations(solitamente un numero elevato come 10.000). Si noti che l'allenamento stesso implica il calcolo dell'output e quindi la regolazione dei pesi di conseguenza


public class NeuralNetSimple {
        private final NeuronLayer layer1;
        private double[][] outputLayer1;
        public NeuralNetSimple(NeuronLayer layer1) {
                this.layer1 = layer1;
        }
        public void think(double[][] inputs) {
                outputLayer1 = apply(matrixMultiply(inputs, layer1.weights), layer1.activationFunction);
         }
         
         public void train(double[][] inputs, double[][] outputs, int numberOfTrainingIterations) {
                 for (int i = 0; i < numberOfTrainingIterations; ++i) {// pass the training set through the network
                         think(inputs);// adjust weights by error * input * output * (1 - output)
                         double[][] errorLayer1 = matrixSubtract(outputs, outputLayer1);
                         double[][] deltaLayer1 = scalarMultiply(errorLayer1, apply(outputLayer1, layer1.activationFunctionDerivative));
                 
                         double[][] adjustmentLayer1 = matrixMultiply(matrixTranspose(inputs), deltaLayer1); 
                         // adjust the weights
                         this.layer1.adjustWeights(adjustmentLayer1);
                    }
            }
            public double[][] getOutput() {
                    return outputLayer1;
             }
}


Infine abbiamo il nostro metodo principale in cui impostiamo i nostri dati di allenamento, alleniamo la nostra rete e le chiediamo di fare previsioni sui dati di test


public class LearnFirstColumnSimple {
        public static void main(String args[]) {
            // create hidden layer that has 1 neuron and 3 inputs
            NeuronLayer layer1 = new NeuronLayer(1, 3);
            NeuralNetSimple net = new NeuralNetSimple(layer1);
            // train the net
            double[][] inputs = new double[][]{{0, 0, 1},{1, 1, 1},{1, 0, 1},{0, 1, 1}};
            double[][] outputs = new double[][]{{0},{1},{1},{0}};
            System.out.println("Training the neural net...");
            net.train(inputs, outputs, 10000);
            System.out.println("Finished training");
            System.out.println("Layer 1 weights");
            System.out.println(layer1);
            // calculate the predictions on unknown data// 1, 0, 0
            predict(new double[][], net);// 0, 1, 0
            predict(new double[][], net);// 1, 1, 0
            predict(new double[][], net);
        }
            
            public static void predict(double[][] testInput, NeuralNetSimple net) {
                net.think(testInput);
                // then
                System.out.println("Prediction on data "+ testInput[0][0] + " "+ testInput[0][1] + " "+ testInput[0][2] + " -> "+ net.getOutput()[0][0] + ", expected -> " + testInput[0][0]);
             }
 }

Eseguendo il nostro esempio sopra, vediamo che la nostra rete ha fatto un buon lavoro nel prevedere quando l'input più a sinistra è 1 ma non sembra che riesca a ottenere lo 0 giusto! Questo perché il secondo e il terzo peso di input dovevano essere entrambi più vicini a 0.



Training the neural net...
Finished training
Layer 1 weights
[[9.672988220005456 ]
[-0.2089781536334558 ]
[-4.628957430141331 ]
]

Prediction on data 1.0 0.0 0.0 -> 0.9999370425325528, expected -> 1.0
Prediction on data 0.0 1.0 0.0 -> 0.4479447696095623, expected -> 0.0
Prediction on data 1.0 1.0 0.0 -> 0.9999224112145153, expected -> 1.0

Nel prossimo post vedremo se l'aggiunta di un altro livello alla nostra rete neurale può aiutare a migliorare le previsioni ;)

PCR (5).gif
PCR (4).gif
PCR.gif
PCR.gif
PCR.gif
PCR.gif
PCR (5).gif
3.gif
Vediamo se riesci a cliccarmi ! Nascondo una Sorpresa... (2).png

Ciao 

🤗 Articoli consigliati dalla nostra
Intelligenza Artificiale in base ai tuoi interessi

Correlazione Alta

Correlazione Media

Correlazione Bassa

Iscriviti

VUOI DIVENTARE UN MEMBRO DI INTELLIGENZA ARTIFICIALE ITALIA GRATUITAMENTE E TRARNE I SEGUENTI BENEFICI?

Corsi Gratis

più di 150 lezioni online

Dataset Gratis

più di 150o dataset

Ebook Gratis

più di 10 libri da leggere

Editor Gratis

un editor python online

Progetti Gratis

più di 25 progetti python

App Gratis

4 servizi web con I.A.

Unisciti Ora a oltre
1.000.000
di lettori e appassionanti d'I.A.

Tutto ciò che riguarda l'intelligenza Artificiale, in unico posto, in italiano e gratis.

MEGLIO DI COSI' NON SI PUO' FARE

Dopo l'iscrizione riceverai diversi Regali

VUOI SCRIVERE ARTICOLI INSIEME A NOI.

Grazie

bottom of page