Compartilhar via


BrainScript e Python: compreendendo e estendendo leitores

A partir da versão 1.5, o CNTK está se afastando do design do leitor monolítico em direção a um modelo mais redigido que permite especificar e redigir dados de entrada de diferentes formatos.

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

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

Na versão 1.5, as principais partes da funcionalidade acima foram fatoradas e movidas para o CNTK principal para serem compartilhadas entre diferentes leitores. Essa versão também apresenta duas abstrações principais que podem ser estendidas para dar suporte a 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 mais detalhadamente.

Configurando um leitor (fonte de minibatch) no Python

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

O exemplo a seguir foi adaptado de AlexNet_ImageNet_Distributed.py, mostrando o equivalente python do leitor AlexNet da seção Transformações .

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.

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 examinar o seguinte fragmento de configuração para o HTKMLFReader do teste 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"
    ]
]

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

  • uma lista de arquivos de recurso conhecidos na linguagem HTK como um scp arquivo (arquivo "script")
  • um arquivo de rótulo conhecido como mlf arquivo ("arquivo de rótulo mestre")

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

Para aumentar a capacidade de redimensionamento e reutilização, a nova configuração para a mesma entrada define explicitamente 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 pelo e htk desserializadores mlf 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 ambos e scpmlf arquivos). Quando você precisa de outro fluxo de formato diferente, basta adicionar o desserializador correspondente à configuração (não é possível com o recurso HTK e desserializadores HTK MLF agora para expor mais de um fluxo de entrada cada).

Observação

Atualmente, há suporte para configurações de leitor antigos e novos. Quando a chave "desserializadores" é 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, ele Cntk.Composite.dll deve estar localizado no mesmo diretório que o executável CNTK. No Linux Cntk.Composite.so , deve estar na lib pasta que fica lado a lado na bin pasta que contém o executável CNTK.

Atualmente, o CNTK dá suporte aos desserializadores abaixo:

Tipo de desserializador Módulo Descrição
HTKFeatureDeserializer HTKDeserializers Desserializador para arquivos de recurso HTK
HTKMLFDeserializer HTKDeserializers Desserializador para arquivos MLF do HTK
ImageDeserializer ImageReader Desserializador para imagens codificadas como arquivos sem formatação ou em arquivo zip.
Base64ImageDeserializer ImageReader 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ções

Uma transformação é uma abstração simples que usa uma sequência como entrada, executa algumas transformações de exemplos 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 corte, escala ou transposição. As transformações podem ser configuradas por entrada.

Vamos ver como as transformações podem ser aplicadas à entrada (a configuração é obtida 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
            ]
        ]
    ]
])

Nesta configuração, quatro transformações são aplicadas ao fluxo featuresde entrada. 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: primeiro, a transformação Cortar , seguida por Escala e Média. A última transformação é Transpose que altera o layout da imagem de HWC para CHW.

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

Tipo de transformação Módulo
Crop ImageReader
Escala ImageReader
Color ImageReader
Média ImageReader
Transpor ImageReader

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

Uma seção de configuração de 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 de 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 todas as 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 O nível de verbosidade (0, 1, 2), controla a saída de diagnóstico de diferentes componentes (Randomizer, Desserializador, Bundler etc.) Opcional, 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 de semente de randomização inicial (incrementado a cada varredura quando os dados de entrada são realeamensionados). Opcional, o padrão é 0.
randomizationWindow Especifica o tamanho (inteiro positivo) da janela de randomização (ou seja, intervalo de randomização). Esse parâmetro afeta quanto do conjunto de dados precisa residir na memória ao mesmo tempo. Opcional, o padrão é o tamanho de todo o conjunto de dados (em amostras ou em partes, dependendo do sampleBasedRandomizationWindow valor). No entanto, se um dos desserializadores for CNTKTextFormatDeserializer e sampleBasedRandomizationWindownão estiver explicitamente definido como true, randomizationWindow o padrão 128 será (que é aproximadamente 4 GB de espaço em disco em partes). Esse parâmetro é ignorado quando randomize é false.
sampleBasedRandomizationWindow Se true, o tamanho da janela de randomização é interpretado como um determinado número de amostras e como um número de partes de outra forma. Opcional, o padrão é se trueCNTKTextFormatDeserializer não estiver presente na lista de desserializadores e para false o contrário. Da mesma forma que randomizationWindow, esse parâmetro é ignorado, quando randomize é false.
truncationLength Especifica o comprimento do truncamento em exemplos de 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 uma minibatch 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. Ambos frameMode e truncated não podem ser definidos ao true mesmo tempo.
truncated Quando true, habilita a propagação de back-propagação truncada por meio do tempo (BPTT). Opcional. Ambos frameMode e truncated não podem ser definidos ao true 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 um grande corpus. Se você tiver certeza de que suas chaves de sequência são numéricas, defina esse parâmetro como true, nesse caso, todas as chaves de cadeia de caracteres serão convertidas em inteiros diminuindo a pressão de memória. Opcional, padrão false.
hashSequenceKeys Pelos motivos de memória descritos acima, as chaves de cadeia de caracteres também podem ser hashed definindo esse parâmetro como true. Use-o apenas para desserializadores que dão suporte a chaves de sequência de cadeia de caracteres (HTK, MLF). Opcional, padrão false.
cacheIndex Especifica se os metadados criados durante o estágio de pré-processamento devem ser gravados no disco e carregados no disco, se disponíveis (true, false). Opcional, o padrão é false. Para obter mais detalhes, consulte a seção abaixo. Novidades no CNTK versão 2.1.
Cache de índice

Observação

Novidades no CNTK versão 2.1.

O cache de índice permite reduzir significativamente (por um fator de 2 a 3x) os tempos de inicialização, especialmente ao trabalhar com arquivos de entrada grandes. Definir o cacheIndex sinalizador para true sinalizará o leitor para gravar os metadados de indexação no disco (mesmo diretório que o arquivo de entrada) se o arquivo de cache não estiver disponível ou se ele estiver obsoleto (mais antigo que o arquivo de entrada). A gravação é o melhor esforço e é realizada em um thread separado para não afetar o desempenho do leitor. Se o arquivo de cache estiver presente e estiver atualizado, o leitor não deslizará mais o arquivo de entrada para compilar o índice, em vez disso, ele carregará o índice do arquivo de cache. Observe que determinados parâmetros de configuração de leitor têm um impacto direto na indexação (por exemplo, valores frameMode diferentes podem potencialmente resultar em índices que têm um número diferente 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 o benefício total do cache, a configuração não deve ser modificada em execuções subsequentes.

cacheIndex não tem efeito sobre ImageDeserializer e CNTKBinaryFormatDeserializer, pois o primeiro não indexa os dados de entrada e o posterior tem as informações de índice inseridas 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 leitor fornecido. Obrigatório.

Configuração geral da 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 de HTKFeatureDeserializer

Parâmetro Descrição
scpFile Uma lista de caminhos para arquivos SCP a serem processados. Os arquivos devem ser arquivos compatíveis com HTK e devem ser especificados no formato "archive". Os detalhes do uso de um arquivo morto são descritos no Leitor HTKMLF. Obrigatório.
dim Um inteiro que especifica a dimensão completa do vetor do recurso com a janela de contexto desejada. 1Obrigatório
contextWindow Pode ser especificado como um par de inteiros positivos ou como um único inteiro positivo (nesse caso, ele é interpretado como um par com o mesmo número repetido duas vezes). Especifica o tamanho esquerdo e direito (primeiro e segundo inteiro 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ê tiver recursos de 72 dimensões (recursos do banco de filtros 24 dimensões mais coeficientes delta e delta) e a rede for projetada para processar uma janela de contexto de 11 quadros, a dimensão especificada deverá ser 792.

Opções de HTKMLFDeserializer

Parâmetro Descrição
mlfFile Caminho para um arquivo no estilo mlf HTK que contém os rótulos de todos os enunciados especificados nos scp arquivos. Obrigatório se mlfFileList não for especificado.
mlfFileList Matriz de caminhos para arquivos no estilo mlf HTK que contém os rótulos de todos os enunciados especificados nos scp arquivos. 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 mlf arquivo, um por linha. Obrigatório.

LabelDim pode ser usado como sinônimo de dim.

Opções do CNTKTextFormatDeserializer

Mesmas opções que podem ser usadas com CNTKTextFormatReader

Opções do ImageDeserializer

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

Para obter mais informações, consulte ImageReader.

Opções base64ImageDeserializer

Esse desserializador dá suporte às 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 em que cada linha contém um mapeamento separado por tabulação entre chave de sequência lógica (opcional, pode ser omitido), rótulo de categoria baseado em 0 e arquivo de imagem codificado em base 64 (por exemplo, JPEG, PNG etc.).

Exemplos de configurações e testes

Você encontrará definições de rede completas e os exemplos de conjunto 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,