CNTK - 序列分类

在本章中,我们将详细了解 CNTK 中的序列及其分类。

张量

CNTK 工作所基于的概念是张量。基本上,CNTK 输入、输出以及参数被组织为张量,这通常被认为是一个广义矩阵。每个张量都有一个

  • 秩为 0 的张量是标量。

  • 秩为 1 的张量是矢量。

  • 秩为 2 的张量是矩阵。

此处,这些不同的维度称为轴。

静态轴和动态轴

顾名思义,静态轴在整个网络生命周期中具有相同的长度。另一方面,动态轴的长度可能因实例而异。事实上,在呈现每个小批量之前,它们的长度通常是未知的。

动态轴与静态轴类似,因为它们也定义了张量中包含的数字的有意义的分组。

示例

为了更清楚起见,让我们看看如何在 CNTK 中表示短视频片段的小批量。假设视频片段的分辨率均为 640 * 480。而且,这些片段是彩色拍摄的,通常用三个通道编码。这进一步意味着我们的小批量具有以下 −

  • 3 个静态轴,长度分别为 640、480 和 3。

  • 两个动态轴;视频的长度和小批量轴。

这意味着,如果一个小批量有 16 个视频,每个视频长 240 帧,则将表示为 16*240*3*640*480 个张量。

使用 CNTK 中的序列

让我们首先了解长短期记忆网络,以了解 CNTK 中的序列。

长短期记忆网络 (LSTM)

长短期记忆网络

长短期记忆 (LSTM) 网络由 Hochreiter 和 Schmidhuber 引入。它解决了让基本循环层长时间记住事物的问题。上图给出了 LSTM 的架构。我们可以看到,它有输入神经元、记忆单元和输出神经元。为了解决梯度消失问题,长短期记忆网络使用显式记忆单元(存储先前的值)和以下门 −

  • Forget gate(忘记门) − 顾名思义,它告诉记忆单元忘记先前的值。记忆单元存储这些值,直到门,即"忘记门"告诉它忘记它们。

  • Input gate(输入门) − 顾名思义,它会将新的东西添加到单元中。

  • Output gate(输出门) −顾名思义,输出门决定何时将向量从单元传递到下一个隐藏状态。

在 CNTK 中处理序列非常容易。让我们借助以下示例来看一下 −

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

上述程序的详细解释将在下一节中介绍,特别是当我们构建循环神经网络时。