Condividi tramite


Il presente articolo è stato tradotto automaticamente.

Esecuzione di test

Approfondimenti sulle reti neurali

James McCaffrey

Scaricare il codice di esempio

James McCaffreyUna rete neurale artificiale (denominata in genere solo una rete neurale) è un'astrazione vagamente modellata su sinapsi e neuroni biologici.Sebbene le reti neurali sono state studiate per decenni, molte implementazioni di codice di rete neurale su Internet sono non, a mio parere, spiega molto bene. Nell'articolo di questo mese, mi spiegare che cosa sono le reti neurali artificiali e presentare il codice c# che implementa una rete neurale.

Il modo migliore per vedere dove sono diretto è quello di dare un'occhiata a Figura 1 e Figura 2. Uno dei modi di pensare a reti neurali è di considerare loro meccanismi di ingresso-uscita numeriche. La rete neurale in Figura 1 ha tre ingressi con etichettati x 0, x 1 e 2 x, con valori, 1.0, 2.0 e 3.0, rispettivamente. La rete neurale ha due uscite etichettati y0 e y1, con valori 0,72 e-0.88, rispettivamente. La rete neurale in Figura 1 ha uno strato di cosiddetti neuroni nascosti e può essere descritta come una rete di tre strati, completamente collegati, feedforward con tre ingressi, due uscite e quattro i neuroni nascosti. Purtroppo, terminologia di rete neurale varia un po. In questo articolo, sarà generalmente — ma non sempre — utilizzare la terminologia descritta nella rete neurale eccellente FAQ al bit.ly/wfikTI.

Neural Network Structure
Figura 1 struttura di rete neurale

Neural Network Demo Program

Figura 2 il programma Demo rete neurale**

Figura 2 mostra l'output prodotto dal programma demo presentato in questo articolo. La rete neurale utilizza una funzione di attivazione sigmoidea e una funzione di attivazione tanh. Queste funzioni sono suggerite da due equazioni con il phi lettere greche in Figura 1. Le uscite prodotte da una rete neurale dipendono i valori di un set di pesi numerici e pregiudizi. In questo esempio, ci sono un totale di 26 pesi e pregiudizi con valori 0.10, 0,20... -5.00. Dopo che i valori di peso e bias vengono caricati nella rete neurale, il programma carica i tre valori di input (1.0, 2.0, 3.0) e quindi esegue una serie di calcoli, come suggerito dai messaggi circa le somme ingresso nascoste e le somme nascoste--output. Il programma si conclude visualizzando i valori di due output (0.72,-0.88).

Io sarò a piedi attraverso il programma che ha prodotto l'output mostrato nella Figura 2. Questa colonna presuppone che avete abilità di programmazione intermedie, ma non assumere che si sa nulla di reti neurali. Il programma di dimostrazione è codificato mediante il linguaggio c#, ma non si dovrebbe avere alcuna difficoltà refactoring del codice demo in un'altra lingua, ad esempio Visual Basic.NET o Python. Il programma presentato in questo articolo è essenzialmente un tutorial e una piattaforma per la sperimentazione; esso non direttamente risolvere qualsiasi problema pratico, quindi verrà spiegato come è possibile espandere il codice per risolvere i problemi significativi. Penso che troverai le informazioni molto interessanti, e alcune delle tecniche di programmazione può essere preziose aggiunte alla tua codifica insieme di abilità.

Modellazione di una rete neurale

Concettualmente, reti neurali artificiali sono modellate sul comportamento delle reti neurali reale biologiche. In Figura 1 i cerchi rappresentano i neuroni cui elaborazione si verifica e le frecce rappresentano il flusso di informazioni sia chiamati pesi di valori numerici. In molte situazioni, i valori di input vengono copiati direttamente nei neuroni ingresso senza alcuna ponderazione ed emessi direttamente senza qualsiasi elaborazione, così la prima azione reale si verifica nei neuroni livello nascosto. Si supponga che i valori di input 1.0, 2.0 e 3.0 vengono emessi dai neuroni ingresso. Se si esamina Figura 1, si può vedere una freccia che rappresenta un valore di peso compreso tra ciascuno dei tre neuroni ingresso e ciascuno dei quattro neuroni nascosti. Supponiamo che i tre peso frecce mostrato che punta in alto neurone nascosto sono denominati w00, w10 e w20. In questa notazione primo indice rappresenta l'indice del neurone ingresso sorgente e il secondo indice rappresenta l'indice del neurone destinazione nascosto. Elaborazione del neurone si verifica in tre passaggi. Il primo passo, viene calcolata una somma pesata. Si supponga che w00 = 0.1, w10 = 0,5 e w20 = 0.9. È la somma pesata per neurone nascosto superiore (1.0)(0.1) + (2.0)(0.5) + (3.0)(0.9) = 3.8. La seconda fase di elaborazione consiste nell'aggiungere un valore di sbieco. Si supponga che il valore di polarizzazione è-2.0; allora la somma pesata rettificata diventa 3.8 + (-2.0) = 1,8. Il terzo passo è quello di applicare una funzione di attivazione alla somma pesata rettificata. Supponiamo che la funzione di attivazione è la funzione sigmoidea definita da 1.0 / (1.0 * Exp(-x)), dove Exp rappresenta la funzione esponenziale. L'uscita dal neurone nascosto diventa 1.0 / (1.0 * Exp(-1.8)) = 0,86. Questo output diventa quindi parte della somma ponderata di input in ciascuno dei neuroni del livello di uscita. In Figura 1, l'equazione con il phi lettera greca suggerisce questo processo in tre fasi: ponderata somme (xw) vengono calcolate, viene aggiunto un bias (b) e viene applicata una funzione di attivazione (phi).

Dopo tutto nascosto neurone i valori sono stati calcolati, uscita valori neurone strato vengono calcolati nello stesso modo. La funzione di attivazione utilizzata per calcolare i valori di output neurone possono essere la stessa funzione utilizzati nel calcolo i valori dei neuroni nascosti o una funzione di attivazione diversi può essere utilizzata. Il programma di dimostrazione mostrato in esecuzione in Figura 2 utilizza la funzione di tangente iperbolica come la funzione di attivazione nascosto all'uscita. Dopo ogni neurone strato uscita i valori calcolati, nella maggior parte dei casi questi valori non sono ponderati o trasformati, ma sono semplicemente emessa come i valori di output finale della rete neurale.

Struttura interna

La chiave per comprendere l'implementazione di reti neurali qui presentato è quello di esaminare attentamente Figura 3, che, a prima vista, potrebbe apparire estremamente complicata. Ma sopportarmi — la figura è quasi non complessa come potrebbe apparire in primo luogo. Figura 3 mostra un totale di otto matrici e due matrici. La prima matrice viene indicato come this.inputs. Questa matrice contiene i valori di input rete neurale, che sono 1.0, 2.0 e 3.0 in questo esempio. Successiva è l'insieme dei valori di peso che vengono utilizzati per calcolare i valori nel cosiddetto livello nascosto. Questi pesi sono memorizzati in una matrice 3 x 4 con l'etichetta h i pesi dove i-h sta per ingresso nascosto. Si noti che nel Figura 1 che la rete neurale demo ha quattro i neuroni nascosti. La matrice pesi i-h ha un numero di righe pari al numero di ingressi e un numero di colonne uguale al numero di neuroni nascosti.


Neural Network Internal Structure

Figura 3 rete neurale struttura interna

La matrice con etichettata somme i-h è una matrice vuota, utilizzata per il calcolo. Nota che la lunghezza dei-h riassume la matrice sarà sempre lo stesso numero di neuroni nascosti (quattro, in questo esempio). Successiva è una matrice con etichettata h i pregiudizi. Pregiudizi di rete neurale sono usati per calcolare i neuroni nascosti e output strato supplementare dei pesi. La lunghezza della matrice h i pregiudizi sarà lo stesso come la lunghezza della matrice di i-h somme, che a sua volta è lo stesso numero di neuroni nascosti.

La matrice con etichettata uscite i-h è un risultato intermedio e i valori in questo array vengono utilizzati come input per il livello successivo. La matrice di somme i-h ha lunghezza pari al numero di neuroni nascosti.

Successiva è una matrice con etichettata h-o pesi dove l'h-o sta per uscita nascosta. Qui la matrice h-o pesi ha dimensioni 4 x 2, perché ci sono quattro i neuroni nascosti e due uscite. La matrice di h-o somme, la matrice di h-o pregiudizi e la matrice di this.outputs hanno lunghezze pari al numero di uscite (in questo esempio due).

Con l'etichetta della matrice pesi nella parte inferiore della Figura 3 detiene tutti i pesi ingresso nascosto e nascosta--output e pregiudizi. In questo esempio, la lunghezza della matrice pesi è (3 * 4) + 4 + (4 * 2) + 2 = 26. In generale, se Ni è il numero di valori di input, Nh è il numero di neuroni nascosti e non è il numero di uscite, allora sarà la lunghezza della matrice pesi Nw = (Ni * Nh) + Nh + (Nh * No) + n.

Le uscite di informatica

Dopo aver creati le matrici di otto e due matrici descritti nella sezione precedente, una rete neurale può calcolare il suo output basato sul suo input, pesi e pregiudizi. Il primo passo è quello di copiare i valori di input nella matrice this.inputs. Il passo successivo consiste nell'assegnare i valori nella matrice pesi. Ai fini di una dimostrazione che è possibile utilizzare i valori di peso come. Successivamente, i valori della matrice pesi vengono copiati la matrice h i pesi, la matrice h i pregiudizi, la matrice h-o pesi e la matrice h-o pregiudizi. Figura 3 dovrebbe fare questo rapporto chiaro.

I valori della matrice di i-h somme vengono calcolati in due fasi. Il primo passo è quello di calcolare le somme ponderate moltiplicando i valori nella matrice ingressi dai valori nella colonna appropriata della matrice h i pesi. Ad esempio, la somma pesata per neurone nascosto [3] (dove sto usando indicizzazione in base zero) utilizza ogni valore e i valori nella colonna [3] della matrice h i pesi di input: (1.0)(0.4) + (2.0)(0.8) + (3.0)(1.2) = 5.6. Il secondo passo quando calcolo valori somma i-h è quello di aggiungere ogni valore di sbieco con il valore corrente di somma i-h. Ad esempio, perché i-h biases [3] ha valore-7.0, il valore di i-h somme [3] diventa 5.6 + (-7.0) = dell'1,4.

Dopo che tutti i valori della matrice di i-h somme sono stati calcolati, viene applicata la funzione di attivazione ingresso nascosto a tali somme per produrre i valori di input nascosto output. Ci sono molte funzioni di attivazione possibili. La funzione di attivazione più semplice viene chiamata la funzione di passaggio, che semplicemente restituisce 1.0 per qualsiasi valore di input maggiore di zero e restituisce 0.0 per qualsiasi valore di input inferiore o uguale a zero. Un'altra funzione di attivazione comune e quella utilizzata in questo articolo, è la funzione sigmoidea, che viene definita come f (x) = 1.0 / (1.0 * Exp(-x)). Il grafico della funzione sigmoidea è mostrato in Figura 4.

The Sigmoid Function
Figura 4 la funzione sigmoidea

Si noti che la funzione sigmoidea restituisce un valore nel campo strettamente maggiore di zero e strettamente minore di uno. In questo esempio, se il valore di i-h [3] le somme dopo che è stato aggiunto il valore di polarizzazione è dell'1,4, allora il valore di output i-h [3] diventa 1.0 / (1.0 * Exp(-(-1.4))) = 0.20.

Dopo che tutti i valori del neurone di uscita ingresso nascoste sono stati calcolati, quei valori servono come gli ingressi per i calcoli di neurone strato nascosto all'uscita. Questi calcoli funzionano nello stesso modo come i calcoli di input nascosto: somme ponderate preliminari sono calcolati, pregiudizi vengono aggiunti e quindi viene applicata la funzione di attivazione. In questo esempio io uso la funzione tangente iperbolica, abbreviata come tanh, per la funzione di attivazione nascosto all'uscita. La funzione tanh è strettamente correlata alla funzione sigmoidea. Il grafico della funzione tanh ha una curva a forma di s simile alla funzione sigmoidea, ma tanh restituisce un valore nell'intervallo (-1,1) anziché nell'intervallo (0,1).

Combinazione di pesi e pregiudizi

Tutte le implementazioni di rete neurale che ho visto su Internet non mantenere separato peso e matrici di sbieco, ma invece combinare pesi e pregiudizi in matrice pesi. Come è possibile? Ricordiamo che il calcolo del valore di input nascosto neurone [3] somigliava (i0 * w03) + (i1 * w13) + (i2 * w23) + b3, dove i0 è il valore di input [0], w03 è il peso per l'input [0] e [3] neurone e b3 è il valore di sbieco per neurone nascosto [3]. Se si crea un ulteriore, falso input [4] che ha un valore fittizio di 1.0 e diventa una riga aggiuntiva dei pesi che contengono i valori di sbieco, quindi il calcolo descritto in precedenza: (i0 * w03) + (i1 * w13) + (i2 * w23) + (i3 * motore), dove i3 è il valore di input 1.0 fittizio e motore è il pregiudizio. L'argomento è che questo approccio semplifica il modello di rete neurale. Io non sono d'accordo. A mio parere, che unisce i pesi e pregiudizi rende un modello di rete neurale, più difficile da comprendere e più soggetta ad attuare. Tuttavia, a quanto pare sono l'unico autore che sembra avere questo parere, così si dovrebbe fare la propria decisione di progettazione.

Implementazione

Ho implementato la rete neurale mostrata in figure 1, 2 e 3 utilizzando Visual Studio 2010. Ho creato un'applicazione di console c# denominata NeuralNetworks. Nella finestra Esplora selezionata sul file Program. cs e la ribattezzò a NeuralNetworksProgram.cs, cambiato anche il nome della classe modello generato in NeuralNetworksProgram. La struttura complessiva del programma, con la maggior parte delle istruzioni WriteLine rimosso, è mostrata in Figura 5.

Figura 5 struttura del programma rete neurale

using System;
namespace NeuralNetworks
{
  class NeuralNetworksProgram
  {
    static void Main(string[] args)
    {
      try
      {
        Console.WriteLine("\nBegin Neural Network demo\n");
        NeuralNetwork nn = new NeuralNetwork(3, 4, 2);
        double[] weights = new double[] {
          0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.1, 1.2,
          -2.0, -6.0, -1.0, -7.0,
          1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2.0,
          -2.5, -5.0 };
        nn.SetWeights(weights);
        double[] xValues = new double[] { 1.0, 2.0, 3.0 };
        double[] yValues = nn.ComputeOutputs(xValues);
        Helpers.ShowVector(yValues);
        Console.WriteLine("End Neural Network demo\n");
      }
      catch (Exception ex)
      {
        Console.WriteLine("Fatal: " + ex.Message);
      }
    }
  }
  class NeuralNetwork
  {
    // Class members here
    public NeuralNetwork(int numInput, int numHidden, int numOutput) { ...
}
    public void SetWeights(double[] weights) { ...
}
    public double[] ComputeOutputs(double[] xValues) { ...
}
    private static double SigmoidFunction(double x) { ...
}
    private static double HyperTanFunction(double x) { ...
}
  }
  public class Helpers
  {
    public static double[][] MakeMatrix(int rows, int cols) { ...
}
    public static void ShowVector(double[] vector) { ...
}
    public static void ShowMatrix(double[][] matrix, int numRows) { ...
}
  }
} // ns

Ho cancellato tutto il modello generato utilizzando le istruzioni ad eccezione di quello che fa riferimento a spazio dei nomi System. Nella funzione Main, dopo la visualizzazione di un messaggio di inizio, istanziare un oggetto NeuralNetwork denominato nn con tre ingressi, i neuroni nascosti quattro e due uscite. Successivamente, assegnare 26 pesi arbitrari e pregiudizi a una matrice denominata pesi. Caricare i pesi in oggetto rete neurale utilizzando un metodo denominato SetWeights. Assegnare valori 1.0, 2.0 e 3.0 a una matrice denominata xValues. Utilizzare il metodo ComputeOutputs per caricare i valori di input in rete neurale e determinare gli output, che io recuperare in una matrice denominata yValues. La demo conclude visualizzando i valori di output.

Classe NeuralNetwork

Inizia la definizione della classe NeuralNetwork:

class NeuralNetwork
{
  private int numInput;
  private int numHidden;
  private int numOutput;
...

Come spiegato nelle sezioni precedenti, la struttura di una rete neurale è determinata dal numero di valori di input, il numero di neuroni livello nascosto e il numero di valori di output. La definizione della classe continua come:

private double[] inputs;
private double[][] ihWeights; // input-to-hidden
private double[] ihSums;
private double[] ihBiases;
private double[] ihOutputs;
private double[][] hoWeights;  // hidden-to-output
private double[] hoSums;
private double[] hoBiases;
private double[] outputs;
...

Queste sette matrici e due matrici corrispondono a quelli mostrati Figura 3. Io uso un prefisso ih per i dati di input nascosto e un ho del prefisso per dati nascosti di output. Ricordiamo che i valori della matrice ihOutputs serviscono come gli ingressi per i calcoli di strato di uscita, così denominazione precisamente questo array è un po ' fastidioso.

Figura 6 mostra come viene definito il costruttore della classe NeuralNetwork.

Nella figura 6, il costruttore della classe NeuralNetwork

public NeuralNetwork(int numInput, int numHidden, int numOutput)
{
  this.
numInput = numInput;
  this.
numHidden = numHidden;
  this.
numOutput = numOutput;
  inputs = new double[numInput];
  ihWeights = Helpers.MakeMatrix(numInput, numHidden);
  ihSums = new double[numHidden];
  ihBiases = new double[numHidden];
  ihOutputs = new double[numHidden];
  hoWeights = Helpers.MakeMatrix(numHidden, numOutput);
  hoSums = new double[numOutput];
  hoBiases = new double[numOutput];
  outputs = new double[numOutput];
}

Dopo aver copiato il parametro di input valori numInput, numHidden e numOutput nei loro campi rispettivi classe, ciascuna delle matrici di nove membri e matrici vengono allocate le dimensioni spiegato in precedenza. Implementare matrici come matrici di matrici, piuttosto che utilizzando il tipo di matrice multidimensionale di c#, in modo che si può più facilmente il refactoring mio codice su una lingua che non supporta i tipi matrice multidimensionale. Perché ogni riga della mia matrici deve essere allocato, è conveniente usare un metodo di supporto, ad esempio MakeMatrix.

Il metodo SetWeights accetta una matrice di pesi e pregiudizi valori e popolano ihWeights, ihBiases, hoWeights e hoBiases. Il metodo inizia come questo:

public void SetWeights(double[] weights)
{
  int numWeights = (numInput * numHidden) +
    (numHidden * numOutput) + numHidden + numOutput;
  if (weights.Length != numWeights)
    throw new Exception("xxxxxx");
  int k = 0;
...

Come spiegato in precedenza, il numero totale di pesi e polarizzazioni, Nw, in un feedforward completamente collegati è rete neurale (Ni * Nh) + (Nh * No) + Nh + n. Faccio un semplice controllo per vedere se il parametro di matrice pesi ha la lunghezza corretta. Qui, "xxxxxx" è una controfigura per un messaggio di errore descrittivi. Successivamente, inizializzare un indice k variabile all'inizio del parametro di matrice pesi. Si conclude il metodo SetWeights:

for (int i = 0; i < numInput; ++i)
  for (int j = 0; j < numHidden; ++j)
    ihWeights[i][j] = weights[k++];
for (int i = 0; i < numHidden; ++i)
  ihBiases[i] = weights[k++];
for (int i = 0; i < numHidden; ++i)
  for (int j = 0; j < numOutput; ++j)
    hoWeights[i][j] = weights[k++];
for (int i = 0; i < numOutput; ++i)
  hoBiases[i] = weights[k++]
}

Ogni valore nel parametro matrice pesi viene copiato in modo sequenziale nel ihWeights, ihBiases, hoWeights e hoBiases. Avviso no i valori vengono copiati in ihSums o hoSums, perché quei due graffiare matrici vengono utilizzati per il calcolo.

Le uscite di informatica

Il cuore della classe NeuralNetwork è il metodo ComputeOutputs. Il metodo è sorprendentemente breve e semplice e comincia:

public double[] ComputeOutputs(double[] xValues)
{
  if (xValues.Length != numInput)
    throw new Exception("xxxxxx");
  for (int i = 0; i < numHidden; ++i)
    ihSums[i] = 0.0;
  for (int i = 0; i < numOutput; ++i)
    hoSums[i] = 0.0;
...

In primo luogo verificare se la lunghezza della matrice di valori di input x è la dimensione corretta per l'oggetto NeuralNetwork. Poi azzerare le matrici ihSums e hoSums. Se ComputeOutputs viene chiamato solo una volta, quindi questa inizializzazione esplicita non è necessaria, ma se ComputeOutputs viene chiamato più di una volta — perché ihSums e hoSums sono valori accumulati — l'inizializzazione esplicita è assolutamente necessario. Un approccio alternativo design è quello di non dichiarare e allocare ihSums e hoSums come membri della classe, ma anziché fare loro locale al metodo ComputeOutputs. Metodo ComputeOutputs continua:

for (int i = 0; i < xValues.Length; ++i)
  this.inputs[i] = xValues[i];
for (int j = 0; j < numHidden; ++j)
  for (int i = 0; i < numInput; ++i)
    ihSums[j] += this.inputs[i] * ihWeights[i][j];
...

Il membro della classe ingressi matrice vengono copiati i valori del parametro di matrice xValues. In alcuni scenari di rete neurale, i valori di parametro di input vengono normalizzati, ad esempio eseguendo una trasformazione lineare in modo che tutti gli input vengono ridimensionati tra -1.0 e +1.0, ma qui non viene eseguita alcuna normalizzazione. Successivamente, un ciclo nidificato calcola gli importi ponderati come mostrato in figure 1 e 3. Si noti che al fine di indice ihWeights nella forma standard dove indicizzare i è l'indice di riga e indice j è l'indice di colonna, è necessario avere j nel ciclo esterno. Metodo ComputeOutputs continua:

for (int i = 0; i < numHidden; ++i)
  ihSums[i] += ihBiases[i];
for (int i = 0; i < numHidden; ++i)
  ihOutputs[i] = SigmoidFunction(ihSums[i]);
...

Ogni somma pesata viene modificata aggiungendo il valore appropriato di sbieco. A questo punto, per produrre l'output mostrato Figura 2, ho usato il metodo Helpers.ShowVector per visualizzare i valori correnti della matrice ihSums. Successivamente, applicare la funzione sigmoidea a ciascuno dei valori in ihSums e assegnare i risultati a matrice ihOutputs. Sarà presente il codice per il metodo SigmoidFunction poco. Metodo ComputeOutputs continua:

for (int j = 0; j < numOutput; ++j)
  for (int i = 0; i < numHidden; ++i)
    hoSums[j] += ihOutputs[i] * hoWeights[i][j];
for (int i = 0; i < numOutput; ++i)
  hoSums[i] += hoBiases[i];
...

Io uso i valori calcolati solo in ihOutputs e i pesi in hoWeights per calcolare i valori in hoSums, poi aggiungere i valori appropriati di sbieco nascosta--output. Ancora una volta, per produrre l'output mostrato Figura 2, ho chiamato Helpers.ShowVector. Metodo ComputeOutputs termina:

for (int i = 0; i < numOutput; ++i)
    this.outputs[i] = HyperTanFunction(hoSums[i]);
  double[] result = new double[numOutput];
  this.outputs.CopyTo(result, 0);
  return result;
}

Applicare il metodo HyperTanFunction per l'hoSums per generare gli output finali in classe matrice membro privato uscite. Io copiare tali output in una matrice di risultato locale e utilizzare tale array come valore restituito. Una scelta di progettazione alternativa sarebbe quella di implementare ComputeOutputs senza un valore restituito, ma implementare un metodo pubblico GetOutputs, in modo che le uscite dell'oggetto rete neurale potrebbero essere recuperate.

Le funzioni di attivazione e i metodi di supporto

Ecco il codice per la funzione sigmoidea utilizzato per calcolare le uscite di input nascosto:

private static double SigmoidFunction(double x)
{
  if (x < -45.0) return 0.0;
  else if (x > 45.0) return 1.0;
  else return 1.0 / (1.0 + Math.Exp(-x));
}

Perché alcune implementazioni della funzione Math.Exp possono produrre overflow aritmetico, verifica il valore del parametro di input viene solitamente eseguita. Il codice per la funzione tanh utilizzato per calcolare i risultati di uscita nascosta è:

private static double HyperTanFunction(double x)
{
  if (x < -10.0) return -1.0;
  else if (x > 10.0) return 1.0;
  else return Math.Tanh(x);
}

La funzione di tangente iperbolica restituisce i valori tra -1 e + 1, quindi overflow aritmetico non è un problema. Qui il valore di input viene controllato semplicemente per migliorare le prestazioni.

I metodi di utilità statico nella classe Helpers sono appena codificazione convenienze. Il metodo MakeMatrix utilizzato per allocare matrici nel costruttore NeuralNetwork alloca ogni riga di una matrice implementata come una matrice di matrici:

public static double[][] MakeMatrix(int rows, int cols)
{
  double[][] result = new double[rows][];
  for (int i = 0; i < rows; ++i)
    result[i] = new double[cols];
  return result;
}

Metodi ShowVector e ShowMatrix visualizzano i valori in una matrice o di matrice nella console. È possibile visualizzare il codice per questi due metodi nel download del codice che accompagna questo articolo (disponibile presso archive.msdn.microsoft.com/mag201205TestRun).

Passaggi successivi

Il codice presentato qui dovrebbe darvi una solida base per capire e sperimentare con le reti neurali. Si desidera esaminare gli effetti della utilizzando le funzioni di attivazione diversi e variando il numero di ingressi, uscite e neuroni livello nascosto. È possibile modificare la rete neurale rendendolo parzialmente connesso, dove alcuni neuroni non sono logicamente collegate ai neuroni nel livello successivo. La rete neurale, presentata in questo articolo ha un livello nascosto. È possibile creare più complesse reti neurali che hanno due o più livelli nascosti, e si potrebbe voler estendere il codice presentato qui per implementare tale rete neurale.

Reti neurali possono essere utilizzate per risolvere una serie di problemi pratici, compresi problemi di classificazione. Al fine di risolvere tali problemi ci sono diverse sfide. Ad esempio, è necessario sapere come codificare dati non numerici e come addestrare una rete neurale per trovare il miglior set di pesi e pregiudizi. Presenterò un esempio di utilizzo di reti neurali per la classificazione in un prossimo articolo.

Dr. James McCaffrey* lavora per Volt Information Sciences Inc., dove gestisce la formazione tecnica per gli ingegneri software presso il campus di Redmond, Washington, di Microsoft. Egli ha lavorato su diversi prodotti di Microsoft, tra cui Internet Explorer e MSN Search. Egli è l'autore di ".NET Test Automation Recipes"(Apress, 2006) e può essere raggiunto a jammc@microsoft.com.*

Grazie ai seguenti esperti tecnici Microsoft per la revisione di questo articolo: Dan Liebling e Anne Loomis Thompson