CNTK - 序列分类



本章将详细讲解 CNTK 中的序列及其分类。

张量

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

  • 秩为 0 的张量是标量。

  • 秩为 1 的张量是向量。

  • 秩为 2 的张量是矩阵。

这里,这些不同的维度被称为

静态轴和动态轴

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

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

示例

为了更清楚地说明,让我们看看如何在 CNTK 中表示短视频剪辑的小批量。假设视频剪辑的分辨率都是 640 * 480。而且,剪辑也是彩色的,通常用三个通道编码。这意味着我们的迷你批次具有以下内容:

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

  • 两个动态轴;视频的长度和迷你批次轴。

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

在 CNTK 中使用序列

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

长短期记忆网络 (LSTM)

Long-Short Term Memory Network

长短期记忆 (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

上述程序的详细解释将在接下来的章节中介绍,尤其是在构建循环神经网络时。

广告