CNTK - Clasificación de secuencia
En este capítulo, aprenderemos en detalle sobre las secuencias en CNTK y su clasificación.
Tensores
El concepto sobre el que trabaja CNTK es tensor. Básicamente, las entradas, salidas y parámetros de CNTK se organizan comotensors, que a menudo se considera una matriz generalizada. Cada tensor tiene unrank -
El tensor de rango 0 es un escalar.
El tensor de rango 1 es un vector.
El tensor del rango 2 es una matriz.
Aquí, estas diferentes dimensiones se denominan axes.
Ejes estáticos y ejes dinámicos
Como su nombre lo indica, los ejes estáticos tienen la misma longitud durante toda la vida de la red. Por otro lado, la longitud de los ejes dinámicos puede variar de una instancia a otra. De hecho, normalmente no se conoce su longitud antes de que se presente cada minibatch.
Los ejes dinámicos son como ejes estáticos porque también definen una agrupación significativa de los números contenidos en el tensor.
Ejemplo
Para hacerlo más claro, veamos cómo se representa un minibatch de videoclips cortos en CNTK. Suponga que la resolución de los clips de vídeo es de 640 * 480. Y también los clips se graban en color, que normalmente se codifica con tres canales. Además, significa que nuestro minibatch tiene lo siguiente:
3 ejes estáticos de longitud 640, 480 y 3 respectivamente.
Dos ejes dinámicos; la duración del video y los ejes del minibatch.
Significa que si un minibatch tiene 16 videos, cada uno de los cuales tiene 240 cuadros de longitud, se representaría como 16*240*3*640*480 tensores.
Trabajar con secuencias en CNTK
Entendamos las secuencias en CNTK aprendiendo primero sobre la Red de memoria a corto y largo plazo.
Red de memoria a largo plazo (LSTM)
Las redes de memoria a largo plazo a corto plazo (LSTM) fueron introducidas por Hochreiter & Schmidhuber. Resolvió el problema de conseguir una capa recurrente básica para recordar cosas durante mucho tiempo. La arquitectura de LSTM se muestra arriba en el diagrama. Como podemos ver, tiene neuronas de entrada, células de memoria y neuronas de salida. Para combatir el problema del gradiente que desaparece, las redes de memoria a corto y largo plazo utilizan una celda de memoria explícita (almacena los valores anteriores) y las siguientes puertas:
Forget gate- Como su nombre lo indica, le dice a la celda de memoria que olvide los valores anteriores. La celda de memoria almacena los valores hasta que la puerta, es decir, "puerta olvidada", le dice que los olvide.
Input gate - Como su nombre lo indica, agrega cosas nuevas a la celda.
Output gate - Como su nombre lo indica, la puerta de salida decide cuándo pasar los vectores de la celda al siguiente estado oculto.
Es muy fácil trabajar con secuencias en CNTK. Veámoslo con la ayuda del siguiente ejemplo:
import sys
import os
from cntk import Trainer, Axis
from cntk.io import MinibatchSource, CTFDeserializer, StreamDef, StreamDefs,\
INFINITELY_REPEAT
from cntk.learners import sgd, learning_parameter_schedule_per_sample
from cntk import input_variable, cross_entropy_with_softmax, \
classification_error, sequence
from cntk.logging import ProgressPrinter
from cntk.layers import Sequential, Embedding, Recurrence, LSTM, Dense
def create_reader(path, is_training, input_dim, label_dim):
return MinibatchSource(CTFDeserializer(path, StreamDefs(
features=StreamDef(field='x', shape=input_dim, is_sparse=True),
labels=StreamDef(field='y', shape=label_dim, is_sparse=False)
)), randomize=is_training,
max_sweeps=INFINITELY_REPEAT if is_training else 1)
def LSTM_sequence_classifier_net(input, num_output_classes, embedding_dim,
LSTM_dim, cell_dim):
lstm_classifier = Sequential([Embedding(embedding_dim),
Recurrence(LSTM(LSTM_dim, cell_dim)),
sequence.last,
Dense(num_output_classes)])
return lstm_classifier(input)
def train_sequence_classifier():
input_dim = 2000
cell_dim = 25
hidden_dim = 25
embedding_dim = 50
num_output_classes = 5
features = sequence.input_variable(shape=input_dim, is_sparse=True)
label = input_variable(num_output_classes)
classifier_output = LSTM_sequence_classifier_net(
features, num_output_classes, embedding_dim, hidden_dim, cell_dim)
ce = cross_entropy_with_softmax(classifier_output, label)
pe = classification_error(classifier_output, label)
rel_path = ("../../../Tests/EndToEndTests/Text/" +
"SequenceClassification/Data/Train.ctf")
path = os.path.join(os.path.dirname(os.path.abspath(__file__)), rel_path)
reader = create_reader(path, True, input_dim, num_output_classes)
input_map = {
features: reader.streams.features,
label: reader.streams.labels
}
lr_per_sample = learning_parameter_schedule_per_sample(0.0005)
progress_printer = ProgressPrinter(0)
trainer = Trainer(classifier_output, (ce, pe),
sgd(classifier_output.parameters, lr=lr_per_sample),progress_printer)
minibatch_size = 200
for i in range(255):
mb = reader.next_minibatch(minibatch_size, input_map=input_map)
trainer.train_minibatch(mb)
evaluation_average = float(trainer.previous_minibatch_evaluation_average)
loss_average = float(trainer.previous_minibatch_loss_average)
return evaluation_average, loss_average
if __name__ == '__main__':
error, _ = train_sequence_classifier()
print(" error: %f" % error)
average since average since examples
loss last metric last
------------------------------------------------------
1.61 1.61 0.886 0.886 44
1.61 1.6 0.714 0.629 133
1.6 1.59 0.56 0.448 316
1.57 1.55 0.479 0.41 682
1.53 1.5 0.464 0.449 1379
1.46 1.4 0.453 0.441 2813
1.37 1.28 0.45 0.447 5679
1.3 1.23 0.448 0.447 11365
error: 0.333333
La explicación detallada del programa anterior se cubrirá en las siguientes secciones, especialmente cuando estemos construyendo redes neuronales recurrentes.