CNTK - Classificação de Sequência
Neste capítulo, aprenderemos em detalhes sobre as sequências no CNTK e sua classificação.
Tensores
O conceito no qual CNTK trabalha é tensor. Basicamente, as entradas, saídas e parâmetros do CNTK são organizados comotensors, que geralmente é considerada uma matriz generalizada. Cada tensor tem umrank -
Tensor de classificação 0 é um escalar.
Tensor de classificação 1 é um vetor.
Tensor de classificação 2 é amatrix.
Aqui, essas diferentes dimensões são referidas como axes.
Eixos estáticos e eixos dinâmicos
Como o nome indica, os eixos estáticos têm o mesmo comprimento ao longo da vida da rede. Por outro lado, o comprimento dos eixos dinâmicos pode variar de instância para instância. Na verdade, seu comprimento normalmente não é conhecido antes de cada minibatch ser apresentado.
Os eixos dinâmicos são como eixos estáticos porque também definem um agrupamento significativo dos números contidos no tensor.
Exemplo
Para deixar mais claro, vamos ver como um minibatch de clipes de vídeo curtos é representado no CNTK. Suponha que a resolução dos clipes de vídeo seja de 640 * 480. E também os clipes são filmados em cores que são normalmente codificadas com três canais. Além disso, significa que nosso minibatch tem o seguinte -
3 eixos estáticos de comprimento 640, 480 e 3, respectivamente.
Dois eixos dinâmicos; a duração do vídeo e os eixos do minibatch.
Isso significa que se um minibatch tiver 16 vídeos, cada um dos quais com 240 quadros, será representado como 16*240*3*640*480 tensores.
Trabalhando com sequências em CNTK
Vamos entender as sequências em CNTK aprendendo primeiro sobre Rede de Memória de Longo-Curto Prazo.
Rede de memória de longo-curto prazo (LSTM)
As redes de memória de longo-curto prazo (LSTMs) foram introduzidas por Hochreiter & Schmidhuber. Resolveu o problema de obter uma camada básica recorrente para lembrar coisas por um longo tempo. A arquitetura do LSTM é fornecida acima no diagrama. Como podemos ver, ele possui neurônios de entrada, células de memória e neurônios de saída. A fim de combater o problema do gradiente de desaparecimento, as redes de memória de longo-curto prazo usam uma célula de memória explícita (armazena os valores anteriores) e as seguintes portas -
Forget gate- Como o nome indica, diz à célula de memória para esquecer os valores anteriores. A célula de memória armazena os valores até que o portão, ou seja, 'esqueça o portão', diga para esquecê-los.
Input gate - Como o nome indica, adiciona coisas novas à célula.
Output gate - Como o nome indica, a porta de saída decide quando passar os vetores da célula para o próximo estado oculto.
É muito fácil trabalhar com sequências no CNTK. Vamos ver com a ajuda do seguinte exemplo -
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
A explicação detalhada do programa acima será abordada nas próximas seções, especialmente quando estivermos construindo redes neurais recorrentes.