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
...
# 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 sampleBasedRandomizationWindow nã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, false padrã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, false padrã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,
- https://github.com/Microsoft/CNTK/tree/release/latest/Tests/EndToEndTests/Speech/HTKDeserializers/LSTM/FullUtterance
- https://github.com/Microsoft/CNTK/tree/release/latest/Tests/EndToEndTests/Image/AlexNet
- https://github.com/Microsoft/CNTK/tree/release/latest/Tests/UnitTests/ReaderTests/Config/ImageAndTextReaderSimple_Config.cntk
- https://github.com/Microsoft/CNTK/tree/release/latest/Tests/UnitTests/ReaderTests/Config/CNTKTextFormatReader/dense.cntk