Partilhar via


BrainScript e Python: Entendendo e estendendo leitores

A partir da versão 1.5, a CNTK está se afastando do design monolítico do leitor para um modelo mais compostável que permite especificar e compor dados de entrada de diferentes formatos.

Antes, cada leitor era responsável por diferentes aspetos da leitura de dados, incluindo, mas não limitado a:

  • Desserialização dos dados do armazenamento externo para representação na memória
  • Aleatorização de todo o corpus
  • Diferentes transformações de sequências/amostras de entrada (ou seja, corte ou dimensionamento de imagens)
  • Criação de minilotes para diferentes modos (ou seja, quadro, sequência ou BPTT truncado) com um layout que pode ser consumido pela GPU
  • Pré-busca no nível de minilotes e blocos de E/S

Na versão 1.5, as principais partes da funcionalidade acima foram fatoradas e movidas para o núcleo CNTK para serem compartilhadas entre diferentes leitores. Esta versão também introduz duas abstrações principais que podem ser estendidas para suportar novos formatos de dados:

  • desserializador - é responsável pela desserialização da entrada do armazenamento externo em sequências na memória
  • transformar - transforma uma sequência de entrada em uma sequência de saída

Nas próximas seções, discutiremos essas abstrações com mais detalhes.

Configurando um leitor (fonte minibatch) em Python

Esta seção fornece vários exemplos sobre como um leitor composto (também conhecido como MinibatchSource ) pode ser configurado em Python.

O exemplo a seguir foi adaptado de AlexNet_ImageNet_Distributed.py, ele mostra o equivalente Python do leitor AlexNet de seção Transforms.

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)))) 

O exemplo a seguir (adaptado de A2_RunCntk_py3.py) mostra como vários desserializadores podem ser combinados juntos.

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

Desserializadores

Vamos dar uma olhada no seguinte fragmento de configuração para o HTKMLFReader do teste de LSTM/FullUtterance de ponta a ponta (configuração completa aqui):

...
# 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"
    ]
]

Este fragmento de configuração declara um leitor que produz dois fluxos de dados com nomes "features" e "labels". Ele toma como entrada dois tipos de arquivos:

  • uma lista de arquivos de recursos conhecidos no jargão HTK como um arquivo de (arquivo "script")
  • um arquivo de rótulo conhecido como arquivo mlf ("arquivo de rótulo mestre")

No fragmento de configuração acima, não há entidades explícitas que definiriam como os formatos scp ou mlf são desserializados. Tudo é encapsulado na configuração HTKMLFReader. Então, se você precisar expor mais um fluxo de entrada de formato de dados diferente junto com scp e mlf, você precisaria alterar HTKMLFReader e adicionar suporte lá.

Para aumentar a capacidade de composição e reutilização, o nova configuração de para a mesma entrada define explicitamente os desserializadores e os fluxos de entrada que eles produzem:

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
                ]
            ]
        ]
    )
]

As sequências produzidas pelos desserializadores mlf e htk são combinadas com base em sua chave lógica (que é uma cadeia de caracteres que identifica exclusivamente um enunciado de fala e está presente em arquivos scp e mlf). Quando você precisar de outro fluxo de formato diferente, você pode simplesmente adicionar o desserializador correspondente à configuração (não é possível com o recurso HTK e os desserializadores MLF HTK agora expor mais de um fluxo de entrada cada).

Observação

Atualmente, as configurações de leitor antigo e novo são suportadas. Quando a chave "deserializers" é usada na configuração do leitor, o tipo de leitor é implicitamente definido como "CompositeDataReader". Para garantir que o módulo CompositeDataReader possa ser carregado no Windows, o Cntk.Composite.dll deve estar localizado no mesmo diretório que o executável CNTK. No Linux Cntk.Composite.so deve estar na pasta lib que fica lado a lado com a pasta bin que contém o executável CNTK.

Atualmente, o CNTK suporta os desserializadores abaixo:

Tipo de desserializador Módulo Descrição
HTKFeatureDeserializer HTKDeserializers Desserializador para arquivos de recursos HTK
HTKMLFDeserializer HTKDeserializers Desserializador para arquivos MLF HTK
ImageDeserializer Leitor de imagens Desserializador para imagens codificadas como arquivos simples ou em arquivo zip.
Base64ImageDeserializer Leitor de imagens Desserializador para imagens codificadas como cadeias de caracteres base64 no arquivo de mapeamento.
CNTKTextFormatDeserializer CNTKTextFormatReader Desserializador para arquivos de formato de texto CNTK
CNTKBinaryFormatDeserializer CNTKBinaryReader Desserializador para arquivos de formato binário CNTK

Consulte as tabelas abaixo para obter a descrição completa dos parâmetros de configuração.

Transforma

Uma transformação é uma abstração simples que toma uma sequência como entrada, executa alguma transformação de amostras na sequência e retorna a sequência de saída. Exemplos típicos de transformações são diferentes transformações de imagens, como cortar, dimensionar ou transpor. As transformações podem ser configuradas por entrada.

Vamos dar uma olhada em como as transformações podem ser aplicadas à entrada (a configuração é retirada do teste 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
            ]
        ]
    ]
])

Nessa configuração, quatro transformações são aplicadas ao fluxo de entrada features. Inicialmente, o desserializador de dados de imagem produz sequências que consistem em uma única imagem na representação HWC. Depois disso, a lista ordenada de transformações é aplicada à imagem: em primeiro lugar, a Crop transform, seguida por Scale e Mean. A última transformação é Transpose que altera o layout da imagem de HWC para CHW.

Atualmente, as seguintes transformações são implementadas. Para obter uma descrição detalhada, consulte ImageReader.

Tipo de transformação Módulo
Colheita Leitor de imagens
Escala Leitor de imagens
Cor Leitor de imagens
Média Leitor de imagens
Transpor Leitor de imagens

Descrição do novo formato de configuração do leitor

Uma seção de configuração do leitor para compor vários desserializadores de dados tem a seguinte aparência:

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

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

Cada configuração do desserializador é especificada como:

[
    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>]
        ...
    ]
]

Uma configuração de entrada contém opções específicas de entrada e, opcionalmente, uma lista ordenada de transformações que devem ser aplicadas à entrada:

[
    # Per-input data deserializer-specific options

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

A configuração de transformação identifica o tipo de transformação e quaisquer opções específicas de transformação:

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

Opções de configuração

Configuração geral do leitor

Parâmetro Descrição
verbosity Nível de verbosidade (0, 1, 2), controla a saída de diagnóstico de diferentes componentes (Randomizer, Deserializer, Bundler, etc.) Optional, o padrão é 0.
randomize Especifica se a entrada deve ser aleatória ( true, false). O método de randomização é idêntico ao blockRandomize do HTKMLFReader. Opcional, o padrão é true.
randomizationSeed Valor inicial da semente de aleatorização (incrementado a cada varredura quando os dados de entrada são re-randomizados). Opcional, o padrão é 0.
randomizationWindow Especifica o tamanho (inteiro positivo) da janela de aleatorização (ou seja, intervalo de aleatorização). Esse parâmetro afeta quanto do conjunto de dados precisa residir na memória de uma só vez. Opcional, o padrão é o tamanho do conjunto de dados inteiro (em amostras ou em partes, dependendo do valor sampleBasedRandomizationWindow). No entanto, se um dos desserializadores estiver CNTKTextFormatDeserializer e sampleBasedRandomizationWindownão foi explicitamente definido como true, randomizationWindow o padrão será 128 (que é aproximadamente 4 GB de espaço em disco em blocos). Este parâmetro é ignorado quando randomize é false.
sampleBasedRandomizationWindow Se true, o tamanho da janela de aleatorização é interpretado como um certo número de amostras, e como um número de pedaços de outra forma. Opcional, o padrão é true se CNTKTextFormatDeserializer não estiver presente na lista de desserializadores e false de outra forma. Da mesma forma que randomizationWindow, esse parâmetro é ignorado, quando randomize é false.
truncationLength Especifica o comprimento do truncamento em amostras para BPTT (inteiro positivo). Obrigatório somente se truncated for true, ignorado de outra forma.
multiThreadedDeserialization Especifica se vários threads devem ser usados ao coletar sequências para um minilote dos desserializadores (true, false). Opcional.
frameMode Especifica se os dados devem ser randomizados e retornados no nível de quadro ou sequência. Quando true, a sequência de entrada é dividida em quadros. Opcional. Tanto frameMode quanto truncated não podem ser definidas para true ao mesmo tempo.
truncated Quando true, permite a retropropagação truncada ao longo do tempo (BPTT). Opcional. Tanto frameMode quanto truncated não podem ser definidas para true ao mesmo tempo.
useNumericSequenceKeys As chaves de sequência são usadas para correlacionar sequências entre diferentes desserializadores. Para alguns desserializadores (ou seja, HTK e MLF), as chaves de sequência são cadeias de caracteres arbitrárias. Armazená-los requer muita memória em grandes corpus. Se você tiver certeza de que suas chaves de sequência são numéricas, defina este parâmetro como true, nesse caso, todas as teclas de string serão convertidas em inteiros diminuindo a pressão da memória. Opcional, falsepadrão .
hashSequenceKeys Pelos motivos de memória descritos acima, as chaves de cadeia de caracteres também podem ser colocadas em hash definindo esse parâmetro como true. Use-o apenas para desserializadores que suportam chaves de sequência de cadeia de caracteres (HTK, MLF). Opcional, falsepadrão .
cacheIndex Especifica se os metadados criados durante o estágio de pré-processamento devem ser gravados em disco e carregados do disco, se disponíveis (true, false). Opcional, o padrão é false. Para obter mais detalhes, consulte a seção abaixo. Novo na CNTK versão 2.1.
Cache de índice

Observação

Novo na CNTK versão 2.1.

O cache de índice permite reduzir significativamente (por um fator de 2-3x) os tempos de inicialização, especialmente ao trabalhar com grandes arquivos de entrada. Definir o sinalizador cacheIndex como true sinalizará ao leitor para gravar os metadados de indexação no disco (mesmo diretório do arquivo de entrada) se o arquivo de cache não estiver disponível ou se estiver obsoleto (mais antigo que o arquivo de entrada). A escrita é o melhor esforço e é realizada em um fio separado, a fim de não afetar o desempenho do leitor. Se o arquivo de cache estiver presente e estiver up-to-date, o leitor não ignorará mais o arquivo de entrada para criar o índice, em vez disso, carregará o índice do arquivo de cache. Observe que certos parâmetros de configuração do leitor têm um impacto direto na indexação (por exemplo, valores diferentes de frameMode podem resultar em índices com diferentes números de sequências). Por esse motivo, um arquivo de cache pode ser ignorado por um leitor com uma configuração diferente daquela que produziu o cache. Para ver todos os benefícios do cache, a configuração não deve ser modificada em reexecuções subsequentes.

cacheIndex não tem efeito sobre ImageDeserializer e CNTKBinaryFormatDeserializer, pois o primeiro não indexa os dados de entrada e o segundo tem as informações de índice incorporadas no próprio formato.

Configuração geral do desserializador

Parâmetro Descrição
module Especifica o nome do módulo leitor que implementa o desserializador de dados. Obrigatório.
type Especifica um nome de desserializador de dados exposto pelo módulo de leitura fornecido. Obrigatório.

Configuração geral de transformação

Parâmetro Descrição
type Especifica um nome de transformação exposto pelo módulo leitor que implementa o desserializador de dados. Obrigatório.

Opções HTKFeatureDeserializer

Parâmetro Descrição
scpFile Uma lista de caminhos para arquivos SCP a serem processados. Os arquivos devem ser compatíveis com HTK e devem ser especificados no formato "arquivo". Os detalhes do uso de um arquivo são descritos em HTKMLF Reader. Obrigatório.
dim Um inteiro que especifica a dimensão vetorial completa do recurso com a janela de contexto desejada.1 Obrigatório
contextWindow Pode ser especificado como um par de inteiros positivos ou como um único inteiro positivo (caso em que é interpretado como um par com o mesmo número repetido duas vezes). Especifica o tamanho esquerdo e direito (primeiro e segundo inteiros do par) da janela de contexto em exemplos. Opcional, o padrão é 1.
prefixPathInSCP Uma cadeia de caracteres de prefixo a ser aplicada aos caminhos especificados nos arquivos SCP. Opcional.

1 Por exemplo, se você tinha recursos de 72 dimensões (recursos de banco de filtros de 24 dimensões mais coeficientes delta e delta-delta) e a rede foi projetada para processar uma janela de contexto de 11 quadros, a dimensão especificada deve ser 792.

Opções HTKMLFDeserializer

Parâmetro Descrição
mlfFile Caminho para um arquivo mlf estilo HTK que contém os rótulos para todos os enunciados especificados no(s) arquivo(s) scp. Obrigatório se mlfFileList não for especificado.
mlfFileList Matriz de caminhos para o(s) arquivo(s) de mlf no estilo HTK que contém os rótulos para todos os enunciados especificados no(s) arquivo(s) scp. Obrigatório se mlfFile não for especificado.
dim Cardinalidade total do conjunto de rótulos (inteiro positivo). Obrigatório.
labelMappingFile Caminho para um arquivo que lista todos os rótulos vistos no arquivo mlf, um por linha. Obrigatório.

labelDim pode ser usado como sinônimo de dim.

Opções CNTKTextFormatDeserializer

As mesmas opções que podem ser usadas com CNTKTextFormatReader

Opções de ImageDeserializer

  • file: um arquivo de texto simples onde cada linha contém um mapeamento separado por tabulações entre chave de sequência lógica, arquivo de imagem (e.g. JPEG, PNG, etc.) e rótulo baseado em 0.

Para obter mais informações, consulte ImageReader.

Opções de Base64ImageDeserializer

Este desserializador suporta as mesmas opções que podem ser usadas com ImageDeserializer. A única diferença está no formato do arquivo de mapeamento:

  • file: um arquivo de texto simples onde cada linha contém um mapeamento separado por tabulações entre a chave de sequência lógica (opcional, pode ser omitida), rótulo de categoria baseado em 0 e arquivo de imagem codificado base 64 (e.g. JPEG, PNG, etc.).

Exemplos de configurações e testes

Você encontrará definições de rede completas e os exemplos de conjuntos de dados correspondentes no Repositório CNTK. Lá você também encontrará testes de unidade e de ponta a ponta que usam desserializadores, ou seja,