CNTK - Clasificación de redes neuronales

En este capítulo, estudiaremos cómo clasificar una red neuronal utilizando CNTK.

Introducción

La clasificación puede definirse como el proceso para predecir etiquetas de salida categóricas o respuestas para los datos de entrada dados. La salida categorizada, que se basará en lo que el modelo ha aprendido en la fase de entrenamiento, puede tener la forma "Negro" o "Blanco" o "spam" o "no spam".

Por otro lado, matemáticamente, es la tarea de aproximar una función de mapeo, digamos f de las variables de entrada digamos X a las variables de salida digamos Y.

Un ejemplo clásico de problema de clasificación puede ser la detección de spam en correos electrónicos. Es obvio que solo puede haber dos categorías de salida, "spam" y "no spam".

Para implementar dicha clasificación, primero necesitamos hacer un entrenamiento del clasificador donde los correos electrónicos "spam" y "no spam" se usarían como datos de entrenamiento. Una vez que el clasificador se haya entrenado correctamente, se puede usar para detectar un correo electrónico desconocido.

Aquí, vamos a crear un 4-5-3 NN usando un conjunto de datos de flores de iris con lo siguiente:

  • Nodos de 4 entradas (uno para cada valor predictor).

  • 5 nodos de procesamiento ocultos.

  • Nodos de 3 salidas (porque hay tres posibles especies en el conjunto de datos de iris).

Cargando conjunto de datos

Usaremos un conjunto de datos de flores de iris, a partir del cual queremos clasificar las especies de flores de iris en función de las propiedades físicas del ancho y largo del sépalo, y del ancho y largo del pétalo. El conjunto de datos describe las propiedades físicas de diferentes variedades de flores de iris:

  • Longitud del sépalo

  • Ancho del sépalo

  • Longitud del pétalo

  • Ancho del pétalo

  • Clase ie iris setosa o iris versicolor o iris virginica

Tenemos iris.CSVarchivo que usamos antes en capítulos anteriores también. Se puede cargar con la ayuda dePandasbiblioteca. Pero, antes de usarlo o cargarlo para nuestro clasificador, necesitamos preparar los archivos de entrenamiento y prueba, para que se pueda usar fácilmente con CNTK.

Preparación de archivos de prueba y entrenamiento

El conjunto de datos Iris es uno de los conjuntos de datos más populares para proyectos de AA. Tiene 150 elementos de datos y los datos sin procesar se ven de la siguiente manera:

5.1 3.5 1.4 0.2 setosa
4.9 3.0 1.4 0.2 setosa
…
7.0 3.2 4.7 1.4 versicolor
6.4 3.2 4.5 1.5 versicolor
…
6.3 3.3 6.0 2.5 virginica
5.8 2.7 5.1 1.9 virginica

Como se dijo anteriormente, los primeros cuatro valores de cada línea describen las propiedades físicas de las diferentes variedades, es decir, longitud del sépalo, ancho del sépalo, longitud del pétalo, ancho del pétalo de las flores del iris.

Pero, deberíamos tener que convertir los datos en el formato, que pueda ser usado fácilmente por CNTK y ese formato es un archivo .ctf (también creamos un iris.ctf en la sección anterior). Se verá así:

|attribs 5.1 3.5 1.4 0.2|species 1 0 0
|attribs 4.9 3.0 1.4 0.2|species 1 0 0
…
|attribs 7.0 3.2 4.7 1.4|species 0 1 0
|attribs 6.4 3.2 4.5 1.5|species 0 1 0
…
|attribs 6.3 3.3 6.0 2.5|species 0 0 1
|attribs 5.8 2.7 5.1 1.9|species 0 0 1

En los datos anteriores, la etiqueta | attribs marca el inicio del valor de la característica y la | especie etiqueta los valores de la etiqueta de clase. También podemos usar cualquier otro nombre de etiqueta de nuestro deseo, incluso podemos agregar el ID del artículo. Por ejemplo, observe los siguientes datos:

|ID 001 |attribs 5.1 3.5 1.4 0.2|species 1 0 0 |#setosa
|ID 002 |attribs 4.9 3.0 1.4 0.2|species 1 0 0 |#setosa
…
|ID 051 |attribs 7.0 3.2 4.7 1.4|species 0 1 0 |#versicolor
|ID 052 |attribs 6.4 3.2 4.5 1.5|species 0 1 0 |#versicolor
…

Hay un total de 150 elementos de datos en el conjunto de datos del iris y, para este ejemplo, usaremos la regla del conjunto de datos de retención 80-20, es decir, 80% (120 elementos) elementos de datos con fines de entrenamiento y 20% restante (30 elementos) elementos de datos para pruebas propósito.

Construyendo modelo de clasificación

Primero, necesitamos procesar los archivos de datos en formato CNTK y para eso usaremos la función auxiliar llamada create_reader como sigue -

def create_reader(path, input_dim, output_dim, rnd_order, sweeps):
x_strm = C.io.StreamDef(field='attribs', shape=input_dim, is_sparse=False)
y_strm = C.io.StreamDef(field='species', shape=output_dim, is_sparse=False)
streams = C.io.StreamDefs(x_src=x_strm, y_src=y_strm)
deserial = C.io.CTFDeserializer(path, streams)
mb_src = C.io.MinibatchSource(deserial, randomize=rnd_order, max_sweeps=sweeps)
return mb_src

Ahora, necesitamos establecer los argumentos de la arquitectura para nuestro NN y también proporcionar la ubicación de los archivos de datos. Se puede hacer con la ayuda del siguiente código de Python:

def main():
print("Using CNTK version = " + str(C.__version__) + "\n")
input_dim = 4
hidden_dim = 5
output_dim = 3
train_file = ".\\...\\" #provide the name of the training file(120 data items)
test_file = ".\\...\\" #provide the name of the test file(30 data items)

Ahora, con la ayuda de la siguiente línea de código, nuestro programa creará el NN no capacitado:

X = C.ops.input_variable(input_dim, np.float32)
Y = C.ops.input_variable(output_dim, np.float32)
with C.layers.default_options(init=C.initializer.uniform(scale=0.01, seed=1)):
hLayer = C.layers.Dense(hidden_dim, activation=C.ops.tanh, name='hidLayer')(X)
oLayer = C.layers.Dense(output_dim, activation=None, name='outLayer')(hLayer)
nnet = oLayer
model = C.ops.softmax(nnet)

Ahora, una vez que creamos el modelo dual sin entrenamiento, necesitamos configurar un objeto de algoritmo de aprendizaje y luego usarlo para crear un objeto de entrenamiento de entrenador. Vamos a utilizar aprendiz SGD ycross_entropy_with_softmax función de pérdida -

tr_loss = C.cross_entropy_with_softmax(nnet, Y)
tr_clas = C.classification_error(nnet, Y)
max_iter = 2000
batch_size = 10
learn_rate = 0.01
learner = C.sgd(nnet.parameters, learn_rate)
trainer = C.Trainer(nnet, (tr_loss, tr_clas), [learner])

Codifique el algoritmo de aprendizaje de la siguiente manera:

max_iter = 2000
batch_size = 10
lr_schedule = C.learning_parameter_schedule_per_sample([(1000, 0.05), (1, 0.01)])
mom_sch = C.momentum_schedule([(100, 0.99), (0, 0.95)], batch_size)
learner = C.fsadagrad(nnet.parameters, lr=lr_schedule, momentum=mom_sch)
trainer = C.Trainer(nnet, (tr_loss, tr_clas), [learner])

Ahora, una vez que terminamos con el objeto Trainer, necesitamos crear una función de lector para leer los datos de entrenamiento.

rdr = create_reader(train_file, input_dim, output_dim, rnd_order=True, sweeps=C.io.INFINITELY_REPEAT)
iris_input_map = { X : rdr.streams.x_src, Y : rdr.streams.y_src }

Ahora es el momento de entrenar nuestro modelo NN

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

Una vez que hayamos terminado con el entrenamiento, evaluemos el modelo usando elementos de datos de prueba:

print("\nEvaluating test data \n")
rdr = create_reader(test_file, input_dim, output_dim, rnd_order=False, sweeps=1)
iris_input_map = { X : rdr.streams.x_src, Y : rdr.streams.y_src }
num_test = 30
all_test = rdr.next_minibatch(num_test, input_map=iris_input_map) acc = (1.0 - trainer.test_minibatch(all_test)) * 100
print("Classification accuracy = %0.2f%%" % acc)

Después de evaluar la precisión de nuestro modelo NN entrenado, lo usaremos para hacer una predicción sobre datos invisibles:

np.set_printoptions(precision = 1, suppress=True)
unknown = np.array([[6.4, 3.2, 4.5, 1.5]], dtype=np.float32)
print("\nPredicting Iris species for input features: ")
print(unknown[0]) pred_prob = model.eval(unknown)
np.set_printoptions(precision = 4, suppress=True)
print("Prediction probabilities are: ")
print(pred_prob[0])

Modelo de clasificación completo

Import numpy as np
Import cntk as C
def create_reader(path, input_dim, output_dim, rnd_order, sweeps):
x_strm = C.io.StreamDef(field='attribs', shape=input_dim, is_sparse=False)
y_strm = C.io.StreamDef(field='species', shape=output_dim, is_sparse=False)
streams = C.io.StreamDefs(x_src=x_strm, y_src=y_strm)
deserial = C.io.CTFDeserializer(path, streams)
mb_src = C.io.MinibatchSource(deserial, randomize=rnd_order, max_sweeps=sweeps)
return mb_src
def main():
print("Using CNTK version = " + str(C.__version__) + "\n")
input_dim = 4
hidden_dim = 5
output_dim = 3
train_file = ".\\...\\" #provide the name of the training file(120 data items)
test_file = ".\\...\\" #provide the name of the test file(30 data items)
X = C.ops.input_variable(input_dim, np.float32)
Y = C.ops.input_variable(output_dim, np.float32)
with C.layers.default_options(init=C.initializer.uniform(scale=0.01, seed=1)):
hLayer = C.layers.Dense(hidden_dim, activation=C.ops.tanh, name='hidLayer')(X)
oLayer = C.layers.Dense(output_dim, activation=None, name='outLayer')(hLayer)
nnet = oLayer
model = C.ops.softmax(nnet)
tr_loss = C.cross_entropy_with_softmax(nnet, Y)
tr_clas = C.classification_error(nnet, Y)
max_iter = 2000
batch_size = 10
learn_rate = 0.01
learner = C.sgd(nnet.parameters, learn_rate)
trainer = C.Trainer(nnet, (tr_loss, tr_clas), [learner])
max_iter = 2000
batch_size = 10
lr_schedule = C.learning_parameter_schedule_per_sample([(1000, 0.05), (1, 0.01)])
mom_sch = C.momentum_schedule([(100, 0.99), (0, 0.95)], batch_size)
learner = C.fsadagrad(nnet.parameters, lr=lr_schedule, momentum=mom_sch)
trainer = C.Trainer(nnet, (tr_loss, tr_clas), [learner])
rdr = create_reader(train_file, input_dim, output_dim, rnd_order=True, sweeps=C.io.INFINITELY_REPEAT)
iris_input_map = { X : rdr.streams.x_src, Y : rdr.streams.y_src }
for i in range(0, max_iter):
curr_batch = rdr.next_minibatch(batch_size, input_map=iris_input_map) trainer.train_minibatch(curr_batch)
if i % 500 == 0:
mcee = trainer.previous_minibatch_loss_average
macc = (1.0 - trainer.previous_minibatch_evaluation_average) * 100
print("batch %4d: mean loss = %0.4f, accuracy = %0.2f%% " \ % (i, mcee, macc))
print("\nEvaluating test data \n")
rdr = create_reader(test_file, input_dim, output_dim, rnd_order=False, sweeps=1)
iris_input_map = { X : rdr.streams.x_src, Y : rdr.streams.y_src }
num_test = 30
all_test = rdr.next_minibatch(num_test, input_map=iris_input_map) acc = (1.0 - trainer.test_minibatch(all_test)) * 100
print("Classification accuracy = %0.2f%%" % acc)
np.set_printoptions(precision = 1, suppress=True)
unknown = np.array([[7.0, 3.2, 4.7, 1.4]], dtype=np.float32)
print("\nPredicting species for input features: ")
print(unknown[0])
pred_prob = model.eval(unknown)
np.set_printoptions(precision = 4, suppress=True)
print("Prediction probabilities: ")
print(pred_prob[0])
if __name__== ”__main__”:
main()

Salida

Using CNTK version = 2.7
batch 0: mean loss = 1.0986, mean accuracy = 40.00%
batch 500: mean loss = 0.6677, mean accuracy = 80.00%
batch 1000: mean loss = 0.5332, mean accuracy = 70.00%
batch 1500: mean loss = 0.2408, mean accuracy = 100.00%
Evaluating test data
Classification accuracy = 94.58%
Predicting species for input features:
[7.0 3.2 4.7 1.4]
Prediction probabilities:
[0.0847 0.736 0.113]

Guardar el modelo entrenado

Este conjunto de datos de Iris tiene solo 150 elementos de datos, por lo que solo tomaría unos segundos entrenar el modelo de clasificador NN, pero el entrenamiento en un conjunto de datos grande con cientos o miles de elementos de datos puede llevar horas o incluso días.

Podemos guardar nuestro modelo para que no tengamos que retenerlo desde cero. Con la ayuda de seguir el código de Python, podemos guardar nuestro NN entrenado:

nn_classifier = “.\\neuralclassifier.model” #provide the name of the file
model.save(nn_classifier, format=C.ModelFormat.CNTKv2)

A continuación se presentan los argumentos de save() función utilizada anteriormente -

  • El nombre de archivo es el primer argumento de save()función. También se puede escribir junto con la ruta del archivo.

  • Otro parámetro es el format parámetro que tiene un valor predeterminado C.ModelFormat.CNTKv2.

Cargando el modelo entrenado

Una vez que guardó el modelo entrenado, es muy fácil cargar ese modelo. Solo necesitamos usar elload ()función. Comprobemos esto en el siguiente ejemplo:

import numpy as np
import cntk as C
model = C.ops.functions.Function.load(“.\\neuralclassifier.model”)
np.set_printoptions(precision = 1, suppress=True)
unknown = np.array([[7.0, 3.2, 4.7, 1.4]], dtype=np.float32)
print("\nPredicting species for input features: ")
print(unknown[0])
pred_prob = model.eval(unknown)
np.set_printoptions(precision = 4, suppress=True)
print("Prediction probabilities: ")
print(pred_prob[0])

La ventaja del modelo guardado es que, una vez que carga un modelo guardado, se puede utilizar exactamente como si el modelo se acabara de entrenar.