CNTK - Classification des séquences

Dans ce chapitre, nous apprendrons en détail les séquences dans CNTK et sa classification.

Tenseurs

Le concept sur lequel travaille CNTK est tensor. Fondamentalement, les entrées, sorties et paramètres CNTK sont organisés commetensors, qui est souvent considérée comme une matrice généralisée. Chaque tenseur a unrank -

  • Le tenseur de rang 0 est un scalaire.

  • Le tenseur de rang 1 est un vecteur.

  • Le tenseur de rang 2 est une matrice.

Ici, ces différentes dimensions sont appelées axes.

Axes statiques et axes dynamiques

Comme son nom l'indique, les axes statiques ont la même longueur tout au long de la vie du réseau. En revanche, la longueur des axes dynamiques peut varier d'une instance à l'autre. En fait, leur longueur n'est généralement pas connue avant la présentation de chaque mini-match.

Les axes dynamiques sont comme des axes statiques car ils définissent également un regroupement significatif des nombres contenus dans le tenseur.

Exemple

Pour clarifier les choses, voyons comment un mini-lot de courts clips vidéo est représenté dans CNTK. Supposons que la résolution des clips vidéo est entièrement de 640 * 480. Et les clips sont également tournés en couleur qui est généralement encodée avec trois canaux. Cela signifie en outre que notre minibatch a ce qui suit -

  • 3 axes statiques de longueur 640, 480 et 3 respectivement.

  • Deux axes dynamiques; la longueur de la vidéo et les axes du mini-match.

Cela signifie que si un minibatch contient 16 vidéos dont chacune mesure 240 images, sera représenté comme 16*240*3*640*480 tenseurs.

Travailler avec des séquences dans CNTK

Laissez-nous comprendre les séquences dans CNTK en apprenant d'abord sur le réseau de mémoire à long terme.

Réseau de mémoire à long terme (LSTM)

Les réseaux de mémoire à long terme (LSTM) ont été introduits par Hochreiter & Schmidhuber. Cela a résolu le problème d'obtenir une couche récurrente de base pour se souvenir des choses pendant longtemps. L'architecture de LSTM est donnée ci-dessus dans le schéma. Comme nous pouvons le voir, il a des neurones d'entrée, des cellules mémoire et des neurones de sortie. Afin de lutter contre le problème du gradient de disparition, les réseaux de mémoire à long terme utilisent une cellule mémoire explicite (stocke les valeurs précédentes) et les portes suivantes -

  • Forget gate- Comme son nom l'indique, il indique à la cellule mémoire d'oublier les valeurs précédentes. La cellule de mémoire stocke les valeurs jusqu'à ce que la porte, c'est-à-dire «oublier la porte», lui dise de les oublier.

  • Input gate - Comme son nom l'indique, il ajoute de nouveaux éléments à la cellule.

  • Output gate - Comme son nom l'indique, la porte de sortie décide quand passer les vecteurs de la cellule à l'état caché suivant.

Il est très facile de travailler avec des séquences en CNTK. Voyons-le à l'aide de l'exemple suivant -

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

L'explication détaillée du programme ci-dessus sera couverte dans les sections suivantes, en particulier lorsque nous construirons des réseaux neuronaux récurrents.