Condividi tramite


Il presente articolo è stato tradotto automaticamente.

Esecuzione di test

Reti di funzione radiale di base per i programmatori

James McCaffrey

Scaricare il codice di esempio

James McCaffreyBase radiale funzione (RBF) reti sono sistemi software che hanno alcune somiglianze con le reti neurali. Una rete RBF accetta uno o più input numerico valori, quali (1.0 -2.0, 3.0) e genera uno o più valori di output numerici, come ad esempio (4.6535, 9.4926). Reti RBF (a volte chiamati reti radiali) possono essere utilizzati per classificare i dati e fare previsioni. Ad esempio, una rete RBF potrebbe essere usata per predire i punteggi delle due squadre di calcio che sono programmati per giocare a vicenda, basate su dati storici come l'attuale percentuale di vincita di ogni squadra, vantaggio domestico del campo (-1,0 o + 1.0) e così via. O una rete RBF potrebbe essere usata per classificare il rischio del paziente di un ospedale di cancro (basso = 0, alto = 1) sulla base dei valori dei risultati dei test medici e altri fattori quali l'età e il sesso.

A mio parere, reti RBF sono tra i più affascinanti di tutte le tecniche di apprendimento automatico. Ma anche se ci sono molte pubblicazioni che spiegano la matematica dietro queste reti complessa, ci sono pochissime risorse che spiegano la loro prospettiva del programmatore. Questo articolo descrive esattamente le reti RBF, spiegare come si calcola il loro uscite e illustrano con un completo programma di ingresso-uscita demo rete RBF.

Il modo migliore per vedere dove è diretto questo articolo è quello di dare un'occhiata al programma demo in Figura 1. La demo crea una rete RBF, configura, invia un vettore d'ingresso con valori di (1.0 -2.0, 3.0) e visualizza il vettore di uscita di (4.6535, 9.4926). La demo inizia creando un 3-4-2 rete RBF. 3 Indica che un input alla rete radiale ha tre valori numerici. Il 2 indica che ci sono due uscite numeriche. Il 4 indica che la rete radiale ha quattro nodi di elaborazione interna, nascosta.

Radial Basis Function Network Input-Output Demo
Figura 1 base radiale funzione rete Input-Output Demo

Rete radiale richiede quattro serie di configurazione infor­zione, solitamente indicato come i parametri del sistema. Il primo parametro è un insieme di cosiddetti centroidi. Centroidi sono a volte chiamati mezzi. Nella demo, i centroidi sono (-3,0, -3,5 -3,8), (-1,0, -1.5, 1.8), (2.0, 2.5, 2.8) e (4.0, 4.5, 4.8). Notare che c'è un centro per ogni nodo nascosto e che ciascun centroide ha lo stesso numero di valori come un vettore di ingresso. Poiché lo scopo della demo è solo per spiegare come reti RBF calcolare la loro uscita, piuttosto che risolvere un problema realistico, il numero dei nodi nascosti (quattro) è artificialmente piccolo, e i valori dei quattro centroidi sono arbitrari.

Il secondo set di parametri per la demo net radiale è quattro deviazioni standard. Deviazioni standard sono a volte chiamati larghezze RBF. I valori nella demo sono 2.22, 3,33, 4.44 e 5,55. C'è una deviazione standard per ogni unità di elaborazione di nodo nascosto. Ancora una volta, i valori utilizzati nella demo sono valori fittizi e non corrispondono a un problema reale.

Il terzo set di parametri di rete RBF è i pesi. Si noti che nel Figura 1 che ci sono otto pesi. Se un netto RBF ha nodi nascosti di j e k nodi di uscita, ci sarà j * pesi k. Qui, il 4 * 2 = 8 pesi sono 5.0, -5,1,-5.2, 5.3, 5,4, 5.5, 5.6 e 5,7. Come gli altri insiemi di parametri, questi valori sono puramente arbitrari e sono solo a scopo dimostrativo.

Il quarto set di parametri rete radiali nella demo è due valori di bias. Il numero di valori di bias in una rete RBF è uguale al numero di valori di output, due in questo caso. I due valori di bias fittizi sono 7.0 e 7.1.

Dopo il caricamento quattro insiemi dei valori di parametro in rete RBF, input (1.0 -2.0, 3.0) è alimentato a rete. Utilizzando il parametro valori, un'uscita di (4.6535, 9.4926) è calcolata. Il programma demo visualizza alcuni valori intermedi del calcolo — una distanza e uscita per ogni nodo nascosto — ma questi valori intermedi sono indicati solo per aiutare a comprendere il meccanismo di entrata-uscita RBF e normalmente non vengono visualizzati.

Questo articolo presuppone che si hanno competenze di programmazione di livello almeno intermedio con un linguaggio C-famiglia, ma non si assume che si sa nulla di reti funzione radiale di base. Il programma demo è codificato utilizzando c#, ma lei non dovrebbe avere problemi il mio codice a un altro linguaggio, ad esempio Python o Windows PowerShelldi refactoring. Ho rimosso controllo errori più normale per mantenere le idee chiare. Tutto il codice chiave per il programma demo è presentato in questo articolo, ma ho omesso alcuni dei metodi di visualizzazione supporto. Il codice sorgente completo per la demo è disponibile presso archive.msdn.microsoft.com/mag201310TestRun.

Il meccanismo di Input-Output RBF

Per capire il meccanismo di entrata-uscita RBF, date un'occhiata al diagramma in Figura 2. I valori del diagramma corri­corrispondono a quelli nel programma demo. Elaborazione si verifica in due passaggi principali. In primo luogo, un vettore d'ingresso viene inviato a ciascun nodo nascosto e ogni nodo nascosto indipendentemente calcola un valore di output intermedi. In secondo luogo, i valori di output intermedio di nodi nascosti vengono combinati per produrre i valori di output finale del sistema.

Radial Basis Function Network ArchitectureArchitettura di rete funzione radiale di base figura 2

Il diagramma in Figura 2 utilizza l'indicizzazione in base zero per tutti gli oggetti. L'uscita di un nodo nascosto j espressa matematicamente, è:

Questa equazione è un esempio di quello che ha chiamato la funzione gaussiana e quando il grafico è una curva caratteristica a forma di campana. L'icona a forma di campana nella parte superiore di ogni nodo nascosto indica che il nodo nascosto uscite vengono calcolate utilizzando una funzione gaussiana.

L'equazione è più semplice di quanto può sembrare inizialmente. Phi greca lettera rappresenta l'output di un nodo nascosto, j in questo caso. La x rappresenta l'input. Qui x è un vettore (1,0, -2.0, 3.0) piuttosto che un singolo valore scalare. Minuscolo e è la costante di Eulero (2,71828...) ed e elevato a qualche valore corrisponde alla funzione Exp disponibile nella maggior parte dei linguaggi di programmazione. Il mu lettera greca è il vettore del centroide jth. La coppia di simboli doppia barra, quando applicato alla differenza tra due vettori, equivale a una notazione abbreviata per la funzione di distanza euclidea, che è la radice quadrata della somma delle differenze al quadrato tra i componenti dei due vettori. Il sigma lettera greca rappresenta la deviazione standard del jth nodo nascosto.

Illustrerò come output intermedio del nodo nascosto più in basso [3] è calcolato utilizzando i valori del programma demo. L'ingresso x è (1.0 -2.0, 3.0). Il centroide, mu, è (4.0, 4.5, 4.8). La deviazione standard, sigma, è 5,55. Ricordare che questi valori sono puramente arbitrarie e a scopo dimostrativo e non corrispondono a un problema reale.

La distanza tra x e mu è Sqrt ((1.0-4.0) ^ 2 + (-2.0-4.5) ^ 2 + (3.0-4.8) ^ 2) = Sqrt (9.00 + 42,25 + 3,24) = 7.3817 come mostrato Figura 1. La distanza al quadrato è 54.49. Si noti che la squadratura operazione Annulla l'operazione di radice quadrata della formula di distanza, il che significa che l'equazione potrebbe hai semplificato un po'.

Mettendo insieme tutti i valori, l'output per il nodo nascosto 3 è Exp (-54.49 / (2 * (5,55) ^ 2)) = Exp(-0.88451) = 0.4129, che è il valore indicato in Figura 1.

I valori di output per i nodi nascosti, 0, 1 e 2 sono calcolati nello stesso modo e sono 0,0014, 0.2921 e 0.5828, rispettivamente.

Ora ti mostrerò come le uscite di nascosto-nodo sono combinate per produrre le uscite di rete RBF finale. Uscita su una rete RBF è la somma ponderata dei prodotti di tutte le uscite di nodo nascosto volte un peso associato, più un valore finale di sbieco. Espresso in un'equazione, il valore di uscita nodo k è:

Qui, k è l'indice di un nodo di uscita (0, 1 nella demo); j è l'indice di un nodo nascosto (0, 1, 2, 3); e M è il numero di nodi nascosti. Il termine w(jk) è il peso da nodo nascosto j a k nodo di uscita. Il termine b(k) è il valore di bias associato con uscita k.

Ad esempio, se h0, h1, h2, e h3 (utilizzando la lettera più facile-a-tipo h invece phi lettera greca) sono le uscite dei nodi nascosti da 0 a 3, allora l'output finale 0 viene calcolato come (w00 * h0) + (w10 * h1) + (w20 * h2) + (w30 * h3) + b0 = (5,0 * 0,0014) + (-5.2 * 0.2921) + (5,4 * 0.5828) + (5.6 * 0.4129) + 7.0 = 0.0070 +-1.5189 +-3.1471 + 2.3122 + 7.0 = 4.6535 (arrotondato), come in Figura 1.

Struttura del programma demo

La struttura generale del programma demo, con la maggior parte WriteLine dichiarazioni rimosse e alcune lievi modifiche, viene presentata in Figura 3. La funzionalità di rete RBF contenuta nella classe RadialNet. Classe helper contiene tre metodi di visualizzazione.

Figura 3 RBF rete Demo programma struttura

using System;
namespace RadialNetworksInputOutput
{
  class RadialNetIOProgram
  {
    static void Main(string[] args)
    {
      Console.WriteLine("\nBegin Radial Basis Function (RBF) network demo\n");
      int numInput = 3;
      int numHidden = 4;
      int numOutput = 2;
      Console.WriteLine("\nCreating a 3-4-2 radial net");
      RadialNet rn = new RadialNet(numInput, numHidden, numOutput);
      double[][] centroids = new double[4][];
      centroids[0] = new double[] { -3.0, -3.5, -3.8 };
      centroids[1] = new double[] { -1.0, -1.5, -1.8 };
      centroids[2] = new double[] { 2.0, 2.5, 2.8 };
      centroids[3] = new double[] { 4.0, 4.5, 4.8 };
      rn.SetCentroids(centroids);
      double[] stdDevs = new double[4] { 2.22, 3.33, 4.44, 5.55 };
      rn.SetStdDevs(stdDevs);
      double[][] hoWeights = new double[4][];
      hoWeights[0] = new double[2] { 5.0, -5.1 };
      hoWeights[1] = new double[2] { -5.2, 5.3 };
      hoWeights[2] = new double[2] { -5.4, 5.5 };
      hoWeights[3] = new double[2] { 5.6, -5.7 };
      rn.SetWeights(hoWeights);
      double[] oBiases = new double[2] { 7.0, 7.1 };
      rn.SetBiases(oBiases);
      double[] xValues = new double[3] { 1.0, -2.0, 3.0 };
      Console.WriteLine("\nSetting x-input to:");
      Helpers.ShowVector(xValues, 1, 4, true);
      Console.WriteLine("\nComputing the output of the radial net\n");
      double[] yValues = rn.ComputeOutputs(xValues);
      Console.WriteLine("\nThe output of the RBF network is:");
      Helpers.ShowVector(yValues, 4, 4, true);
      Console.WriteLine("\nEnd RBF network demo\n");
      Console.ReadLine();
    } // Main
  } // Program
  public class RadialNet { ...
}
  public class Helpers { ...
}
} // ns

Per creare il programma demo, lanciato Visual Studio 2012. La demo non ha significative .NET dipendenze, quindi qualsiasi versione di Visual Studio dovrebbe funzionare. Ho creato una nuova applicazione console c#­progetto cazione denominato RadialNetworksInputOutput. Dopo aver caricato il codice del template, rinominato il file Program.cs per NetIOProgram.cs radiale e Visual Studio automaticamente rinominato classe Program di conseguenza. Ho cancellato tutto inutili istruzioni using all'inizio del codice sorgente.

Si dovrebbe essere in grado di capire le istruzioni nel metodo Main senza difficoltà. L'istruzione che crea un'istanza dell'oggetto di rete RBF è:

RadialNet rn = new RadialNet(numInput, numHidden, numOutput);

Le quattro affermazioni che caricano i valori dei parametri rete RBF sono:

rn.SetCentroids(centroids);
rn.SetStdDevs(stdDevs);
rn.SetWeights(hoWeights);
rn.SetBiases(oBiases);

L'istruzione che carica l'input e calcola e restituisce l'output di rete RBF è:

double[] yValues = rn.ComputeOutputs(xValues);

Classe RadialNet

La definizione della classe di rete RBF inizia con:

public class RadialNet
{
  private int numInput;
  private int numHidden;
  private int numOutput;
  private double[] inputs;
  private double[][] centroids; // Aka means
  private double[] stdDevs; // Aka widths
  private double[][] hoWeights;
  private double[] oBiases;
  private double[] outputs;
...

Lo scopo di ognuno di questi membri di classe dovrebbe essere chiaro, basato sulla spiegazione di come una rete RBF calcola il relativo output. I pesi sono memorizzati come una matrice di matrici-stile-matrice dove il primo indice indica il nodo nascosto e il secondo indice indica il nodo di uscita. Il linguaggio c#, a differenza della maggior parte delle lingue, supporta un tipo di dati della matrice vera, e si potrebbe desiderare di utilizzare invece una matrice di matrici per la matrice dei pesi.

Il costruttore RadialNet è definito come:

public RadialNet(int numInput, int numHidden, int numOutput)
{
  this.
numInput = numInput;
  this.
numHidden = numHidden;
  this.
numOutput = numOutput;
  this.inputs = new double[numInput];
  this.centroids = MakeMatrix(numHidden, numInput);
  this.stdDevs = new double[numHidden];
  this.hoWeights = MakeMatrix(numHidden, numOutput);
  this.oBiases = new double[numOutput];
  this.outputs = new double[numOutput];
}

Se si fa riferimento al diagramma in Figura 2, vedrai come la dimensione di ciascuna classe matrice e matrice è relativo a numInput, numHidden e numOutput. Un metodo di utilità statico, MakeMatrix, viene chiamato dal costruttore solo per mantenere il codice un po ' più pulito. Metodo MakeMatrix è definito come:

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

Classe RadialNet ha quattro metodi per impostare i valori dei centroidi, deviazioni standard, pesi e pregiudizi. Un design alternativo è quello di eseguire l'overload del costruttore per accettare tutti i valori di parametro RBF, ma io preferisco i metodi set separati nella maggior parte delle situazioni. Metodo SetCentroids è:

public void SetCentroids(double[][] centroids)
{
  if (centroids.Length != numHidden)
    throw new Exception("Bad number of centroids");
  if (centroids[0].Length != numInput)
    throw new Exception("Bad centroid size");
  for (int i = 0; i < numHidden; ++i)
    for (int j = 0; j < numInput; ++j)
      this.centroids[i][j] = centroids[i][j];
}

Metodo SetStdDevs è:

public void SetStdDevs(double[] stdDevs)
{
  if (stdDevs.Length != numHidden)
    throw new Exception("Bad number of stdDevs");
  Array.Copy(stdDevs, this.stdDevs, stdDevs.Length);
}

Metodo SetWeights è:

public void SetWeights(double[][] hoWeights)
{
  if (hoWeights.Length != numHidden)
    throw new Exception("Bad number of weights");
  if (hoWeights[0].Length != numOutput)
    throw new Exception("Bad number of weights");
  for (int i = 0; i < numHidden; ++i)
    for (int j = 0; j < numOutput; ++j)
      this.hoWeights[i][j] = hoWeights[i][j];
}

Metodo SetBiases è:

public void SetBiases(double[] oBiases)
{
  if (oBiases.Length != numOutput)
    throw new Exception("Bad number of hoBiases");
  Array.Copy(oBiases, this.oBiases, oBiases.Length);
}

Il metodo ComputeOutputs

Il cuore della classe RadialNet è il metodo ComputeOutputs. Inizio della definizione del metodo:

public double[] ComputeOutputs(double[] xValues)
{
  Array.Copy(xValues, inputs, xValues.Length);
  double[] hOutputs = new double[numHidden];
...

Qui, sono semplicemente copiati i valori x-ingresso agli ingressi di matrice membro. Perché il metodo ComputeOutputs utilizza ma non cambia i valori di x-ingresso, ingressi matrice membro non è davvero necessario. Tuttavia, in alcuni casi si potrebbe voler eseguire alcune elaborazioni preliminari del x-input. E sento che mediante la matrice di input di classe esplicita è un design più pulito e vale la pena l'extra lavoro di copiatura in valori. La matrice di hOutputs locale detiene le uscite di ogni nodo nascosto. Un design alternativo è quello di definire hOutputs come un membro della classe.

Successivamente, ComputeOutputs calcola le uscite per ogni nodo nascosto:

for (int j = 0; j < numHidden; ++j)
{
  double d = Distance(inputs, centroids[j]);
  // Display distance here if you wish
  double r = -1.0 * (d * d) / (2 * stdDevs[j] * stdDevs[j]);
  double g = Math.Exp(r);
  // Display hidden output here if you wish
  hOutputs[j] = g;
}
...

La distanza viene calcolata con un metodo statico dell'utilità, distanza, che è definito:

public static double Distance(double[] x, double[] c)
{
  double sum = 0.0;
  for (int i = 0; i < x.Length; ++i)
    sum += (x[i] - c[i]) * (x[i] - c[i]);
  return Math.Sqrt(sum);
}

Si noti che la distanza esegue un'operazione di radice quadrata e che questo valore viene memorizzato nella variabile d, che poi è quadrato. Un'alternativa è di definire un metodo DistanceSquared, che è la stessa distanza tranne la chiamata alla funzione radice quadrata e quindi non quadrato il valore di d. Sebbene questo approccio è più efficiente, rende il codice sincronizzati con le definizioni di matematica standard usate in letteratura RBF.

Il codice in ComputeOutputs che calcola l'output finale è:

for (int k = 0; k < numOutput; ++k)
  outputs[k] = 0.0;
for (int k = 0; k < numOutput; ++k)
  for (int j = 0; j < numHidden; ++j)
    outputs[k] += (hOutputs[j] * hoWeights[j][k]);
for (int k = 0; k < numOutput; ++k)
  outputs[k] += oBiases[k];
...

Metodo ComputeOutputs finisce copiando i valori nella matrice membro uscite per un valore di ritorno:

...
double[] result = new double[numOutput];
  Array.Copy(outputs, result, outputs.Length);
  return result;
}

Un design alternativo è quello di definire ComputeOutputs usando un tipo restituito void e definire un metodo separato GetOutputs.

Il confezionamento

Il codice e la spiegazione ha presentato in questo articolo dovrebbe farti iniziato se volete esplorare le reti funzione radiale di base. Ci sono diversi tipi di reti RBF. RBF netta in questo articolo utilizza una funzione gaussiana per calcolare l'uscita del nodo nascosto. Reti RBF possono utilizzare molte altre funzioni, con nomi come multi-quadratica e thin-plate spline. La funzione particolare utilizzata da una rete RBF è chiamata il kernel della rete.

Ci si potrebbe chiedere esattamente dove l'architettura piuttosto esotico delle reti RBF proviene. Reti RBF erano un risultato della ricerca accademica nella teoria dell'approssimazione della funzione. Si può dimostrare che, con alcuni presupposti e senza bloccare parlando, reti RBF possono replicare qualsiasi funzione matematica. Questo significa, almeno in teoria reti RBF possono essere utilizzati per fare previsioni sui dati che segue qualsiasi modello sottostante. Reti neurali condividono questa caratteristica universale-funzione. A mio parere, non è chiaro se le reti RBF sono più o meno efficaci di reti neurali, o più o meno lo stesso, quando si tratta di apprendimento automatico classificazione e previsione. Tuttavia, ci è buona prova che reti RBF possono essere addestrati molto più rapidamente rispetto alle reti neurali, e a differenza di reti neurali, che in genere richiedono grandi quantità di dati di training, reti RBF possono funzionare bene anche con piccole quantità di dati di training.

Per poter utilizzare una rete RBF fare previsioni per un problema realistico, la rete deve essere addestrata. Questo significa che utilizzando dati storici attuali e trovare il miglior set di centroidi, deviazioni standard, pesi e valori di bias — cioè, il set di valori del parametro che genera output di rete RBF che corrisponde più da vicino i dati storici. Formazione di una rete RBF coinvolge anche trovando il numero ottimale di nodi nascosti. Formazione reti RBF è un interessante problema che verrà spiegato in un articolo futuro.

Dr. James McCaffrey lavora per la ricerca di Microsoft di Redmond, WA Ha lavorato su diversi prodotti Microsoft, inclusi Internet Explorer e Bing. Può essere raggiunto a jammc@microsoft.com.

Si ringraziano i seguenti esperti tecnici per aver rivisto questo articolo: Dan Liebling (Microsoft Research)