Febrero de 2018

Volumen 33, número 2

Machine Learning: clasificadores de redes neuronales profundas con CNTK

Por James McCaffrey

La biblioteca de Microsoft Cognitive Toolkit (CNTK) es un eficaz conjunto de funciones que permite crear sistemas de predicción de Machine Learning (ML). Proporcioné una introducción a la versión 2 en la edición de julio de 2017 (msdn.com/magazine/mt784662). En este artículo, explico cómo usar CNTK para crear un clasificador de redes neuronales profundas. Una buena manera de ver hacia dónde se dirige este artículo es echar un vistazo a la captura de pantalla de la Figura 1.

Demo de predicción de variedad de semillas de trigo
Figura 1 Demo de predicción de variedad de semillas de trigo

La biblioteca de CNTK está escrita en C++ por motivos de rendimiento, pero la manera más habitual de llamar a las funciones de la biblioteca es usar la API del lenguaje Python de CNTK. Para invocar el programa de demostración, ejecuté el siguiente comando en un shell de comandos ordinario de Windows 10:

> python seeds_dnn.py

El objetivo del programa de demostración es crear una red neuronal profunda que pueda predecir la variedad de una semilla de trigo. En segundo plano, el programa de demostración usa un conjunto de datos de aprendizaje con el aspecto siguiente:

|properties 15.26 14.84 ... 5.22 |variety 1 0 0
|properties 14.88 14.57 ... 4.95 |variety 1 0 0
...
|properties 17.63 15.98 ... 6.06 |variety 0 1 0
...
|properties 10.59 12.41 ... 4.79 |variety 0 0 1

Los datos de aprendizaje tienen 150 elementos. Cada línea representa una de las tres variedades de semillas de trigo: "Kama", "Rosa" o "Canadian". Los siete primeros valores numéricos de cada línea son valores predictores, a menudo denominados atributos o características en terminología de Machine Learning. Los predictores son el área, el perímetro, la compacidad, la longitud, el ancho, el coeficiente de asimetría y la longitud de la ranura de las semillas. El elemento que se va a predecir (a menudo denominado "clase" o "etiqueta") rellena las tres últimas columnas y está codificado como 1 0 0 para Kama, 0 1 0 para Rosa y 0 0 1 para Canadian.

El programa de demostración también usa un conjunto de datos de prueba de 60 elementos, 20 de cada variedad de semilla. Los datos de prueba tienen el mismo formato que los de aprendizaje.

El programa de demostración crea una red neuronal profunda 7-(4-4-4)-3. La red se ilustra en la Figura 2. Existen siete nodos de entrada (uno por cada valor predictor), tres capas ocultas, cada una de las cuales tiene cuatro nodos de procesamiento, y tres nodos de salida que corresponden a las tres variedades de semillas de trigo codificadas posibles.

Estructura de una red neuronal profunda
Figura 2 Estructura de una red neuronal profunda

El programa de demostración entrena la red con 5000 lotes de 10 elementos cada uno, mediante el algoritmo de descenso de gradiente estocástico (SGD). Una vez entrenado el modelo de predicción, se aplica al conjunto de datos de prueba de 60 elementos. El modelo logró un 78,33 % de precisión, lo que significa que predijo correctamente 47 de los 60 elementos de la prueba.

El programa de demostración finaliza con una predicción de una semilla de trigo desconocida. Los siete valores de entrada son (17.6, 15.9, 0.8, 6.2, 3.5, 4.1, 6.1). Los valores de los nodos de salida sin procesar calculados son (1.0530, 2.5276, -3.6578) y los valores de probabilidad de los nodos de salida asociados son (0.1859, 0.8124, 0.0017). Dado que el valor medio es mayor, la salida se asigna a (0, 1, 0), que es la variedad Rosa.

 En este artículo se supone que tiene conocimientos de programación intermedios o avanzados con un lenguaje de la familia C y que está un poco familiarizado con las redes neuronales. Independientemente de sus conocimientos, debería poder seguirlo sin grandes obstáculos. En este artículo se presenta todo el código fuente del programa seeds_dnn.py. El código, así como los archivos de datos de prueba y aprendizaje asociados, también están disponibles en la descarga de archivo que acompaña a este artículo.

Instalación de CNTK v2

Dado que CNTK v2 es relativamente nuevo, es posible que no esté familiarizado con el proceso de instalación. De manera resumida, primero debe instalar una distribución del lenguaje Python (recomiendo encarecidamente la distribución Anaconda) que contenga el lenguaje Python principal y los paquetes de Python necesarios y, a continuación, instalar CNTK como un paquete de Python adicional. En otras palabras, CNTK no es una instalación independiente.

En el momento de redactar este artículo, la versión de CNTK es v2.3. Dado que CNTK está en una fase de fuerte desarrollo, cuando lea este artículo, podría existir perfectamente una versión más reciente. Usé la distribución Anaconda, versión 4.1.1 (que contiene la versión de Python 3.5.2, la versión de NumPy 1.11.1 y la versión de SciPy 0.17.1). Después de instalar Anaconda, instalé la versión de solo CPU de CNTK mediante la utilidad pip. La instalación de CNTK puede ser un poco compleja si no presta atención a la compatibilidad de las versiones, pero en la documentación de CNTK se describe el proceso de instalación en detalle.

Descripción de los datos

La creación de la mayoría de los sistemas de Machine Learning comienza con el proceso arduo y largo que requiere la configuración de los archivos de datos de pruebas y aprendizaje. El conjunto de datos de semillas de trigo sin procesar se puede encontrar en bit.ly/2idhoRK. Los datos sin procesar y delimitados por tabulaciones de 210 elementos tienen el aspecto siguiente:

14.11  14.1   0.8911  5.42  3.302  2.7  5      1
16.63  15.46  0.8747  6.053 3.465  2.04 5.877  1

Escribí una utilidad para generar un archivo en un formato que CNTK puede controlar fácilmente. El archivo de 210 elementos resultante tiene el aspecto siguiente:

|properties 14.1100 14.1000 ... 5.0000 |variety 1 0 0
|properties 16.6300 15.4600 ... 5.8770 |variety 1 0 0

La utilidad anteponía una etiqueta "|properties" para identificar la ubicación de las características y una etiqueta "|variety" para identificar la ubicación de la clase que se debía predecir. Los valores de clase sin procesar presentaban la codificación 1-de-N (en ocasiones conocida como codificación "uno activo"), las pestañas se reemplazaban por caracteres de espacio blanco únicos y todos los valores predictores estaban formateados con cuatro decimales exactamente.

En la mayoría de las situaciones, su objetivo será normalizar los valores predictores numéricos para tener aproximadamente el mismo rango. No normalicé estos datos con la finalidad de mantener este artículo un poco más simple. Dos formas de normalización comunes son la normalización de puntuación z y la normalización mínima-máxima. En general, en escenarios que no sean de demostración debería normalizar sus valores predictores.

A continuación, escribí otra utilidad que tomó el archivo de datos de 210 elementos en formato CNTK y, luego, usé el archivo para generar un archivo de datos de aprendizaje de 150 elementos denominado seeds_train_data.txt (los primeros 50 de cada variedad) y un archivo de prueba de 60 elementos denominado seeds_test_data.txt (los primeros 20 de cada variedad).

Dado que existen siete variables predictoras, no es viable elaborar un gráfico completo de los datos. No obstante, puede hacerse una idea de la estructura de los datos con el gráfico de datos parciales de la Figura 3. Usé solo los valores predictores de perímetro y compacidad de las semillas del conjunto de datos de prueba de 60 elementos.

Gráfico parcial de los datos de prueba
Figura 3 Gráfico parcial de los datos de prueba

Programa de demostración de la red neuronal profunda

Usé el Bloc de notas para escribir el programa de demostración. Me gusta el Bloc de notas, pero la mayoría de mis colegas prefieren uno de los numerosos y excelentes editores de Python disponibles. El editor Visual Studio Code gratuito con el complemento de lenguaje Python es especialmente bueno. El código fuente del programa de demostración completo, con ediciones menores para ahorrar espacio, se presenta en la Figura 4. Observe que Python usa el carácter de barra diagonal inversa para la continuación de línea.

Figura 4 Programa de demostración de clasificador de semillas completo

# seeds_dnn.py
# classify wheat seed variety
import numpy as np
import cntk as C
def create_reader(path, is_training, input_dim, output_dim):
  strm_x = C.io.StreamDef(field='properties',
    shape=input_dim, is_sparse=False)
  strm_y = C.io.StreamDef(field='variety',
    shape=output_dim, is_sparse=False)
  streams = C.io.StreamDefs(x_src=strm_x,
    y_src=strm_y)
  deserial = C.io.CTFDeserializer(path, streams)
  sweeps = C.io.INFINITELY_REPEAT if is_training else 1
  mb_source = C.io.MinibatchSource(deserial,
    randomize=is_training, max_sweeps=sweeps)
  return mb_source
def main():
  print("\nBegin wheat seed classification demo  \n")
  print("Using CNTK verson = " + str(C.__version__) + "\n")
  input_dim = 7
  hidden_dim = 4
  output_dim = 3
  train_file = ".\\Data\\seeds_train_data.txt"
  test_file = ".\\Data\\seeds_test_data.txt"
  # 1. create network and model
  X = C.ops.input_variable(input_dim, np.float32)
  Y = C.ops.input_variable(output_dim, np.float32)
  print("Creating a 7-(4-4-4)-3 tanh softmax NN for seed data ")
  with C.layers.default_options(init= \
    C.initializer.normal(scale=0.1, seed=2)):
    h1 = C.layers.Dense(hidden_dim, activation=C.ops.tanh,
      name='hidLayer1')(X)
    h2 = C.layers.Dense(hidden_dim, activation=C.ops.tanh,
      name='hidLayer2')(h1)
    h3 = C.layers.Dense(hidden_dim, activation=C.ops.tanh,
      name='hidLayer3')(h2)
    oLayer = C.layers.Dense(output_dim, activation=None,
      name='outLayer')(h3)
  nnet = oLayer
  model = C.softmax(nnet)
  # 2. create learner and trainer
  print("Creating a cross entropy, SGD with LR=0.01, \
    batch=10 Trainer \n")
  tr_loss = C.cross_entropy_with_softmax(nnet, Y)
  tr_clas = C.classification_error(nnet, Y)
  learn_rate = 0.01
  learner = C.sgd(nnet.parameters, learn_rate)
  trainer = C.Trainer(nnet, (tr_loss, tr_clas), [learner])
  max_iter = 5000  # maximum training iterations
  batch_size = 10   # mini-batch size
  # 3. create data reader
  rdr = create_reader(train_file, True, input_dim,
    output_dim)
  my_input_map = {
    X : rdr.streams.x_src,
    Y : rdr.streams.y_src
  }
  # 4. train
  print("Starting training \n")
  for i in range(0, max_iter):
    curr_batch = rdr.next_minibatch(batch_size,
      input_map=my_input_map)
    trainer.train_minibatch(curr_batch)
    if i % 1000 == 0:
      mcee = trainer.previous_minibatch_loss_average
      pmea = trainer.previous_minibatch_evaluation_average
      macc = (1.0 - pmea) * 100
      print("batch %6d: mean loss = %0.4f, \
        mean accuracy = %0.2f%% " % (i,mcee, macc))
  print("\nTraining complete")
  # 5. evaluate model on the test data
  print("\nEvaluating test data \n")
  rdr = create_reader(test_file, False, input_dim, output_dim)
  my_input_map = {
    X : rdr.streams.x_src,
    Y : rdr.streams.y_src
  }
  numTest = 60
  allTest = rdr.next_minibatch(numTest, input_map=my_input_map)
  acc = (1.0 - trainer.test_minibatch(allTest)) * 100
  print("Classification accuracy on the \
    60 test items = %0.2f%%" % acc)
  # (could save model here)
  # 6. use trained model to make prediction
  np.set_printoptions(precision = 4)
  unknown = np.array([[17.6, 15.9, 0.8, 6.2, 3.5, 4.1, 6.1]],
    dtype=np.float32)
  print("\nPredicting variety for (non-normalized) seed features: ")
  print(unknown[0])
  raw_out = nnet.eval({X: unknown})
  print("\nRaw output values are: ")
  for i in range(len(raw_out[0])):
    print("%0.4f " % raw_out[0][i])
  pred_prob = model.eval({X: unknown})
  print("\nPrediction probabilities are: ")
  for i in range(len(pred_prob[0])):
    print("%0.4f " % pred_prob[0][i])
  print("\nEnd demo \n ")
# main()
if __name__ == "__main__":
  main()

Para empezar, la demo importa los paquetes de NumPy y CNTK necesarios, y les asigna los alias de acceso directo de np y C. La función create_reader es una aplicación auxiliar definida por el programa que se puede usar para leer datos de aprendizaje (si el parámetro is_training está establecido en True) o datos de prueba (si is_training está establecido en False).

Puede considerar la función create_reader como código reutilizable para los problemas de clasificación neuronal. Lo único que deberá cambiar en la mayoría de los casos son los dos valores de cadena de los argumentos de los campos de las llamadas a la función StreamDef, "properties" y "varieties" en la demo.

Toda la lógica de control del programa se incluye en una sola función main. Todo el código de comprobación de errores normal se quitó para mantener un tamaño reducido de la demo y para ayudar a aclarar las ideas principales. Observe que, para ahorrar espacio, aplico una sangría de dos espacios en lugar de los cuatro espacios más habituales.

Creación de la red y el modelo

La función main comienza con la configuración de las dimensiones de la arquitectura de la red neuronal:

def main():
  print("Begin wheat seed classification demo")
  print("Using CNTK verson = " + str(C.__version__) )
  input_dim = 7
  hidden_dim = 4
  output_dim = 3
...

Dado que CNTK se desarrolla rápidamente, es una buena idea imprimir o anotar la versión que se está usando. La demo tiene tres capas ocultas, todas ellas con cuatro nodos. El número de capas ocultas y el número de nodos de cada capa se deben determinar por prueba y error. Puede tener un número diferente de nodos en cada capa si lo desea. Por ejemplo, hidden_dim = [10, 8, 10, 12] correspondería a una red profunda con cuatro capas ocultas, y con 10, 8, 10 y 12 nodos respectivamente.

A continuación, se especifica la ubicación de los archivos de datos de pruebas y aprendizaje, y se crean los vectores de entrada y salida de la red:

train_file = ".\\Data\\seeds_train_data.txt"
test_file = ".\\Data\\seeds_test_data.txt"
# 1. create network and model
X = C.ops.input_variable(input_dim, np.float32)
Y = C.ops.input_variable(output_dim, np.float32)

Observe que coloqué los archivos de pruebas y aprendizaje en un subdirectorio de datos diferente, una práctica común porque a menudo tiene muchos archivos de datos distintos durante la creación del modelo. Es mucho más habitual usar el tipo de datos np.float32 que el tipo np.float64, ya que la precisión adicional que se obtiene con 64 bits no suele merecer la pena dada la pérdida de rendimiento ocasionada.

A continuación, se crea la red:

print("Creating a 7-(4-4-4)-3 NN for seed data ")
with C.layers.default_options(init= \
  C.initializer.normal(scale=0.1, seed=2)):
  h1 = C.layers.Dense(hidden_dim,
    activation=C.ops.tanh, name='hidLayer1')(X)
  h2 = C.layers.Dense(hidden_dim, activation=C.ops.tanh,
    name='hidLayer2')(h1)
  h3 = C.layers.Dense(hidden_dim, activation=C.ops.tanh,
    name='hidLayer3')(h2)
  oLayer = C.layers.Dense(output_dim, activation=None,
    name='outLayer')(h3)
nnet = oLayer
model = C.softmax(nnet)

Suceden muchas cosas en este código. La instrucción with de Python es una sintaxis de acceso directo para aplicar un conjunto de valores comunes a varias capas de una red. Aquí, todos los pesos reciben un valor aleatorio gaussiano (curva en forma de campana) con una desviación estándar de 0,1 y una media de 0. La configuración de un valor de seed garantiza la reproducibilidad. CNTK admite un gran número de algoritmos de inicialización, incluidos "uniform", "glorot", "he" y "xavier". Las redes neuronales profundas suelen ser increíblemente sensibles a la elección del algoritmo de inicialización, por lo que, si se produce un error en el aprendizaje, una de las primeras cosas que debe probar es un algoritmo de inicialización alternativo.

Las tres capas ocultas se definen mediante la función Dense, que recibe este nombre porque cada uno de los nodos se conecta completamente a los nodos de las capas antes y después. La sintaxis usada puede ser confusa. Aquí, X actúa como entrada para la capa oculta h1. La capa h1 actúa como entrada para la capa oculta h2, y así sucesivamente.

Observe que la capa de salida no usa ninguna función activation, por lo que los nodos de salida tendrán valores que no sumarán necesariamente 1. Si tiene experiencia con otras bibliotecas de redes neuronales, esto requiere alguna explicación. Con muchas otras bibliotecas neuronales, usaría la función de activación softmax en la capa de salida. Por tanto, el valor de salida siempre sumará 1 y se puede interpretar como probabilidades. Luego, durante el aprendizaje, usaría el error de entropía cruzada (también denominado "pérdida de registro"), que requiere un conjunto de valores que suman 1.

No obstante, de manera un poco sorprendente, CNTK v2.3 no tiene ninguna función de error de entropía cruzada básica para el aprendizaje. En su lugar, CNTK tiene una entropía cruzada con la función softmax. Esto significa que, durante el aprendizaje, los valores de los nodos de salida se convierten sobre la marcha en probabilidades mediante softmax para calcular un término de error.

Por tanto, con CNTK, se entrena una red profunda basándose en valores de nodos de salida sin procesar. No obstante, al realizar predicciones, si quiere probabilidades de predicción, que es lo habitual, debe aplicar la función softmax de manera explícita. El enfoque que se usa en la demo consiste en entrenar el objeto "nnet" (sin activación en la capa de salida), pero crear un objeto "model" adicional, con la función softmax aplicada, para usar al realizar predicciones.

De hecho, ahora se puede usar la función de activación softmax en la capa de salida y, luego, usar la entropía cruzada con softmax durante el aprendizaje. Con este enfoque, softmax se aplica dos veces, primero a los valores de salida sin procesar y, luego, nuevamente a los valores de los nodos de salida normalizados. Parece ser que, aunque este enfoque funcionará, por motivos técnicos bastante complejos, el aprendizaje no es tan eficiente.

Encadenar capas ocultas es viable hasta cierto punto. En el caso de redes muy profundas, CNTK admite una metafunción denominada Sequential, que proporciona una sintaxis de acceso directo para crear redes de varias capas. La biblioteca de CNTK también presenta una función Dropout, que ayuda a evitar el sobreajuste del modelo. Por ejemplo, para agregar Dropout a la primera capa oculta, puede modificar el código de la demostración de la siguiente manera:

h1 = C.layers.Dense(hidden_dim, activation=C.ops.tanh,
  name='hidLayer1')(X)
d1 = C.layers.Dropout(0.50, name='drop1')(h1)
h2 = C.layers.Dense(hidden_dim, activation=C.ops.tanh,
  name='hidLayer2')(d1)
h3 = C.layers.Dense(hidden_dim, activation=C.ops.tanh,
  name='hidLayer3')(h2)
oLayer = C.layers.Dense(output_dim, activation=None,
  name='outLayer')(h3)

Muchos de mis colegas prefieren usar siempre Sequential, incluso para las redes neuronales profundas que solo tienen unas pocas capas ocultas. Prefiero el encadenamiento manual, aunque es solo cuestión de estilo.

Entrenamiento de la red

Después de crear una red neuronal y un modelo, el programa de demostración crea un objeto Learner y un objeto Trainer:

print("Creating a Trainer \n")
tr_loss = C.cross_entropy_with_softmax(nnet, Y)
tr_clas = C.classification_error(nnet, Y)
learn_rate = 0.01
learner = C.sgd(nnet.parameters, learn_rate)
trainer = C.Trainer(nnet, (tr_loss, tr_clas), [learner])

Puede pensar en Learner como un algoritmo y en Trainer como un objeto que usa el algoritmo Learner. El objeto tr_loss ("training loss") define cómo medir el error entre los valores de salida calculados por la red y los valores de salida correctos conocidos de los datos de aprendizaje. Para la clasificación, se usa la entropía cruzada prácticamente siempre, pero CNTK admite varias alternativas. La parte "with_softmax" del nombre de la función indica que la función espera valores de nodos de salida sin procesar en lugar de valores normalizados con softmax. Este es el motivo por el que la capa de salida no usa una función activation.

El objeto tr_clas ("training classification error") define cómo se calcula el número de predicciones correctas e incorrectas durante el aprendizaje. CNTK define una función de biblioteca de error de clasificación (porcentaje de predicciones incorrectas) en lugar de la función de precisión de clasificación que usan otras bibliotecas. Por tanto, existen dos tipos de error que se calculan durante el aprendizaje. El error tr_loss se usa para ajustar los pesos y los sesgos. El error tr_clas se usa para supervisar la precisión de la predicción.

El objeto Learner usa el algoritmo SGD con una velocidad de aprendizaje constante establecida en 0.01. SGD es el algoritmo de entrenamiento más simple, pero raramente es el que presenta un mayor rendimiento. CNTK admite un gran número de algoritmos Learner, algunos de los cuales son muy complejos. Como regla de oro, recomiendo empezar con SGD y probar algoritmos más exóticos únicamente en caso de error de aprendizaje. El algoritmo Adam (Adam no es un acrónimo) suele ser mi segunda opción.

Observe la sintaxis inusual para crear un objeto Trainer. Los dos objetos de función loss se pasan como una tupla de Python (entre paréntesis), pero el objeto Learner se pasa como una lista de Python (entre corchetes). Puede pasar varios objetos Learner a un objeto Trainer, pero en el programa de demostración solo se pasa uno.

El código que realiza el aprendizaje realmente es el siguiente:

for i in range(0, max_iter):
  curr_batch = rdr.next_minibatch(batch_size,
    input_map=my_input_map)
  trainer.train_minibatch(curr_batch)
  if i % 1000 == 0:
    mcee = trainer.previous_minibatch_loss_average
    pmea = trainer.previous_minibatch_evaluation_average
    macc = (1.0 - pmea) * 100
    print("batch %6d: mean loss = %0.4f, \
      mean accuracy = %0.2f%% " % (i, mcee, macc))

Es importante supervisar el progreso del aprendizaje porque el aprendizaje genera errores con frecuencia. Aquí, el error de entropía cruzada media en el lote recién usado de 10 elementos de aprendizaje se muestra cada 1000 iteraciones. La demo muestra la precisión de clasificación media (porcentaje de predicciones correctas en los 10 elementos actuales), que creo que es una métrica más natural que el error de clasificación (porcentaje de predicciones incorrectas).

Almacenamiento del modelo entrenado

Dado que solo existen 150 elementos de aprendizaje, la red neuronal de demostración se puede entrenar en tan solo unos segundos. No obstante, en escenarios que no son de demostración, el entrenamiento de una red neuronal muy profunda puede requerir horas, días o incluso más tiempo. Después del entrenamiento, querrá guardar el modelo para no tener que volver a entrenarlo desde cero. El almacenamiento y la carga de un modelo de CNTK entrenado son operaciones muy sencillas. Para guardarlo, puede agregar código como el siguiente al programa de demostración:

mdl = ".\\Models\\seed_dnn.model"
model.save(mdl, format=C.ModelFormat.CNTKv2)

El primer argumento pasado a la función save es solo un nombre de archivo y puede incluir una ruta. No existe una extensión de archivo obligatoria, pero tiene sentido usar ".model". El parámetro de formato tiene el valor predeterminado ModelFormat.CNTKv2, por lo que se podría haber omitido. Una alternativa es usar el nuevo formato de intercambio de red neuronal abierta (ONNX).

Recuerde que el programa de demostración creó un objeto nnet (sin ninguna función softmax en la salida) y un objeto model (con softmax). Por lo general, guardará la versión con softmax de un modelo entrenado, pero puede guardar el objeto sin softmax si lo desea.

Después de guardar un modelo, puede cargarlo en la memoria de la siguiente manera:

model = C.ops.functions.Function.Load(".\\Models\\seed_dnn.model")

Luego, el modelo se puede usar como si se hubiese entrenado. Observe que existe algo de asimetría en las llamadas a save y load (save es un método en un objeto Function y load es un método estático de la clase Function).

Resumen

Muchos problemas de clasificación se pueden controlar mediante una red neuronal con realimentación (FNN) simple con una sola capa oculta. En teoría, dadas unas suposiciones determinadas, una red FNN puede controlar cualquier problema que una red neuronal profunda pueda controlar. No obstante, en la práctica, a veces es más fácil entrenar una red neuronal profunda que una red FNN. El fundamento matemático de estas ideas es el teorema de aproximación universal o teorema de Cybenko.

Si es nuevo en la clasificación de redes neuronales, el número de decisiones que deberá tomar puede parecer una amenaza. Debe decidir el número de capas ocultas, el número de nodos de cada capa, un esquema de inicialización y una función de activación para cada capa oculta, un algoritmo de entrenamiento y los parámetros del algoritmo de entrenamiento, como la velocidad de aprendizaje y el término de momento. No obstante, con la práctica, podrá desarrollar rápidamente un conjunto de reglas de oro para los tipos de problemas a los que se enfrenta.


El Dr. James McCaffrey trabaja para Microsoft Research en Redmond, Washington. Ha colaborado en el desarrollo de varios productos de Microsoft como, por ejemplo, Internet Explorer y Bing. Puede ponerse en contacto con el Dr. McCaffrey en jamccaff@microsoft.com.

Gracias a los siguientes expertos técnicos de Microsoft por revisar este artículo: Chris Lee, Ricky Loynd y Kenneth Tran


Discuta sobre este artículo en el foro de MSDN Magazine