CNTK - Classificazione delle sequenze

In questo capitolo impareremo in dettaglio le sequenze in CNTK e la sua classificazione.

Tensori

Il concetto su cui lavora CNTK è tensor. Fondamentalmente, gli input, gli output ei parametri CNTK sono organizzati come filetensors, che è spesso pensato come una matrice generalizzata. Ogni tensore ha un'estensionerank -

  • Il tensore di rango 0 è uno scalare.

  • Il tensore di rango 1 è un vettore.

  • Il tensore di rango 2 è una matrice.

Qui, queste diverse dimensioni sono indicate come axes.

Assi statici e assi dinamici

Come suggerisce il nome, gli assi statici hanno la stessa lunghezza per tutta la vita della rete. D'altra parte, la lunghezza degli assi dinamici può variare da istanza a istanza. In effetti, la loro lunghezza in genere non è nota prima che venga presentato ogni minibatch.

Gli assi dinamici sono come gli assi statici perché definiscono anche un raggruppamento significativo dei numeri contenuti nel tensore.

Esempio

Per renderlo più chiaro, vediamo come viene rappresentato in CNTK un minibatch di brevi video clip. Supponiamo che la risoluzione dei video clip sia tutta 640 * 480. Inoltre, anche i clip vengono ripresi a colori, che è tipicamente codificato con tre canali. Significa inoltre che il nostro minibatch ha quanto segue:

  • 3 assi statici di lunghezza 640, 480 e 3 rispettivamente.

  • Due assi dinamici; la lunghezza del video e gli assi del minibatch.

Significa che se un minibatch ha 16 video ciascuno dei quali è lungo 240 fotogrammi, sarebbe rappresentato come 16*240*3*640*480 tensori.

Lavorare con sequenze in CNTK

Cerchiamo di capire le sequenze in CNTK imparando prima a conoscere la rete di memoria a lungo termine.

Rete di memoria a lungo termine (LSTM)

Le reti di memoria a lungo termine (LSTM) sono state introdotte da Hochreiter & Schmidhuber. Ha risolto il problema di ottenere un livello ricorrente di base per ricordare le cose per molto tempo. L'architettura di LSTM è data sopra nel diagramma. Come possiamo vedere, ha neuroni di input, celle di memoria e neuroni di output. Per combattere il problema del gradiente di fuga, le reti di memoria a lungo termine utilizzano una cella di memoria esplicita (memorizza i valori precedenti) e le seguenti porte:

  • Forget gate- Come suggerisce il nome, dice alla cella di memoria di dimenticare i valori precedenti. La cella di memoria memorizza i valori fino a quando il gate, ovvero "dimentica porta", gli dice di dimenticarli.

  • Input gate - Come suggerisce il nome, aggiunge nuove cose alla cella.

  • Output gate - Come suggerisce il nome, la porta di uscita decide quando passare lungo i vettori dalla cella al successivo stato nascosto.

È molto facile lavorare con le sequenze in CNTK. Vediamolo con l'aiuto del seguente esempio:

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 spiegazione dettagliata del programma di cui sopra sarà trattata nelle sezioni successive, specialmente quando costruiremo reti neurali ricorrenti.