Introduzione a Neuroph

1. Introduzione

Questo articolo dà un'occhiata a Neuroph, una libreria open source per la creazione di reti neurali e l'utilizzo dell'apprendimento automatico.

Nell'articolo, diamo uno sguardo ai concetti fondamentali e diversi esempi su come metterli insieme.

2. Neuroph

Possiamo interagire con Neuroph usando:

  • uno strumento basato su GUI
  • una libreria Java

Entrambi gli approcci si basano su una gerarchia di classi sottostante che costruisce reti neurali artificiali a partire da strati di neuroni .

Ci concentreremo sul lato programmatico, ma faremo riferimento a diverse classi condivise dall'approccio basato su GUI di Neuroph per chiarire cosa stiamo facendo.

Per ulteriori informazioni sull'approccio basato sulla GUI, dai un'occhiata alla documentazione di Neuroph.

2.1. Dipendenze

Se si desidera utilizzare Neuroph, è necessario aggiungere la seguente voce Maven:

 org.beykery neuroph 2.92 

La versione più recente può essere trovata su Maven Central.

3. Classi e concetti chiave

Tutti i blocchi di costruzione concettuali di base utilizzati hanno classi Java corrispondenti.

I neuroni sono collegati ai livelli che vengono quindi raggruppati in reti neurali . Le reti neurali vengono successivamente addestrate utilizzando LearningRules e DataSet .

3.1. Neurone

La classe Neuron ha quattro attributi principali:

  1. inputConnection: connessioni pesate tra neuroni
  2. inputFunction: specifica i pesi e le somme vettoriali applicate ai dati di connessione in entrata
  3. transferFunction: specifica i pesi e le somme vettoriali applicate ai dati in uscita

  4. output: il valore di output risultante dall'applicazione di transferFunctions e inputFunctions a una inputConnection

Insieme, questi quattro attributi primari stabiliscono il comportamento:

output = transferFunction(inputFunction(inputConnections));

3.2. Strato

Strati sono essenzialmente gruppi di neuroni tale che ogni Neuro n nel layer è (di solito) collegato solo con neuroni nei precedenti e successivi strati .

I livelli , quindi, passano informazioni tra di loro attraverso le funzioni ponderate che esistono sui loro neuroni .

I neuroni possono essere aggiunti ai livelli:

Layer layer = new Layer(); layer.addNeuron(n);

3.3. Rete neurale

La superclasse di livello superiore NeuralNetwork è sottoclasse in diversi tipi familiari di reti neurali artificiali tra cui reti neurali convoluzionali (sottoclasse ConvolutionalNetwork ), reti neurali di Hopfield (sottoclasse Hopfield ) e reti neurali perceptron multistrato (sottoclasse MultilayerPerceptron ).

Tutte le reti neurali sono composte da strati che di solito sono organizzati in una tricotomia:

  1. livelli di input
  2. strati nascosti
  3. livelli di output

Se stiamo usando il costruttore di una sottoclasse di NeuralNetwork (come Perceptron ), possiamo passare i Layer , il numero di Neuron per ogni Layer e il loro indice usando questo semplice metodo:

NeuralNetwork ann = new Perceptron(2, 4, 1);

A volte vorremo farlo manualmente (ed è bello vedere cosa sta succedendo sotto il cofano). L'operazione di base per aggiungere un livello a una rete neurale viene eseguita in questo modo:

NeuralNetwork ann = new NeuralNetwork(); Layer layer = new Layer(); ann.addLayer(0, layer); ann.setInputNeurons(layer.getNeurons()); 

Il primo argomento specifica l'indice del livello nella rete neurale ; il secondo argomento specifica il livello stesso. I livelli aggiunti manualmente devono essere collegati utilizzando la classe ConnectionFactory :

ann.addLayer(0, inputLayer); ann.addLayer(1, hiddenLayerOne); ConnectionFactory.fullConnect(ann.getLayerAt(0), ann.getLayerAt(1));

Anche il primo e l'ultimo Layer dovrebbero essere collegati:

ConnectionFactory.fullConnect(ann.getLayerAt(0), ann.getLayerAt(ann.getLayersCount() - 1), false); ann.setOutputNeurons(ann.getLayerAt( ann.getLayersCount() - 1).getNeurons());

Ricorda che la forza e la potenza di una rete neurale dipendono in gran parte da:

  1. il numero di livelli nella rete neurale
  2. il numero di neuroni in ogni strato (e le funzioni ponderate tra di loro), e
  3. l'efficacia degli algoritmi di addestramento / accuratezza del DataSet

3.4. Formazione della nostra rete neurale

Le reti neurali vengono addestrate utilizzando le classi DataSet e LearningRule .

DataSet viene utilizzato per rappresentare e fornire le informazioni da apprendere o utilizzare per addestrare la rete neurale . I DataSet sono caratterizzati dalla loro dimensione di input, outputize e righe (DataSetRow).

int inputSize = 2; int outputSize = 1; DataSet ds = new DataSet(inputSize, outputSize); DataSetRow rOne = new DataSetRow(new double[] {0, 0}, new double[] {0}); ds.addRow(rOne); DataSetRow rTwo = new DataSetRow(new double[] {1, 1}, new double[] {0}); ds.addRow(rTwo);

LearningRule specifica il modo in cui il DataSet viene insegnato o addestrato da NeuralNetwork . Le sottoclassi di LearningRule includono BackPropagation e SupervisedLearning .

NeuralNetwork ann = new NeuralNetwork(); //... BackPropagation backPropagation = new BackPropagation(); backPropagation.setMaxIterations(1000); ann.learn(ds, backPropagation);

4. Mettere tutto insieme

Now let's put those building blocks together into a real example. We're going to start by combining several layers together into the familiar input layer, hidden layer, and output layer pattern exemplified by most neural network architectures.

4.1. Layers

We'll assemble our NeuralNetwork by combining four layers. Our goal is to build a (2, 4, 4, 1) NeuralNetwork.

Let's first define our input layer:

Layer inputLayer = new Layer(); inputLayer.addNeuron(new Neuron()); inputLayer.addNeuron(new Neuron());

Next, we implement hidden layer one:

Layer hiddenLayerOne = new Layer(); hiddenLayerOne.addNeuron(new Neuron()); hiddenLayerOne.addNeuron(new Neuron()); hiddenLayerOne.addNeuron(new Neuron()); hiddenLayerOne.addNeuron(new Neuron());

And hidden layer two:

Layer hiddenLayerTwo = new Layer(); hiddenLayerTwo.addNeuron(new Neuron()); hiddenLayerTwo.addNeuron(new Neuron()); hiddenLayerTwo.addNeuron(new Neuron()); hiddenLayerTwo.addNeuron(new Neuron());

Finally, we define our output layer:

Layer outputLayer = new Layer(); outputLayer.addNeuron(new Neuron()); 

4.2. NeuralNetwork

Next, we can put them together into a NeuralNetwork:

NeuralNetwork ann = new NeuralNetwork(); ann.addLayer(0, inputLayer); ann.addLayer(1, hiddenLayerOne); ConnectionFactory.fullConnect(ann.getLayerAt(0), ann.getLayerAt(1)); ann.addLayer(2, hiddenLayerTwo); ConnectionFactory.fullConnect(ann.getLayerAt(1), ann.getLayerAt(2)); ann.addLayer(3, outputLayer); ConnectionFactory.fullConnect(ann.getLayerAt(2), ann.getLayerAt(3)); ConnectionFactory.fullConnect(ann.getLayerAt(0), ann.getLayerAt(ann.getLayersCount()-1), false); ann.setInputNeurons(inputLayer.getNeurons()); ann.setOutputNeurons(outputLayer.getNeurons());

4.3. Training

For training purposes, let's put together a DataSet by specifying the size of both the input and resulting output vector:

int inputSize = 2; int outputSize = 1; DataSet ds = new DataSet(inputSize, outputSize);

We add an elementary row to our DataSet adhering to the input and output constraints defined above – our goal in this example is to teach our network to do basic XOR (exclusive or) operations:

DataSetRow rOne = new DataSetRow(new double[] {0, 1}, new double[] {1}); ds.addRow(rOne); DataSetRow rTwo = new DataSetRow(new double[] {1, 1}, new double[] {0}); ds.addRow(rTwo); DataSetRow rThree = new DataSetRow(new double[] {0, 0}, new double[] {0}); ds.addRow(rThree); DataSetRow rFour = new DataSetRow(new double[] {1, 0}, new double[] {1}); ds.addRow(rFour);

Next, let's train our NeuralNetwork with the built in BackPropogation LearningRule:

BackPropagation backPropagation = new BackPropagation(); backPropagation.setMaxIterations(1000); ann.learn(ds, backPropagation); 

4.4. Testing

Now that our NeuralNetwork is trained up let's test it out. For each pair of logical values passed into our DataSet as a DataSetRow, we run the following kind of test:

ann.setInput(0, 1); ann.calculate(); double[] networkOutputOne = ann.getOutput(); 

An important thing to remember is that NeuralNetworks only output a value on the inclusive interval of 0 and 1. To output some other value, we must normalize and denormalize our data.

In this case, for logical operations, 0 and 1 are perfect for the job. The output will be:

Testing: 1, 0 Expected: 1.0 Result: 1.0 Testing: 0, 1 Expected: 1.0 Result: 1.0 Testing: 1, 1 Expected: 0.0 Result: 0.0 Testing: 0, 0 Expected: 0.0 Result: 0.0 

We see that our NeuralNetwork successfully predicts the right answer!

5. Conclusion

We've just reviewed the basic concepts and classes used by Neuroph.

Further information on this library is available here, and the code examples used in this article can be found over on GitHub.