Udostępnij za pośrednictwem


BrainScript i Python: interpretacja i rozszerzanie czytelników

Począwszy od wersji 1.5, CNTK odchodzi od projektu czytnika monolitycznego w kierunku bardziej złożonego modelu, który umożliwia określanie i redagowanie danych wejściowych różnych formatów.

Wcześniej każdy czytelnik był odpowiedzialny za różne aspekty odczytywania danych, w tym między innymi:

  • Deserializacja danych z magazynu zewnętrznego do reprezentacji w pamięci
  • Randomizacja całego korpusu
  • Różne przekształcenia sekwencji wejściowych/przykładów (np. przycinanie lub skalowanie obrazów)
  • Tworzenie minibatches dla różnych trybów (tj. ramka, sekwencja lub obcięta BPTT) z układem, który może być używany przez procesor GPU
  • Pobieranie wstępne na poziomie minibatches i fragmentów we/wy

W wersji 1.5 główne elementy powyższej funkcjonalności zostały wyliczone i przeniesione do podstawowego zestawu CNTK, aby były udostępniane między różnymi czytnikami. W tej wersji wprowadzono również dwie główne abstrakcje, które można rozszerzyć w celu obsługi nowych formatów danych:

  • deserializacji — jest odpowiedzialny za deserializacji danych wejściowych z magazynu zewnętrznego do sekwencji w pamięci
  • przekształcenia — przekształca sekwencję wejściową w sekwencję wyjściową

W następnych sekcjach bardziej szczegółowo omówiono te abstrakcji.

Konfigurowanie czytnika (minibatch source) w języku Python

Ta sekcja zawiera kilka przykładów konfigurowania czytnika złożonego (np. MinibatchSource) w języku Python.

Poniższy przykład został dostosowany z AlexNet_ImageNet_Distributed.py, przedstawia odpowiednik języka Python czytnika AlexNet z sekcji 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)))) 

W poniższym przykładzie (dostosowanym z A2_RunCntk_py3.py) pokazano, jak można połączyć ze sobą kilka deserializacji.

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

Deserializatory

Przyjrzyjmy się następującemu fragmentowi konfiguracji HTKMLFReader z kompleksowego LSTM/FullUtterance (pełna konfiguracja tutaj):

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

Ten fragment konfiguracji deklaruje czytelnika, który tworzy dwa strumienie danych o nazwach "features" i "labels". Przyjmuje on jako dane wejściowe dwa typy plików:

  • lista plików funkcji znanych w parlance jako plik scp ("script" file)
  • plik etykiety znany jako plik mlf ("plik etykiety głównej")

W powyższym fragmentze konfiguracji nie ma jawnych jednostek, które definiują sposób deserializacji formatów scp lub mlf. Wszystko jest hermetyzowane w konfiguracji HTKMLFReader. Jeśli więc musisz uwidocznić kolejny strumień wejściowy innego formatu danych wraz z scp i mlf, musisz zmienić HTKMLFReader i dodać tam obsługę.

Aby zwiększyć możliwość komponowania i ponownego użycia, nową konfigurację dla tych samych danych wejściowych jawnie definiuje deserializatory i strumienie wejściowe, które tworzą:

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

Sekwencje tworzone przez mlf i deserializatory htk są łączone na podstawie ich klucza logicznego (czyli ciągu, który jednoznacznie identyfikuje wypowiedź mowy i jest obecny zarówno w plikach scp, jak i mlf). Jeśli potrzebujesz innego strumienia innego formatu, możesz po prostu dodać odpowiedni deserializator do konfiguracji (nie jest to możliwe w przypadku funkcji HTK i deserializacji HTK MLF w tej chwili, aby uwidocznić więcej niż jeden strumień wejściowy każdy).

Nuta

Obecnie obsługiwane są zarówno stare, jak i nowe konfiguracje czytników. Gdy klucz "deserializatory" jest używany w konfiguracji czytnika, typ czytnika jest niejawnie ustawiony na wartość "CompositeDataReader". Aby upewnić się, że moduł CompositeDataReader można załadować w systemie Windows, Cntk.Composite.dll powinien znajdować się w tym samym katalogu co plik wykonywalny CNTK. W systemie Linux Cntk.Composite.so powinien znajdować się w folderze lib, który znajduje się obok folderu bin zawierającego plik wykonywalny CNTK.

Obecnie cnTK obsługuje poniższe deserializatory:

Typ deserializacji Moduł Opis
HTKFeatureDeserializer HTKDeserializers Deserializator dla plików funkcji HTK
HTKMLFDeserializer HTKDeserializers Deserializator dla plików MLF HTK
ImageDeserializer ImageReader Deserializator obrazów zakodowanych jako zwykłe pliki lub w archiwum zip.
Base64ImageDeserializer ImageReader Deserializator obrazów zakodowanych jako ciągi base64 w pliku mapowania.
CNTKTextFormatDeserializer CNTKTextFormatReader Deserializator dla plików formatu tekstowego CNTK
CNTKBinaryFormatDeserializer CNTKBinaryReader Deserializator dla plików formatu binarnego CNTK

Aby uzyskać pełny opis parametrów konfiguracji, zapoznaj się z tabelami poniżej.

Przekształca

Przekształcenie to prosta abstrakcja, która pobiera sekwencję jako dane wejściowe, wykonuje pewne przekształcenia próbek w sekwencji i zwraca sekwencję danych wyjściowych. Typowe przykłady przekształceń to różne przekształcenia obrazów, takie jak przycinanie, skalowanie lub transponowanie. Przekształcenia można konfigurować na podstawie danych wejściowych.

Przyjrzyjmy się, jak można zastosować przekształcenia do danych wejściowych (konfiguracja jest pobierana z testu /EndToEndTests/Image/AlexNet test):

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

W tej konfiguracji cztery przekształcenia są stosowane do strumienia wejściowego features. Początkowo deserializator danych obrazu tworzy sekwencje składające się z pojedynczego obrazu w reprezentacji HWC. Następnie uporządkowana lista przekształceń jest stosowana do obrazu: najpierw przekształcenie Crop, a następnie Scale i Mean. Ostatnia transformacja jest Transponuj, która zmienia układ obrazu z HWC na CHW.

Obecnie są implementowane następujące przekształcenia. Aby uzyskać szczegółowy opis, zobacz ImageReader.

Typ przekształcenia Moduł
Wole ImageReader
Skala ImageReader
Kolor ImageReader
Znaczyć ImageReader
Transpozycji ImageReader

Opis nowego formatu konfiguracji czytnika

Sekcja konfiguracji czytnika do tworzenia kilku deserializacji danych wygląda następująco:

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

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

Każda konfiguracja deserializacji jest określona jako:

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

Konfiguracja danych wejściowych zawiera opcje specyficzne dla danych wejściowych i, opcjonalnie, uporządkowaną listę przekształceń, które mają być stosowane do danych wejściowych:

[
    # Per-input data deserializer-specific options

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

Konfiguracja przekształcania identyfikuje typ przekształcenia i wszelkie opcje specyficzne dla transformacji:

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

Opcje konfiguracji

Konfiguracja czytelnika ogólnego

Parametr Opis
verbosity Poziom szczegółowości (0, 1, 2), steruje danymi wyjściowymi diagnostycznymi różnych składników (Randomizer, Deserializer, Bundler itp.) opcjonalne, wartości domyślne 0.
randomize Określa, czy dane wejściowe powinny być losowe ( true, false). Metoda randomizacji jest identyczna jak blockRandomize HTKMLFReader. opcjonalne, wartość domyślna to true.
randomizationSeed Początkowa wartość inicjacji losowej (zwiększana jest każda zamiatanie, gdy dane wejściowe są ponownie losowe). opcjonalne, wartość domyślna to 0.
randomizationWindow Określa rozmiar (dodatnia liczba całkowita) okna losowania (tj. zakres losowania). Ten parametr wpływa na ilość zestawu danych, która musi znajdować się w pamięci jednocześnie. Opcjonalnewartość domyślna to rozmiar całego zestawu danych (w próbkach lub we fragmentach w zależności od wartości sampleBasedRandomizationWindow). Jeśli jednak jeden z deserializatorów jest CNTKTextFormatDeserializer i sampleBasedRandomizationWindow nie został jawnie ustawiony na true, randomizationWindow będzie domyślnie 128 (czyli około 4 GB miejsca na dysku o wartości fragmentów). Ten parametr jest ignorowany, gdy randomize jest false.
sampleBasedRandomizationWindow Jeśli true, rozmiar okna losowania jest interpretowany jako pewna liczba próbek i jako liczba fragmentów w przeciwnym razie. Opcjonalnewartość domyślna to true, jeśli CNTKTextFormatDeserializer nie znajduje się na liście deserializacji, a w przeciwnym razie false. Podobnie jak randomizationWindow, ten parametr jest ignorowany, gdy randomize jest false.
truncationLength Określa długość obcięcia w próbkach dla BPTT (dodatnia liczba całkowita). wymagane tylko wtedy, gdy truncated jest true, ignorowane w przeciwnym razie.
multiThreadedDeserialization Określa, czy należy używać wielu wątków podczas zbierania sekwencji dla minibatch z deserializacji (true, false). opcjonalne.
frameMode Określa, czy dane powinny być losowe i zwracane na poziomie ramki lub sekwencji. W przypadku truesekwencja wejściowa jest podzielona na ramki. opcjonalne. Nie można jednocześnie ustawić true zarówno frameMode, jak i truncated.
truncated Gdy true, włącza obcinanie propagacji wstecznej w czasie (BPTT). opcjonalne. Nie można jednocześnie ustawić true zarówno frameMode, jak i truncated.
useNumericSequenceKeys Klucze sekwencji są używane do skorelowanych sekwencji między różnymi deserializatorami. W przypadku niektórych deserializacji (tj. HTK i MLF) klucze sekwencji są dowolnymi ciągami. Przechowywanie ich wymaga dużej ilości pamięci na dużym korpusie. Jeśli masz pewność, że klucze sekwencji są numeryczne, ustaw ten parametr na wartość true, w takim przypadku wszystkie klucze ciągów zostaną przekonwertowane na liczby całkowite zmniejszające ciśnienie pamięci. opcjonalne, domyślne false.
hashSequenceKeys Ze względów pamięci opisanych powyżej można również ustawić skrót kluczy ciągów przez ustawienie tego parametru na wartość true. Użyj go tylko w przypadku deserializacji obsługujących klucze sekwencji ciągów (HTK, MLF). opcjonalne, domyślne false.
cacheIndex Określa, czy metadane utworzone podczas etapu przetwarzania wstępnego powinny być zapisywane na dysku i ładowane z dysku, jeśli są dostępne (true, false). opcjonalne, wartość domyślna to false. Aby uzyskać więcej informacji, zobacz poniższą sekcję. Nowy w CNTK w wersji 2.1.
Buforowanie indeksów

Nuta

Nowość w CNTK w wersji 2.1.

Buforowanie indeksów pozwala znacząco (przez współczynnik 2–3x) skrócić czas uruchamiania, szczególnie podczas pracy z dużymi plikami wejściowymi. Ustawienie flagi cacheIndex na wartość true zasygnalizuje czytelnikowi zapisanie metadanych na dysku (ten sam katalog co plik wejściowy), jeśli plik pamięci podręcznej jest niedostępny lub jest nieaktualny (starszy niż plik wejściowy). Pisanie jest najlepszym wysiłkiem i jest przeprowadzane na osobnym wątku, aby nie wpływać na wydajność czytnika. Jeśli plik pamięci podręcznej jest obecny i jest up-to-date, czytnik nie będzie już skąski pliku wejściowego w celu skompilowania indeksu, zamiast tego załaduje indeks z pliku pamięci podręcznej. Należy pamiętać, że niektóre parametry konfiguracji czytnika mają bezpośredni wpływ na indeksowanie (na przykład różne wartości frameMode mogą potencjalnie spowodować indeksy, które mają różną liczbę sekwencji). Z tego powodu plik pamięci podręcznej może zostać zignorowany przez czytelnika z konfiguracją inną niż ta, która wygenerowała pamięć podręczną. Aby zobaczyć pełną korzyść z buforowania, konfiguracja nie powinna być modyfikowana podczas kolejnych ponownych uruchomień.

cacheIndex nie ma wpływu na ImageDeserializer i CNTKBinaryFormatDeserializer, ponieważ poprzedni nie indeksuje danych wejściowych, a później zawiera informacje o indeksie osadzone w samym formacie.

Ogólna konfiguracja deserializacji

Parametr Opis
module Określa nazwę modułu czytnika, który implementuje deserializator danych. wymagane.
type Określa nazwę deserializacji danych uwidacznianą przez dany moduł czytnika. wymagane.

Konfiguracja przekształcania ogólnego

Parametr Opis
type Określa nazwę przekształcenia uwidacznianą przez moduł czytelnika implementując deserializacji danych. wymagane.

Opcje HTKFeatureDeserializer

Parametr Opis
scpFile Lista ścieżek do plików SCP do przetworzenia. Pliki powinny być plikami zgodnymi z zestawem HTK i muszą być określone w formacie "archiwum". Szczegóły korzystania z archiwum opisano w HTKMLF Reader. wymagane.
dim Liczba całkowita określająca pełny wymiar wektora funkcji z żądanym oknem kontekstu.1wymagane
contextWindow Można określić jako parę dodatnich liczb całkowitych lub jako pojedynczą dodatnią liczbę całkowitą (w takim przypadku jest interpretowana jako para z tą samą liczbą powtórzona dwa razy). Określa rozmiar lewej i prawej (pierwsza i druga liczba całkowita pary) okna kontekstu w przykładach. opcjonalne, wartość domyślna to 1.
prefixPathInSCP Ciąg prefiksu, który ma być stosowany do ścieżek określonych w plikach SCP. opcjonalne.

1 Na przykład jeśli masz 72-wymiarowe funkcje (24-wymiarowe funkcje filtru plus współczynniki różnicowe i różnicowe różnicowe), a sieć jest przeznaczona do przetwarzania okna kontekstowego 11 ramek, określony wymiar powinien wynosić 792.

HTKMLFDeserializer opcje

Parametr Opis
mlfFile Ścieżka do pliku mlf w stylu HTK, który zawiera etykiety dla wszystkich wypowiedzi określonych w plikach scp. wymagane, jeśli nie określono mlfFileList.
mlfFileList Tablica ścieżek do plików mlf w stylu HTK, które zawierają etykiety dla wszystkich wypowiedzi określonych w plikach scp. wymagane, jeśli nie określono mlfFile.
dim Całkowita kardynalność zestawu etykiet (dodatnia liczba całkowita). wymagane.
labelMappingFile Ścieżka do pliku, który zawiera listę wszystkich etykiet widocznych w pliku mlf, jeden na wiersz. wymagane.

labelDim można użyć jako synonimu dim.

Opcje CNTKTextFormatDeserializer

Te same opcje, których można używać z CNTKTextFormatReader

Opcje elementu ImageDeserializer

  • file: prosty plik tekstowy, w którym każdy wiersz zawiera mapowanie rozdzielane tabulatorami między kluczem sekwencji logicznej, plikiem obrazu (e.g. JPEG, PNG itp.) i etykietą opartą na 0.

Aby uzyskać więcej informacji, zobacz ImageReader.

Opcje base64ImageDeserializer

Ten deserializator obsługuje te same opcje, których można używać z elementem ImageDeserializer. Jedyną różnicą jest format pliku mapowania:

  • file: prosty plik tekstowy, w którym każdy wiersz zawiera mapowanie rozdzielane tabulatorami między kluczem sekwencji logicznej (opcjonalnie, można pominąć), etykietą kategorii 0 i zakodowanym w formacie base 64 plikiem obrazu (e.g. JPEG, PNG itp.).

Przykłady konfiguracji i testów

Pełne definicje sieci i odpowiadające im przykłady zestawu danych znajdziesz w repozytorium CNTK. W tym miejscu znajdziesz również testy jednostkowe i kompleksowe, które używają deserializacji, tj.