CNTK - klasyfikacja sekwencji
W tym rozdziale dowiemy się szczegółowo o sekwencjach w CNTK i ich klasyfikacji.
Tensory
Koncepcja, nad którą pracuje CNTK, to tensor. Zasadniczo wejścia i wyjścia CNTK, a także parametry są zorganizowane jakotensors, który jest często uważany za uogólnioną macierz. Każdy tensor marank -
Tensor rzędu 0 jest skalarem.
Tensor rzędu 1 jest wektorem.
Tensor rzędu 2 to amatrix.
Tutaj te różne wymiary są nazywane axes.
Osie statyczne i dynamiczne
Jak sama nazwa wskazuje, statyczne osie mają tę samą długość przez cały okres eksploatacji sieci. Z drugiej strony długość dynamicznych osi może się różnić w zależności od instancji. W rzeczywistości ich długość zazwyczaj nie jest znana przed przedstawieniem każdej minibatchu.
Osie dynamiczne są jak osie statyczne, ponieważ definiują również znaczące zgrupowanie liczb zawartych w tensorze.
Przykład
Aby było jaśniej, zobaczmy, jak niewielka partia krótkich klipów wideo jest reprezentowana w CNTK. Załóżmy, że wszystkie klipy wideo mają rozdzielczość 640 * 480. A także klipy są nagrane w kolorze, który jest zwykle kodowany trzema kanałami. Oznacza to ponadto, że nasz minibatch ma następujące -
3 osie statyczne o długości odpowiednio 640, 480 i 3.
Dwie dynamiczne osie; długość filmu i osie minibatchu.
Oznacza to, że jeśli minibatch zawiera 16 filmów, z których każdy ma 240 klatek, zostanie przedstawiony jako 16*240*3*640*480 tensory.
Praca z sekwencjami w CNTK
Zrozummy sekwencje w CNTK, najpierw poznając Sieć Pamięci Długoterminowej.
Sieć pamięci długoterminowej (LSTM)
Sieci pamięci długoterminowej (LSTM) zostały wprowadzone przez firmę Hochreiter & Schmidhuber. Rozwiązał problem uzyskania podstawowej powtarzającej się warstwy, aby pamiętać rzeczy przez długi czas. Architekturę LSTM przedstawiono powyżej na schemacie. Jak widzimy, ma neurony wejściowe, komórki pamięci i neurony wyjściowe. Aby zwalczyć problem znikającego gradientu, sieci pamięci długoterminowej używają jawnej komórki pamięci (przechowuje poprzednie wartości) i następujących bramek -
Forget gate- Jak sama nazwa wskazuje, mówi komórce pamięci, aby zapomniała poprzednich wartości. Komórka pamięci przechowuje wartości do momentu, gdy bramka, tj. „Zapomnij bramę”, powie jej, aby je zapomnieć.
Input gate - Jak sama nazwa wskazuje, dodaje nowe rzeczy do komórki.
Output gate - Jak sama nazwa wskazuje, bramka wyjściowa decyduje, kiedy przejść wzdłuż wektorów z komórki do następnego stanu ukrytego.
Praca z sekwencjami w CNTK jest bardzo łatwa. Zobaczmy to na poniższym przykładzie -
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
Szczegółowe wyjaśnienie powyższego programu zostanie omówione w następnych sekcjach, zwłaszcza gdy będziemy budować rekurencyjne sieci neuronowe.