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.