Compartir a través de


Ejecución de pruebas

Clasificación y predicción con el uso de redes neuronales

James McCaffrey

Descargar el ejemplo de código

James McCaffrey

En la columna de este mes, explicaré cómo usar redes neuronales para solucionar problemas de clasificación y de predicción. El objetivo de la clasificación queda más claro con un ejemplo. Supongamos que tenemos datos históricos sobre flores de iris que se asemejan a esto:

5.1 3.5 1.4 0.2 Setosa
7.0 3.2 4.7 1.4 Versicolor
6.3 3.3 6.0 2.5 Virginica
6.4 3.2 4.5 1.5 Versicolor
...

Cada línea de datos delimitados por espacios contiene cinco campos. Los primeros cuatro campos numéricos son la longitud del sépalo (lo verde que envuelve el capullo), el ancho del sépalo, la longitud del pétalo (la parte con color de la flor), y el ancho del pétalo. El quinto campo es la especie: Setosa, Versicolor o Virginica. El objetivo de la clasificación es determinar una ecuación o conjunto de reglas que pueda predecir a qué especie o clase pertenece un iris. Luego, se puede usar el conjunto de reglas para predecir la clase de un nuevo iris a partir de sus valores de longitud y ancho de sépalo y pétalo. Los datos de la planta de iris es un ejemplo clásico usado por primera vez por R. A. Fisher en 1936. Es posible que no lo encuentre emocionante, pero la clasificación es extremadamente importante. Ejemplos incluyen la clasificación de la calificación de crédito de un solicitante basada en variables como el ingreso, los gastos mensuales (o de manera equivalente, predecir su valor crediticio) y clasificar si el paciente de un hospital tiene cáncer, a partir de los valores de un examen de sangre.

Existen mucho enfoques para clasificar datos, incluido el uso de las redes neuronales. Una forma de ver las redes neuronales es pensar que son dispositivos virtuales de entrada y salida, que aceptan cualquier cantidad de entradas numéricas y producen cualquier cantidad de salidas numéricas. La mejor forma de ver mi objetivo es examinar la captura de pantalla en la Figura 1 y la imagen en la Figura 2. La Figura 1 muestra la clasificación de la red neuronal en acción. Para que el concepto de la clasificación por medio de las redes neuronales quedara claro, no usé datos reales. En vez, usé datos artificiales en que los valores X de entrada son cuatro valores numéricos arbitrarios sin un significado en especial. La variable Y de salida para clasificar es el color y puede adoptar uno de los tres valores de categoría: rojo, verde o azul. El programa que aparece en la Figura 1 comienza generando un archivo de texto con 100 líneas de datos artificiales (por ejemplo, “8.0 5.0 9.0 5.0 verde”) y luego muestra las primeras cuatro líneas de dichos datos. A continuación, el programa lee el archivo de datos sin procesar en la memoria como una matriz de entrenamiento con 80 filas de datos y una matriz de prueba de 20 filas. Observe que se aplican dos transformaciones a los datos sin procesar. Los datos numéricos de entrada sin procesar se normalizan para que todos los valores estén entre -1.00 y +1.00 y los datos de salida sin procesar (como por ejemplo "rojo") se codifica en un vector con tres valores (“1.0 0.0 0.0”).


Figura 1 La clasificación de la red neuronal en acción

Neural Network Structure
Figura 2 Estructura de una red neuronal

Después de crear las matrices de entrenamiento y de prueba, el programa de demostración crea una red neuronal completamente conectada de alimentación hacia delante con tres neuronas de entrada, cinco neuronas ocultas para cálculos y tres neuronas de salida. Resulta que una red neuronal 4-5-3 completamente conectada requiere 43 ponderaciones e inclinaciones. A continuación, el programa de clasificación analiza los datos de entrenamiento para descubrir las mejores 43 ponderaciones e inclinaciones (aquellas que minimizan el error total de clasificación). El programa usa la optimización por enjambre de partículas junto con error de entropía cruzada para calcular los mejores valores de las ponderaciones e inclinaciones.

Luego, el programa de clasificación carga las mejores ponderaciones e inclinaciones en la red neuronal y evalúa la precisión de predicción del modelo en las 20 filas de datos en la matriz de prueba. Observe que la salida de la red neuronal se diseñó para que los tres valores de salida sumen 1.0. En este ejemplo, el modelo predice correctamente 17 de los 20 vectores de prueba. La imagen en la Figura 2 muestra cuando la red neuronal acepta entradas de (-1.00, 1.00, 0.25, -0.50) y genera una salida predicha de (0.9, 0.1, 0.0), que corresponde a rojo.

El programa de ejemplo destaca que existen cinco decisiones principales que hay que tomar al usar las redes neuronales para la clasificación, en que los datos de entrada son numéricos y los datos de salida son de categoría:

  1. Cómo normalizar los datos numéricos de entrada
  2. Cómo codificar los datos de categoría de salida
  3. Cómo generar salidas neuronales en el intervalo [0.0, 1.0]
  4. Cómo medir los errores al practicar
  5. Cómo medir la precisión al realizar las pruebas

En las siguientes secciones explicaré que las elecciones realizadas en el programa de ejemplo son:

  1. Realizar una transformación lineal de los datos numéricos de entrada
  2. Usar la codificación 1-de-N para los datos de categoría de salida
  3. Usar una función de activación Softmax para la capa de salida
  4. Usar el error de entropía cruzada para determinar las mejores ponderaciones
  5. Usar el enfoque "el ganador se lleva todo" para determinar la precisión

El código del programa que generó la captura de pantalla de la Figura 1 es demasiado grande para mostrarlo en este artículo, por lo que me enfoco en los algoritmos usados. La fuente completa del programa está disponible en el sitio de descarga de código de MSDN en archive.msdn.microsoft.com/mag201207TestRun. Este artículo supone que cuenta con conocimientos de programación avanzados y un conocimiento básico de las redes neuronales. En el número de mayo de 2012 de MSDN Magazine expliqué los fundamentos de la red neuronal (msdn.microsoft.com/magazine/hh975375).

Estructura general del programa

La Figura 3 enumera la estructura del programa del ejemplo que aparece en ejecución en la Figura 1. Usé Visual Studio 2010 para crear una sola aplicación de consola C# llamado NeuralClassification. En la ventana del Explorador de soluciones, cambié el nombre del archivo Program.cs al más descriptivo NeuralClassificationProgram.cs, lo que automáticamente cambió el nombre de la clase que contiene Main. Eliminé lo innecesario con el uso de instrucciones generadas por la plantilla de Visual Studio y agregué una referencia al espacio de nombres System.IO.

Figura 3 Estructura del programa de la clasificación neuronal

using System;
using System.IO;
namespace NeuralClassification
{
  class NeuralClassificationProgram
  {
    static Random rnd = null;
    static void Main(string[] args)
    {
      try
      {
        Console.WriteLine("\nBegin Neural network classification\n");
        rnd = new Random(159); // 159 makes a nice example
        string dataFile = "..\\..\\colors.txt";
        MakeData(dataFile, 100);
        double[][] trainMatrix = null;
        double[][] testMatrix = null;
        MakeTrainAndTest(dataFile, out trainMatrix, out testMatrix);
        NeuralNetwork nn = new NeuralNetwork(4, 5, 3);
        double[] bestWeights = nn.Train(trainMatrix);
        nn.SetWeights(bestWeights);
        double accuracy = nn.Test(testMatrix);
        Console.WriteLine("\nEnd neural network classification\n");
      }
      catch (Exception ex)
      {
        Console.WriteLine("Fatal: " + ex.Message);
      }
    } // Main()
    static void MakeData(string dataFile, int numLines) { ... }
    static void MakeTrainAndTest(string file, out double[][] trainMatrix,
      out double[][] testMatrix) { ... }
  }
  class NeuralNetwork
  {
    // Class member fields here
    public NeuralNetwork(int numInput, int numHidden,
      int numOutput) { ... }
    public void SetWeights(double[] weights) { ... }
    public double[] ComputeOutputs(double[] currInputs) { ... }
    private static double SigmoidFunction(double x) { ... }
    private static double[] Softmax(double[] hoSums) { ... }
    public double[] Train(double[][] trainMatrix) { ... }
    private double CrossEntropy(double[][] trainData,
      double[] weights) { ... }
    public double Test(double[][] testMatrix) { ... }
  }
  public class Helpers
  {
    static Random rnd = new Random(0);
    public static double[][] MakeMatrix(int rows, int cols) { ... }
    public static void ShuffleRows(double[][] matrix) { ... }
    public static int IndexOfLargest(double[] vector) { ... }
    public static void ShowVector(double[] vector, int decimals,
      bool newLine) { ... }
    public static void ShowMatrix(double[][] matrix, int numRows) { ... }
    public static void ShowTextFile(string textFile, int numLines) { ... }
  }
  public class Particle
  {
    // Class member fields here
    public Particle(double[] position, double fitness,
      double[] velocity, double[] bestPosition,
     double bestFitness) { ... }
    public override string ToString() { ... }
  }
} // ns

Además de la clase que contiene el método Main, el programa posee otras tres clases. La clase NeuralNetwork encapsula una red neuronal completamente conectada de alimentación hacia delante. Esta clase contiene toda la lógica fundamental del programa. La clase Helpers contiene seis rutinas de utilidad. La clase Particle define un objeto de partícula que el algoritmo de optimización por enjambre de partículas usa en el método Train de la clase NeuralNetwork. Una característica de los programas de clasificación es que existen muchas estructuras posibles de programa; la organización que aquí se presenta es solo una de las posibilidades.

Generación del archivo de datos sin procesar

En la mayoría de los escenarios de clasificación, ya se cuenta con un conjunto de datos sin procesar, pero para este artículo creé datos sin procesar ficticios con el uso del método MakeData. Así funciona el proceso en seudocódigo:

create 43 arbitrary weights between -2.0 and +2.0
create a 4-5-3 neural network
load weights into the neural network
open a result file for writing
loop 100 times
  generate four random inputs x0, x1, x2, x3 between 1.0 and 9.0
  compute the y0, y1, y2 neural outputs for the input values
  determine largest of y0, y1, y2
  if y0 is largest write x0, x1, x2, x3, red
  else if y1 is largest write x0, x1, x2, x3, green
  else if y2 is largest write x0, x1, x2, x3, blue
end loop
close result file

El objetivo aquí consiste en conseguir algunos datos que definitivamente se pueden clasificar con una precisión del 100%, en vez de datos al azar para los que no está claro qué tan eficiente sería cualquier enfoque de clasificación. En otras palabras, usé una red neuronal para crear datos sin procesar y luego volví a comenzar y usé la red neuronal para intentar clasificar esos datos.

Crear las matrices de entrenamiento y de prueba

Al realizar el análisis de clasificación con un conjunto de datos existentes, un enfoque común, llamado validación simple, consiste en dividir los datos en un conjunto grande de datos (por lo general, un 80%) para entrenar la red neuronal y un conjunto de datos más pequeño (20%) para probar el modelo. El entrenamiento quiere decir que se busca descubrir las ponderaciones e inclinaciones de la red neuronal que minimicen cierto valor de error. La prueba quiere consiste en evaluar la red neuronal con las mejores ponderaciones descubiertas en el entrenamiento, con el uso de cierta medida de precisión. Aquí, el método MakeTrainAndTest crea las matrices de entrenamiento y de prueba y también normaliza los datos numéricos de entrada y codifica los datos de categoría de salida. En seudocódigo, el método funciona así:

determine how many rows to split data into
create a matrix to hold all data
loop
  read a line of data
  parse each field
  foreach numeric input
    normalize input to between -1.0 and +1.0
  end foreach
  encode categorical output using 1-of-N
  place normalized and encoded data in matrix
end loop
shuffle matrix
create train and test matrices
transfer first 80% rows of matrix to train, remaining rows to test

La firma del método es:

static void MakeTrainAndTest(string file, out double[][] trainMatrix,
  out double[][] testMatrix)

El parámetro llamado file es el nombre del archivo de datos sin procesar que se crea. Los parámetros trainMatrix y testMatrix son los parámetros de salida donde se ubican los resultados. El método comienza con:

int numLines = 0;
FileStream ifs = new FileStream(file, FileMode.Open);
StreamReader sr = new StreamReader(ifs);
while (sr.ReadLine() != null)
  ++numLines;
sr.Close(); ifs.Close();
int numTrain = (int)(0.80 * numLines);
int numTest = numLines - numTrain;

El código cuenta el número de líneas en el archivo de datos sin procesar y luego calcula cuántas líneas constituyen el 80% y el 20% de los datos. Aquí, los porcentajes están codificados; es posible que desee parametrizarlos. A continuación, se asigna una matriz que contendrá todos los datos:

double[][] allData = new double[numLines][];
  for (int i = 0; i < allData.Length; ++i)
    allData[i] = new double[7];

Existen siete columnas: una columna para cada una de las entradas numéricas y tres columnas para el valor codificado 1-de-N de la variable de categoría de color. Recuerde que para este ejemplo, el objetivo es predecir el color, que puede adoptar uno de los tres valores de categoría: rojo, verde o azul. La codificación por medio de la técnica 1-de-N en esta situación significa codificar rojo como (1.0, 0.0, 0.0), verde como (0.0, 1.0, 0.0) y azul como (0.0, 0.0, 1.0). Los datos de categoría se deben codificar de forma numeral porque las redes neuronales solo administran directamente valores numéricos. Resulta que la codificación de colores con el uso de un enfoque simple como 1 para rojo, 2 para verde y 3 para azul es una mala idea. La explicación de esto es un poco extensa y queda fuera del alcance de este artículo.

Una excepción a la pauta de codificación 1-de-N para los datos de categoría de salida es que cuando solo hay dos valores posibles, como "masculino" y "femenino", se puede usar la codificación 1-de-(N-1) en la que se tiene solo un valor numérico de salida para que, por ejemplo, 0.0 signifique masculino y 1.0 signifique femenino.

El siguiente código realiza la codificación:

tokens = line.Split(' ');
allData[row][0] = double.Parse(tokens[0]);
allData[row][1] = double.Parse(tokens[1]);
allData[row][2] = double.Parse(tokens[2]);
allData[row][3] = double.Parse(tokens[3]);
for (int i = 0; i < 4; ++i)
  allData[row][i] = 0.25 * allData[row][i] - 1.25;
if (tokens[4] == "red") {
  allData[row][4] = 1.0; 
  allData[row][5] = 0.0; 
  allData[row][6] = 0.0; }
else if (tokens[4] == "green") {
  allData[row][4] = 0.0; 
  allData[row][5] = 1.0; 
  allData[row][6] = 0.0; }
else if (tokens[4] == "blue") {
  allData[row][4] = 0.0; 
  allData[row][5] = 0.0; 
  allData[row][6] = 1.0; }

Recuerde que una línea de datos sin procesar luce así:

8.0 5.0 9.0 5.0 verde

Los cinco campos se analizan con String.Split. La experiencia demuestra que en la mayoría de los casos, se obtienen mejores resultados cuando las entradas numéricas se escalan a valores entre -1.0 y +1.0. Cada una de las primeras cuatro entradas numéricas se convierten al multiplicar por 0.25 y restar 1.25 Recuerde que todas las entradas numéricas en el archivo de datos ficticio están entre 1.0 y 9.0. En un problema de clasificación real, se deben examinar los datos sin procesar y determinar los valores mínimos y máximos. Queremos que -1.0 corresponda con 1.0 y +1.0 corresponda con 9.0. Realizar una transformación lineal significa encontrar la pendiente (aquí 0.25) y el interceptor (-1.25). Estos valores se pueden calcular como:

slope = 2 / (max value - min value) = 2 / (9.0 - 1.0) = 0.25
intercept = 1.0 - (slope * max value) = 1 - (0.25 * 9.0) = -1.25

Existen muchas alternativas para realizar una transformación lineal de los valores numéricos de entrada, pero el enfoque presentado aquí es simple y un buen punto de partida en la mayoría de los casos.

Después que se transforman los cuatro valores numéricos de entrada, el valor de color del archivo de datos sin procesar se codifica por medio de la codificación 1-de-N. Cuando se calcularon todos los valores del archivo de datos sin procesar y se colocaron en la matriz allData, esta reorganiza de manera aleatoria sus filas con el uso del método de utilidad ShuffleRows en la clase Helpers. Después de que se reorganizaron aleatoriamente las filas en allData, se asigna espacio para las matrices trainMatrix y testMatrix, luego las primeras filas numTrain de allData se copian en trainMatrix y el resto de las filas numTest de allData se copian en testMatrix.

Una importante alternativa de diseño para el enfoque de entrenamiento-prueba consiste en dividir los datos sin procesar en tres conjuntos: entrenar, validar y probar. La idea es usar los datos de entrenamiento para determinar el mejor conjunto de ponderaciones de la red neuronal junto con los datos validados, lo que se usa para conocer cuándo terminar el entrenamiento. Existen otros enfoques, que en su conjunto se llaman técnicas de validación cruzada.

La función de activación Softmax

Al realizar la clasificación con una red neuronal donde la variable de salida es de categoría, existe una complicada relación entre la función de activación de la salida de la red neuronal, el cálculo de error durante el entrenamiento y el cálculo de precisión predictiva de la red neuronal. Cuando los datos de categoría de salida (como el color con los valores rojo, verde o azul) se codifica por medio de la codificación 1-de-N, por ejemplo (1.0 0.0 0.0) para rojo, se quiere que la red neuronal emita tres valores numéricos para poder determinar un error al entrenar la red. Sin embargo, no se quiere que se emitan tres valores numéricos arbitrarios, ya que en ese caso no queda demasiado claro cómo calcular un término de error. Pero supongamos que la red neuronal emite tres valores numéricos que están entre 0.0 y 1.0 y que la suma es de 1.0. Entonces los valores emitidos se pueden interpretar como probabilidades, lo que en definitiva facilita calcular un término de error al entrenar y calcular la precisión durante la prueba. La función de activación Softmax emite valores de salida de esta forma. La función Softmax acepta sumas de oculto a salida neuronales y devuelve los valores de salidas neuronales finales; se puede implementar así:

private static double[] Softmax(double[] hoSums)
{
  double max = hoSums[0];
  for (int i = 0; i < hoSums.Length; ++i)
    if (hoSums[i] > max) max = hoSums[i];
  double scale = 0.0;
    for (int i = 0; i < hoSums.Length; ++i)
      scale += Math.Exp(hoSums[i] - max);
  double[] result = new double[hoSums.Length];
    for (int i = 0; i < hoSums.Length; ++i)
      result[i] = Math.Exp(hoSums[i] - max) / scale;
  return result;
}

En un principio, la función Softmax calcula un factor de escalada al tomar Exp de cada suma de oculto a salida, luego sumarlos y finalmente dividir Exp de cada valor por el factor de escalada. Por ejemplo, supongamos que tres sumas de oculto a salida son (2.0, -1.0, 4.0). El factor de escalada sería Exp(2.0) + Exp(-1.0) + Exp(4.0) = 7.39 + 0.37 + 54.60 = 62.36. Luego, los valores Softmax de salida serían Exp(2.0)/62.36, Exp(-1.0)/62.36, Exp(4.0)/62.36) = (0.12, 0.01, 0.87). Observe que las salidas finales están todas entre 0.0 y 1.0 y precisamente suman 1.0, y más que la mayor suma oculta a salida (4.0) posee la más grande salida/probabilidad (0.87) y la relación es semejante para los segundos y terceros valores más grandes.

Lamentablemente, una implementación ingenua de Softmax puede generar errores porque la función Exp se vuelve muy grande, muy rápidamente y puede producir un desbordamiento aritmético. La implementación anterior usa el hecho de que Exp(a - b) = Exp(a) / Exp(b) para calcular salidas de forma de evitar desbordamientos. Si traza la ejecución de la implementación con (2.0, -1.0, 4.0) como entradas, obtendrá las mismas salidas (0.12, 0.01, 0.87), como se explicó en la sección anterior.

El error de entropía cruzada

La esencia del entrenamiento de una red neuronal es descubrir el conjunto de ponderaciones que produce el menor error para los datos en el conjunto de entrenamiento. Por ejemplo, supongamos que una fila de datos de entrenamiento normalizados y codificados es (0.75 -0.25 0.25 -0.50 0.00 1.00 0.00). Recuerde que los primeros cuatro valores corresponden a las entradas normalizadas y los últimos tres valores representan verde en el codificado 1-de-N. Ahora supongamos que las entradas se ingresan a través de la red neuronal, que se cargó con algunos de los conjuntos de ponderaciones y la salida Softmax es (0.20 0.70 0.10). Un enfoque tradicional para calcular error para este vector de prueba es usar la suma de las diferencias cuadradas, que en este caso sería (0.00 - 0.20)^2 + (1.00 - 0.70)^2 + (0.00 - 0.10)^2 = 0.14. Pero supongamos que la salida Softmax fue (0.30 0.70 0.00). Tanto este vector como el anterior predicen que la salida es verde, porque la probabilidad de verde es 0.70. Sin embargo, la suma de las desviaciones cuadradas para este segundo vector es 0.18, lo que es diferente del primer término de error. Aunque la suma de las desviaciones cuadradas se puede usar para calcular el error de entrenamiento, los resultados de algunas investigaciones muestran que es preferible usar una medida alternativa llamada error de entropía cruzada.

En principio, el error de entropía cruzada para el vector v de salida de una red neuronal y un vector t de salida de prueba se calcula al determinar la suma negativa del producto de cada componente del vector v y el componente del vector t. Como siempre, esto se explica mejor con un ejemplo. Si un vector de prueba es (0.75 -0.25 0.25 -0.50 0.00 1.00 0.00) y la salida Softmax de la red neuronal correspondiente es (0.20 0.70 0.10), entonces el error de entropía cruzada es -1 * (0.00 * Log(0.20)) + (1.00 * Log(0.70)) + (0.00 * Log(0.10)) = -1 * (0 + -0.15 + 0) = 0.15. Observe que todos los términos en la suma, excepto uno, siempre serán cero cuando se usa la codificación 1-de-N. El error de entropía cruzada para el conjunto de entrenamiento completo se puede calcular como la suma de la entropía cruzada para todos los vectores de prueba, o la entropía cruzada promedio de cada vector de prueba. Una implementación del error de entropía cruzada se muestra en la Figura 4.

Figura 4 Error de entropía cruzada

private double CrossEntropy(double[][] trainData, 
  double[] weights)
{
  this.SetWeights(weights);
  double sce = 0.0; // sum of cross entropies
  for (int i = 0; i < trainData.Length; ++i)
  {
    double[] currInputs = new double[4];
    currInputs[0] = trainData[i][0];
    currInputs[1] = trainData[i][1];
    currInputs[2] = trainData[i][2];
    currInputs[3] = trainData[i][3];
    double[] currExpected = new double[3];
    currExpected[0] = trainData[i][4];
    currExpected[1] = trainData[i][5];
    currExpected[2] = trainData[i][6];
    double[] currOutputs = this.ComputeOutputs(currInputs);
    double currSum = 0.0;
    for (int j = 0; j < currOutputs.Length; ++j)
    {
      if (currExpected[j] != 0.0)
        currSum += currExpected[j] * Math.Log(currOutputs[j]);
    }
    sce += currSum; // accumulate
  }
  return -sce;
}

Entrenamiento de la red neuronal

Existen muchas formas de entrenar un clasificador de red neuronal para descubrir el conjunto de ponderaciones que mejor coincide con los datos de entrenamiento (o, de manera equivalente, da paso al menor error de entropía cruzada). En un alto nivel de abstracción, entrenar una red neuronal luce así:

create an empty neural network
loop
  generate a candidate set of weights
  load weights into neural network
  foreach training vector
    compute the neural output
    compute cross-entropy error
    accumulate total error
  end foreach
  if current weights are best found so far
    save current weights
  end if
until some stopping condition
return best weights found

Por mucho, la técnica más usada para entrenar redes neuronales se llama propagación inversa. Esta técnica es el tema de una gran cantidad de artículos de investigación; son tantos, que si se es nuevo en el campo de la clasificación de redes neuronales, es muy fácil llegar a creer que la propagación inversa es la única técnica usada para el entrenamiento. Calcular el mejor conjunto de ponderaciones de una red neuronal es un problema de minimización numérica. Dos alternativas comunes a usar la propagación inversa consisten en usar un algoritmo genético con valores reales (también conocido como algoritmo de optimización evolutiva) y usar la optimización por enjambre de partículas. Cada técnica de cálculo tiene sus ventajas y sus desventajas. El programa que aparece en la Figura 1 usa la optimización por enjambre de partículas. Describo los algoritmos de optimización evolutiva en el número de junio de 2012 de MSDN Magazine (msdn.microsoft.com/magazine/jj133825) y la optimización por enjambre de partículas en el número de agosto de 2011 (msdn.microsoft.com/magazine/hh335067).

Existen muchas técnicas para determinar cuándo detener el entrenamiento de una red neuronal. Aunque se podría dejar que el algoritmo de entrenamiento se ejecute hasta que el error de entropía cruzada esté muy cerca de cero, lo que indicaría una coincidencia casi perfecta, el peligro es que las ponderaciones resultantes representarían un sobreajuste para los datos de entrenamiento y las ponderaciones crearían una red neuronal que clasifica mal para cualquier dato que no está en el conjunto de entrenamiento. Además, entrenar hasta que no hayan cambios en el error de entropía cruzada puede conducir fácilmente a un sobreajuste del modelo. Un enfoque simple, usado por el programa de ejemplo, consiste en limitar el entrenamiento a un número fijo de iteraciones. En la mayoría de los casos, una táctica mucho mejor para evitar el sobreajuste consiste en dividir el conjunto de datos de origen en conjuntos de entrenamiento-validación-prueba. Por lo general, estos tres conjuntos de datos usan un 60%, 20% y 20% de los datos de origen, respectivamente. La técnica calcula el error en el conjunto de entrenamiento como se describió anteriormente, pero después de cada iteración en el bucle principal, la técnica calcula el error de entropía cruzada en el conjunto de datos de validación. Cuando el error de entropía cruzada en el conjunto de validación comienza a mostrar un incremento coherente del error, hay muchas posibilidades de que el algoritmo de entrenamiento haya comenzado a sobreajustar los datos y se debe detener el entrenamiento. Existen muchas otras técnicas de detención posibles.

Evaluar el clasificador de la red neuronal

Después de entrenar el clasificador de la red neuronal y de que haya producido un conjunto de las mejores ponderaciones e inclinaciones, el siguiente paso consiste en determinar qué tan preciso es el modelo resultante (donde modelo quiere decir la red neuronal con el conjunto de las mejores ponderaciones) en los datos de prueba. Aunque se pueden usar mediciones como la suma de las desviaciones cuadradas o el error de entropía cruzada, una medición razonable de la precisión es simplemente el porcentaje de predicciones correctas realizadas por el modelo. Nuevamente, existen varios enfoques para medir la precisión, pero una técnica simple es usar lo que se conoce como el enfoque "el ganador se lleva todo". Y, como siempre, la técnica queda más clara con un ejemplo. Supongamos el vector de prueba (-1.00 1.00 0.25 -0.50 1.00 0.00 0.00), como se muestra en el primer conjunto de datos de predicción en la Figura 1. Con el conjunto de mejores ponderaciones, la red neuronal genera una salida Softmax predicha de (0.9 0.1 0.0). Si cada salida se interpreta como una probabilidad, entonces la más alta probabilidad es 0.9 y la salida predicha se pueden considerar como (1 0 0), por lo que el modelo realiza la predicción correcta. Dicho de otra forma, la técnica "el ganador se lo lleva todo" determina el componente de salida de la red neuronal con el valor más grande, hace que ese componente sea 1 y todos el resto de los componentes 0 y compara ese resultado con los datos reales en el vector de entrenamiento. En el tercer conjunto de datos de análisis de precisión en la Figura 1, el vector de prueba es (-0.50 0.75 0.25 -0.75 0.0 0.0 1.0). Los datos de salida reales son (0.0 0.0 1.0), lo que corresponde al azul. La salida predicha es (0.3 0.6 0.1). El componente más grande es 0.6, por lo que la predicción del modelo es (0 1 0), lo que corresponde al verde. El modelo hizo una predicción incorrecta.

En resumen

La clasificación con las redes neuronales es un tema importante, fascinante y complejo. El ejemplo presentado aquí debería proporcionarle un fundamento sólido para experimentar con la clasificación de red neuronal. Sin embargo, este artículo describe únicamente un escenario de clasificación de red neuronal muy específico (variables numéricas de entrada con una variable de categoría de salida) y solo representa un punto de partida. Otros escenarios requieren algunas técnicas algo diferentes. Por ejemplo, si los datos de entrada contienen una variable de categoría, es posible esperar que se use una codificación 1-de-N, tal como una variable de categoría de salida. Sin embargo, en este caso los datos de entrada se deben codificar con la técnica 1-de-(N-1). Si desea conocer más sobre la clasificación con el uso de las redes neuronales, recomiendo el conjunto de siete Preguntas más frecuentes sobre las redes neuronales disponible en faqs.org. Los vínculos de estas Preguntas más frecuentes sueles moverse de un lado a otro, pero no debería tener problemas encontrándolas si realiza una búsqueda en Internet.

El Dr. James McCaffreytrabaja en Volt Information Sciences Inc., donde está a cargo del entrenamiento técnico de los ingenieros informáticos que trabajan en el campus de Microsoft en Redmond, Washington. Ha colaborado en el desarrollo de varios productos de Microsoft como, por ejemplo, Internet Explorer y MSN Search. McCaffrey es el autor de “.NET Test Automation Recipes” (Apress, 2006). Puede ponerse en contacto con él en jmccaffrey@volt.com o jammc@microsoft.com.

Gracias al siguiente experto técnico de Microsoft por su ayuda en la revisión de este artículo: Matthew Richardson