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 的架构如上图所示。正如我们所看到的,它有输入神经元、记忆细胞和输出神经元。为了解决梯度消失问题,长期短期记忆网络使用显式记忆单元(存储先前的值)和以下门 -

  • 忘记门- 顾名思义,它告诉存储单元忘记以前的值。存储单元存储这些值,直到门(即“忘记门”)告诉它忘记它们。

  • 输入门- 顾名思义,它向单元添加新的东西。

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

在 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

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