你当前正在访问 Microsoft Azure Global Edition 技术文档网站。 如果需要访问由世纪互联运营的 Microsoft Azure 中国技术文档网站,请访问 https://docs.azure.cn

BrainScript 和 Python:了解和扩展读者

从版本 1.5 开始,CNTK 正从整体读取器设计转向更可组合的模型,使你可以指定和撰写不同格式的输入数据。

以前,每个读取器都负责数据读取的不同方面,包括但不限于:

  • 将数据从外部存储反序列化到内存中表示形式
  • 整个料库的随机化
  • (输入序列/样本的不同转换,例如裁剪或缩放图像)
  • (帧、序列或截断的 BPTT) 为不同模式创建小型巴切,其布局可由 GPU 使用
  • 预提取小块和 IO 区块级别

在版本 1.5 中,上述功能的主要部分被分解,并移动到核心 CNTK,以便在不同的读取器之间共享。 此版本还引入了两种可以扩展的主要抽象,以支持新的数据格式:

  • 反序列化程序 - 负责将外部存储输入反序列化为内存中序列
  • 转换 - 它将输入序列转换为输出序列

在接下来的部分中,我们将更详细地讨论这些抽象。

在 Python 中配置读取器 (微型批处理源)

本部分提供了一些示例,介绍了如何在 Python 中配置复合读取器 (即 MinibatchSource) 。

下面的示例改编自 AlexNet_ImageNet_Distributed.py,它显示了 与 Transforms 部分中的 AlexNet 读取器等效的 Python。

import cntk.io

mean_file = ...
map_file = ...

# model dimensions
image_height = 227
image_width  = 227
num_channels = 3  # RGB
num_classes  = 1000

transforms = [
     ImageDeserializer.crop(crop_type='randomside', 
                            side_ratio=0.88671875, 
                            jitter_type='uniratio'),
     ImageDeserializer.scale(width=image_width, 
                            height=image_height, 
                            channels=num_channels, 
                            interpolations='linear'),
     ImageDeserializer.mean(mean_file)
]

reader = MinibatchSource(
    ImageDeserializer(map_file, StreamDefs(
        # first column in map file is referred to as 'image'
        features = StreamDef(field='image', transforms=transforms),
        # and second as 'label' 
        labels   = StreamDef(field='label', shape=num_classes)))) 

下面的示例 (改编自 A2_RunCntk_py3.py) 演示了如何将多个反序列化器组合在一起。

n_rois = 100
n_classes = 17
rois_dim = 4 * n_rois
label_dim = n_classes * n_rois

map_file = ...
roi_file = ...
label_file = ...

# read images
scale = ImageDeserializer.scale(width=1000, 
                                height=1000, 
                                channels=3,
                                scale_mode="pad", 
                                pad_value=114, 
                                interpolations='linear')
image_source = ImageDeserializer(map_file)
image_source.ignore_labels()
image_source.map_features('features', [scale])

# read rois and labels
roi_source = CTFDeserializer(roi_file)
roi_source.map_input('rois', dim=rois_dim, format="dense")
label_source = CTFDeserializer(label_file)
label_source.map_input('roiLabels', dim=label_dim, format="dense")

# define a composite reader
reader = MinibatchSource([image_source, roi_source, label_source])

...

# define mapping from reader streams to network inputs
input_map = {
    image_input: reader.streams.features,
    roi_input: reader.streams.rois,
    label_input: reader.streams.roiLabels
}

BrainScript

反序列化程序

让我们从端到端 LSTM/FullUtterance 测试中查看 HTKMLFReader 的以下配置片段, (此处的完整配置) :

...
# Old reader config. For illustration only.
reader = [
    readerType = "HTKMLFReader"
    readMethod = "blockRandomize"
    nbruttsineachrecurrentiter = 32
    randomize = "auto"
    verbosity = 0

    features = [
        dim = 363
        type = "real"
        scpFile = "$DataDir$/glob_0000.scp"
    ]

    labels = [
        mlfFile = "$DataDir$/glob_0000.mlf"
        labelMappingFile = "$DataDir$/state.list"

        labelDim = 132
        labelType = "category"
    ]
]

此配置片段声明一个读取器,该读取器生成两个具有名称和"features""labels"名称的数据流。 它采用输入两种类型的文件:

  • HTK 分析中已知的功能文件列表,该文件scp (“script”文件)
  • 称为 mlf 文件 (“主标签文件”的标签文件)

在上述配置片段中,没有定义反序列化方式 scpmlf 格式的显式实体。 所有内容都封装在 HTKMLFReader 配置中。 因此,如果需要同时公开其他不同数据格式 scp 的输入流, mlf则需要更改 HTKMLFReader 并添加支持。

为了提高可组合性和重用性,同一输入 的新 配置显式定义反序列化器和它们生成的输入流:

reader = [
    verbosity = 0
    randomize = true

    # A list of deserializers the reader uses.
    deserializers = (
        [
            # Type of deserializer, in this case the one that knows
            # how to deserialize HTK feature files.
            type = "HTKFeatureDeserializer"
            # Module (.dll or .so) where this deserializer is implemented
            module = "HTKDeserializers"

            # Description of input streams the deserializer provides,
            # can be one or many, depending on a particular
            # deserializer implementation
            # For HTKFeatureDeserializer, just one stream can be described.
            input = [
                # Description of input stream to feed the Input node named "features"
                features = [
                    dim = 363
                    scpFile = "$DataDir$/glob_0000.scp"
                ]
            ]
        ]:
        [
            # Type of deserializer, in this case the one
            # that knows how to deserialize mlf files.
            type = "HTKMLFDeserializer"
            module = "HTKDeserializers"
            # Description of input streams the deserializer provides,
            # For HTKMLFDeserializer, just one stream can be described.
            input = [
                # Description of input stream to feed the Input node named "labels"
                labels = [
                    dim = 132
                    mlfFile = "$DataDir$/glob_0000.mlf"
                    labelMappingFile = "$DataDir$/state.list"
                    # whether phone boundary information should be encoded
                    # set to true in CTC-type training
                    phoneBoundaries=false
                ]
            ]
        ]
    )
]

htk反序列化程序生成的mlf序列根据其逻辑键 (组合在一起,这是唯一标识语音话语的字符串,并且同时存在于scpmlf文件) 中。 如果需要另一个不同格式的流,只需将相应的反序列化程序添加到配置 (HTK 功能和 HTK MLF 反序列化程序就不可能立即公开每个) 的多个输入流。

注意

目前,支持旧读取器配置和新读取器配置。 当读取器配置中使用“反序列化程序”键时,读取器类型将隐式设置为“CompositeDataReader”。 若要确保可以在 Windows 上加载 CompositeDataReader 模块, Cntk.Composite.dll 应位于 CNTK 可执行文件所在的同一目录中。 Cntk.Composite.so Linux 上应位于lib与包含 CNTK 可执行文件的文件夹并排bin所在的文件夹中。

目前 CNTK 支持以下反序列化程序:

反序列化程序类型 模块 描述
HTKFeatureDeserializer HTKDeserializers HTK 功能文件的反序列化程序
HTKMLFDeserializer HTKDeserializers HTK MLF 文件的反序列化程序
ImageDeserializer ImageReader 编码为纯文件或 zip 存档中的图像的反序列化程序。
Base64ImageDeserializer ImageReader 映射文件中编码为 base64 字符串的图像的反序列化程序。
CNTKTextFormatDeserializer CNTKTextFormatReader CNTK 文本文件的反序列化程序
CNTKBinaryFormatDeserializer CNTKBinaryReader CNTK 二进制格式化文件的反序列化程序

有关配置参数的完整说明,请参阅 下表

转换

转换是一个简单的抽象,采用序列作为输入,在序列中执行样本的一些转换,并返回输出序列。 转换的典型示例是图像的不同转换,例如裁剪、缩放或转置。 可以根据输入配置转换。

让我们看看如何将转换应用到输入 (配置从 Tests/EndToEndTests/Image/AlexNet 测试) :

deserializers = ([
    type = "ImageDeserializer"
    module = "ImageReader"

    # Map file which maps images to labels
    file = "$ConfigDir$/train_map.txt"

    # Description of input streams
    input = [
            # Description of input stream to feed the Input node named "features"
            features = [
                transforms = (
                    [
                        type = "Crop"
                        # Possible values: Center, RandomSide, RandomArea, Multiview10. Default: Center
                        cropType = "RandomSide"
                        # Crop scale side ratio.
                        sideRatio = 0.875
                        # Crop scale ratio jitter type
                        jitterType = "UniRatio"
                    ]:[
                        type = "Scale"
                        width = 224
                        height = 224
                        channels = 3
                        # Interpolation to use when scaling image to width x height size.
                        interpolations = "linear"
                    ]:[
                        type = "Mean"
                        # Stores mean values for each pixel in OpenCV matrix XML format.
                        meanFile = "$ConfigDir$/ImageNet1K_mean.xml"
                    ]:[
                        # Changes the image layout from HWC to CHW
                        type = "Transpose"
                    ]
                )
            ]
            # Description of input stream to feed the Input node named "labels"
            labels = [
                labelDim = 1000
            ]
        ]
    ]
])

在此配置中,四个转换应用于输入流 features。 最初,图像数据反序列化程序生成由 HWC 表示形式的单个图像组成的序列。 之后,将按顺序排列的转换列表应用于图像:首先是 裁剪 转换,后跟 ScaleMean。 最后一个转换是转 ,用于将图像布局从 HWC 更改为 CHW。

目前已实现以下转换。 有关其详细说明,请参阅 ImageReader

转换类型 模块
Crop ImageReader
缩放 ImageReader
Color ImageReader
平均值 ImageReader
转置 ImageReader

新的读取者配置格式说明

用于编写多个数据反序列化器的读取器配置部分如下所示:

reader = [
    randomize = true|false
    verbosity = 0|1|2
    ...

    deserializers = (
        [<deserializerConfiguration1>]:
        [<deserializerConfiguration2>]:
        ...
        [<deserializerConfigurationN>]
    )
]

每个反序列化程序配置都指定为:

[
    module = "<readerModuleName>"   # Name of the external module (.dll or .so) where this particular deserializer is implemented
    type = "<deserializerType>"     # The type of the deserializer

    # There could be more deserializer-specific options in this section

    # Date deserializer input - describes a set of streams this deserializer produces.
    # It can be one (as in HTK) or many (as in CNTKTextFormat)
    input = [
        # Replace 'InputNameN' by the name of the corresponding input node in the network.
        InputName1 = [<inputConfiguration>]
        InputName2 = [<inputConfiguration>]
        ...
    ]
]

输入配置包含输入特定的选项,以及(可选)应用于输入的转换有序列表:

[
    # Per-input data deserializer-specific options

    # Optionally a pipeline of transformations, to be implemented by data deserializer's reader module:
    transforms = (
       [<transformationConfiguration1>]:
       [<transformationConfiguration2>]:
       ...
       [<transformationConfigurationN>]
    )
]

转换配置标识转换类型和任何特定于转换的选项:

[
    type = "<transformName>"
    # Transform-specific options
]

配置选项

常规读取器配置

参数 说明
verbosity 详细级别 (012) 控制不同组件的诊断输出, (Randomizer、Deserializer、Bundler 等) 可选,默认为 0 a0/>。
randomize 指定是否应随机化输入 ( truefalse) 。 随机化方法与 HTKMLFReader 的 blockRandomize 相同。 可选,默认值为 true.
randomizationSeed 当输入数据重新随机化) 时,初始随机化种子值 (递增。 可选,默认值为 0.
randomizationWindow 指定随机化窗口的大小 (正整数) (,即随机化范围) 。 此参数会影响数据集一次驻留在内存中的多少。 (可选)默认为样本或区块中 整个数据集 的大小 (,具体取决于 sampleBasedRandomizationWindow 值) 。 但是,如果其中一个反序列化器未CNTKTextFormatDeserializersampleBasedRandomizationWindow显式设置为truerandomizationWindow则默认为128 (,这是大约 4GB 磁盘空间的区块) 。 如果 randomizefalse,则忽略此参数。
sampleBasedRandomizationWindow 如果 true,随机化窗口的大小将解释为一定数量的样本,否则解释为多个区块。 可选,默认为trueCNTKTextFormatDeserializer在反序列化程序列表中不存在,否则为false不存在。 与randomizationWindow此参数类似,当为 false a0/> 时randomize,将忽略此参数。
truncationLength 指定 BPTT (正整数) 样本中的截断长度。 仅当为 truetruncated,才必需,否则将被忽略。
multiThreadedDeserialization 指定在从反序列化器 (收集小包序列时,是否应使用多个线程,truefalse) 。 可选。
frameMode 指定是否应在帧或序列级别随机化并返回数据。 当 true输入序列拆分为帧时。 可选。 这两者frameMode都不能同时设置为truetruncated
truncated 启用 true通过时间截断的反向传播 (BPTT) 。 可选。 这两者frameMode都不能同时设置为truetruncated
useNumericSequenceKeys 序列键用于关联不同反序列化程序之间的序列。 对于某些反序列化程序, (例如 HTK 和 MLF) 序列键是任意字符串。 存储它们需要在大型库上占用大量内存。 如果确定序列键是数值,请将此参数设置为 true,在这种情况下,所有字符串键将转换为减少内存压力的整数。 可选,默认值 false
hashSequenceKeys 出于上述内存原因,还可以通过将此参数设置为 true 来对字符串键进行哈希处理。 请仅将其用于支持字符串序列键 (HTK、MLF) 的反序列化程序。 可选,默认值 false
cacheIndex 指定在预处理阶段生成的元数据是否应写入磁盘,并在可用 (true时从磁盘加载, false) 。 可选,默认为 false. 有关详细信息,请参阅下面的部分。 CNTK 版本 2.1 中的新增功能。
索引缓存

注意

CNTK 版本 2.1 中的新增功能。

索引缓存允许大幅 (2-3 倍,) 减少启动时间,尤其是在处理大型输入文件时。 将 cacheIndex 标志 true 设置为指示读取器将索引元数据写入磁盘 (与输入文件相同的目录,) 缓存文件不可用,或者缓存文件 (早于输入文件) 。 写入是尽最大努力的,在单独的线程上执行,以便不影响读取器性能。 如果缓存文件存在并且是最新的,则读取器将不再浏览输入文件以生成索引,而是从缓存文件加载索引。 请注意,某些读取器配置参数对索引 (有直接影响,不同的值 frameMode 可能会导致索引具有不同序列数) 。 因此,读取器可以忽略缓存文件,其配置不同于生成缓存的配置。 若要查看缓存的完整优势,不应在后续重新运行时修改配置。

cacheIndex 对 ImageDeserializer 和 CNTKBinaryFormatDeserializer 没有影响,因为前者不为输入数据编制索引,而后来的索引信息嵌入格式本身。

常规反序列化程序配置

参数 说明
module 指定实现数据反序列化器的读取器模块名称。 “必需”。
type 指定给定读取器模块公开的数据反序列化程序名称。 “必需”。

常规转换配置

参数 说明
type 指定由实现数据反序列化器的读取器模块公开的转换名称。 “必需”。

HTKFeatureDeserializer 选项

参数 说明
scpFile 要处理的 SCP 文件的路径列表。 这些文件应为 HTK 兼容文件,并且必须以“存档”格式指定。 使用存档的详细信息在 HTKMLF 读取器中介绍。 “必需”。
dim 一个整数,指定具有所需上下文窗口的完整特征向量维度。1必需
contextWindow 可以指定为正整数对或单个正整数 (在这种情况下,它将同一个数字解释为重复两次) 的对。 指定示例中上下文窗口) 对 (第一个和第二个整数的左右大小。 可选,默认为 1.
prefixPathInSCP 要应用于 SCP 文件中指定的路径的前缀字符串。 可选。

例如 ,如果具有 72 维特征 (24 维筛选器库特征加上增量和增量系数) 并且网络旨在处理 11 帧的上下文窗口,则指定的维度应为 792。

HTKMLFDeserializer 选项

参数 说明
mlfFile HTK 样式 mlf 文件的路径,其中包含文件 () 中指定的 scp 所有言语的标签。 如果未mlfFileList指定,则为必需
mlfFileList HTK 样式 mlf 文件 () 的路径数组,其中包含文件 (s) 中指定的 scp 所有话语的标签。 如果未mlfFile指定,则为必需
dim 标签集的总基数 (正整数) 。 “必需”。
labelMappingFile 文件的路径,其中列出了文件中显示的 mlf 所有标签,每行一行。 “必需”。

labelDim 可用作 dim 的同义词。

CNTKTextFormatDeserializer 选项

可用于 CNTKTextFormatReader 的相同选项

ImageDeserializer 选项

  • file:一个简单的文本文件,其中每行都包含逻辑序列键、图像文件 ((例如 JPEG、PNG ) 等)和基于 0 的标签之间的制表符分隔映射。

有关详细信息,请参阅 ImageReader

Base64ImageDeserializer 选项

此反序列化程序支持可用于 ImageDeserializer 的相同选项。 唯一的区别在于映射文件的格式:

  • file:一个简单的文本文件,其中每行包含逻辑序列键之间的制表符分隔映射 (可选,可以省略) 、基于 0 的类别标签和 base 64 编码的图像文件 (,例如 JPEG、PNG 等) 。

配置和测试示例

你将在 CNTK 存储库中找到完整的网络定义和相应的数据集示例。 你还将在此处找到使用反序列化器的单元和端到端测试,即