Udostępnij za pośrednictwem


BrainScript Network Builder

Sieci niestandardowe są opisane w niestandardowym języku opisu sieci CNTK "BrainScript". Aby zdefiniować sieć niestandardową, dołącz sekcję o nazwie BrainScriptNetworkBuilder w konfiguracji trenowania. Szczegółowy opis języka opisu sieci można znaleźć na stronie Podstawowe pojęcia i odpowiednie podstrony.

Istnieją dwie formy korzystania z konstruktora sieci BrainScript, jeden z nawiasami (...)i krótką formę przy użyciu nawiasów klamrowych {...}. Aby opisać sieć w pliku zewnętrznym, określ blok podobny do następującego:

BrainScriptNetworkBuilder = (new ComputationNetwork {
    include "yourNetwork.bs"
})

gdzie yourNetwork.bs zawiera sieć opisaną przy użyciu języka BrainScript. Plik yourNetwork.bs jest szukany jako pierwszy w tym samym katalogu co plik konfiguracji, a jeśli nie zostanie znaleziony, w katalogu pliku wykonywalnego CNTK. Nazwy ścieżek bezwzględnych i względnych są akceptowane tutaj. Np. bs/yourNetwork.bs oznacza plik znajdujący się w katalogu bs obok pliku konfiguracji (lub alternatywnie katalog wewnątrz katalogu bs wykonywalnego CNTK).

Uwaga: do wersji CNTK 1.6 język BrainScript używał nawiasów klamrowych zamiast nawiasów [...] klamrowych {...}. Nawiasy są nadal akceptowane, ale przestarzałe.

Alternatywnie możesz zdefiniować sieć w tekście bezpośrednio w pliku konfiguracji. Może to uprościć konfigurację, jeśli nie planujesz współużytkowania tego samego skryptu mózgu w wielu konfiguracjach. Użyj tego formularza:

BrainScriptNetworkBuilder = {
    # insert network description here
}

Więc jak wygląda kod BrainScript, który przechodzi do nawiasów? Aby dowiedzieć się, przejdź bezpośrednio do podstaw brainscript podstawowych pojęć.

Możesz też pozostać na tej stronie i przeczytać na temat niektórych rzadziej potrzebnych szczegółów.

Powyższy {...} formularz jest naprawdę tylko krótką ręką dla tego:

BrainScriptNetworkBuilder = (new ComputationNetwork {
    # insert network description here
})

Na koniec, jako zaawansowane użycie, (...) formularz nie jest ograniczony do użycia new. Zamiast tego każde wyrażenie BrainScript, które oblicza obiekt, ComputationNetwork jest dozwolone wewnątrz nawiasów. Na przykład:

BrainScriptNetworkBuilder = ({
    include "myNetworks.bs"
    network = CreateMyNetworkOfType42()
}.network)

Jest to zaawansowane użycie, które czasami występuje również w kontekście edycji modelu.

Dalej: Podstawowe pojęcia dotyczące języka BrainScript.

Starszych NDLNetworkBuilder

W starszych wersjach CNTK konstruktor sieci został nazwany NDLNetworkBuilder. Jego język definicji jest podzbiorem BrainScript. Stary analizator był mniej zdolny, ale również bardziej forgiving. Istnieją również inne małe różnice.

NDLNetworkBuilder jest teraz przestarzały, ale ze względu na podobieństwo nie jest trudno uaktualnić do BrainScriptNetworkBuilderklasy . Poniżej przedstawiono przewodnik dotyczący konwertowania NDLNetworkBuilder opisów sieci na BrainScriptNetworkBuilder's).

Aktualizowanie z NDLNetworkBuilder do BrainScriptNetworkBuilder

Konwertowanie istniejącej definicji sieci dla elementu NDLNetworkBuilder to BrainScriptNetworkBuilder jest proste w większości przypadków. Główne zmiany to otaczająca składnia. Sam opis sieci podstawowej jest w dużej mierze zgodny w górę i prawdopodobnie identyczny lub niemal identyczny, jeśli nie korzystasz z nowych funkcji językowych.

Aby przekonwertować opisy, należy przełączyć konstruktora sieci, dostosować składnię zewnętrzną w.r.t. i ewentualnie wprowadzić drobne dostosowania do samego kodu sieciowego.

Krok 1. Przełączanie konstruktora sieci. NDLNetworkBuilder Zastąp element odpowiednim BrainScriptNetworkBuilder blokiem w pliku konfiguracji CNTK. Jeśli opis sieci znajduje się w osobnym pliku:

# change from:
NDLNetworkBuilder = [
    ndlMacros = "shared.ndl"   # (if any)
    networkDescription = "yourNetwork.ndl"
]
# ...to:
BrainScriptNetworkBuilder = (new ComputationNetwork {
    include "shared.bs"        # (if any)
    include "yourNetwork.bs"
})

(Zmiana rozszerzenia nazwy pliku nie jest ściśle konieczna, ale zalecana).

Jeśli opis sieci znajduje się w .cntk samym pliku konfiguracji:

# change from:
NDLNetworkBuilder = [
    # macros
    load = [
        SigmoidNetwork (x, W, b) = Sigmoid (Plus (Times (W, x), b))
    ]
    # network description
    run = [
        feat = Input (13)
        ...
        ce = CrossEntropyWithSoftmax (labels, z, tag="criterion")
    ]
]
# ...to:
BrainScriptNetworkBuilder = {
    # macros are just defined inline
    SigmoidNetwork (x, W, b) = Sigmoid (Plus (Times (W, x), b))  # or: Sigmoid (W * x + b)
    # network description
    feat = Input {13}
    ...
    ce = CrossEntropyWithSoftmax (labels, z, tag="criterion")
}

Krok 2. Usuń load i run bloki. W programie BrainScriptNetworkBuildersą łączone definicje makr/funkcji i kod główny. Bloki load i run muszą zostać po prostu usunięte. Na przykład:

load = ndlMnistMacros
run = DNN
ndlMnistMacros = [
    featDim = 784
    ...
    labels = InputValue(labelDim)
]
DNN = [
    hiddenDim = 200
    ...
    outputNodes = (ol)
]

po prostu staje się:

featDim = 784
...
labels = InputValue(labelDim)
hiddenDim = 200
...
outputNodes = (ol)

Być może użyto zmiennej run do wybrania jednej z wielu konfiguracji ze zmienną zewnętrzną, np.:

NDLNetworkBuilder = [
    run = $whichModel$   # outside parameter selects model, must be either "model1" or "model2"
    model1 = [ ... (MODEL 1 DEFINITION) ]
    model2 = [ ... (MODEL 1 DEFINITION) ]
]

Ten wzorzec był głównie niezbędny, ponieważ NDL nie ma wyrażeń warunkowych. W języku BrainScript zostanie to teraz napisane za if pomocą wyrażenia:

BrainScriptNetworkBuilder = (new ComputationNetwork
    if      "$whichModel$" == "model1" then { ... (MODEL 1 DEFINITION) }
    else if "$whichModel$" == "model2" then { ... (MODEL 2 DEFINITION) }
    else Fail ("Invalid model selector value '$whichModel$'")
)

Jednak często wybrane modele są bardzo podobne, więc lepszym sposobem byłoby scalenie ich opisów i zamiast tego użycie warunkowych w obrębie tylko tam, gdzie się różnią. Oto przykład, w którym parametr jest używany do wyboru między jednokierunkowym i dwukierunkowym LSTM:

encoderFunction =
    if useBidirectionalEncoder
    then BS.RNNs.RecurrentBirectionalLSTMPStack
    else BS.RNNs.RecurrentLSTMPStack
encoder = encoderFunction (encoderDims, inputEmbedded, inputDim=inputEmbeddingDim)

Krok 3. Dostosowywanie opisu sieci. Jeśli chodzi o sam opis sieci (formuły), BrainScript jest w dużej mierze w górę zgodny z NDL. Są to główne różnice:

  • Zwracana wartość makr (funkcji) nie jest już ostatnią zmienną zdefiniowaną w nich, ale cały zestaw zmiennych. Musisz jawnie wybrać wartość wyjściową na końcu. Na przykład:

      # NDL:  
      f(x) = [  
          x2 = Times (x, x)  
          y = Plus (x2, Constant (1))  
      ]  # <-- return value defaults to last entry, i.e. y  
      # BrainScript:
      f(x) = {
          x2 = x*x  
          y = x2 + 1  
      }.y  # <-- return value y must be explicitly dereferenced
    

    Bez tej zmiany zwracana wartość funkcji będzie całym rekordem, a typowy błąd, który zostanie wyświetlony, jest to, że ComputationNode oczekiwano miejsca znalezienia elementu ComputationNetwork .

  • Język BrainScript nie zezwala na funkcje ze zmiennymi liczbami parametrów. Ma to znaczenie przede wszystkim dla Parameter() funkcji: Parametr wektora nie może być już zapisywany jako Parameter(N), teraz musi być jawnie zapisany jako tensor ParameterTensor{N} lub macierz Parameter(N, 1)1-kolumnowa . Bez tej zmiany zostanie wyświetlony błąd dotyczący niezgodności liczby parametrów pozycyjnych. Ta notacja działa również z językiem NDL, aby można było najpierw wprowadzić tę zmianę i przetestować ją przy użyciu języka NDL przed przekonwertowaniem. Jest to również dobra okazja do zmiany nazwy wszystkich zastosowań starszej nazwy LearnableParameter() na ParameterTensor{}.

    Ma to również znaczenie dla RowStack() funkcji, która w języku BrainScript przyjmuje pojedynczy parametr, który jest tablicą danych wejściowych. Dane wejściowe muszą być rozdzielone dwukropkiem (:) zamiast przecinka, np. RowStack (a:b:c) zamiast RowStack (a, b, c).

  • Niektóre wartości domyślne zostały zaktualizowane, przede wszystkim opcjonalny imageLayout parametr Convolution()operacji puli i ImageInput(). W przypadku języka NDL te domyślne wartości legacyto , natomiast teraz wartość domyślna jest cudnn wymagana do zgodności z elementami pierwotnymi cuDNN. (Wszystkie przykłady kodu NDL jawnie określają ten parametr jako cudnn już).

  • Analizator BrainScript jest bardziej restrykcyjny:

    • Identyfikatory są teraz uwzględniane wielkość liter. Wbudowane funkcje używają funkcji PascalCase (np. RectifiedLinear()), a wbudowane zmienne i nazwy parametrów używają camelCase (np. modelPath, criterionNodes), jak do ciągów opcji (init="fixedValue", tag="criterion"). Należy pamiętać, że w przypadku nazw parametrów opcjonalnych nieprawidłowe pisownie nie zawsze są przechwytywane jako błąd. Zamiast tego niektóre niepoprawnie napisane parametry opcjonalne są po prostu ignorowane. Przykładem są definicje "węzłów specjalnych". Ich poprawne pisownia dla tych jest teraz:

        featureNodes    = ...
        labelNodes      = ...
        criterionNodes  = ...
        evaluationNodes = ...
        outputNodes     = ...
      
    • Skrócone nazwy alternatywne nie są już dozwolone, takie jak Const() powinna być , powinna być tag="evaluation"Constant(), tag="eval" i evalNodes jest teraz evaluationNodes.

    • Poprawiono niektóre nieprawidłowe nazwy: criteria jest teraz criterion (podobnie criterionNodes), defaultHiddenActivity jest teraz defaultHiddenActivation.

    • Znak = nie jest już opcjonalny dla definicji funkcji.

    • Chociaż może używać nawiasów dla bloków ([ ... ]), jest przestarzały. Użyj nawiasów klamrowych ({ ... }).

    • Etykiety opcji muszą być cytowane jako ciągi, np. init="uniform" zamiast init=uniform. Bez cudzysłowów kod BrainScript zakończy się niepowodzeniem z komunikatem o błędzie informującym, że symbol uniform jest nieznany.

    • Typy pierwotne BrainScript, które tworzą parametry (Input{} i ParameterTensor{}), powinny używać nawiasów klamrowych dla argumentów (np. f = Input{42}). Jest to konwencja, która nie jest wymuszana, ale zalecana w przyszłości.

    Ta bardziej ograniczona składnia jest nadal akceptowana przez NDLNetworkBuilderprogram , dlatego zalecamy najpierw wprowadzenie tych zmian syntatycznych i przetestowanie ich za pomocą języka NDL, zanim rzeczywiście zmieni się na BrainScript.

Krok 4. Usuń NDLNetworkBuilder z sekcji "write" i "test". Przejrzyj sekcje "write" i "test" w NDLNetworkBuilder sekcjach i usuń je. Niektóre z naszych stockowych przykładów NDL mają dodatkowe NDLNetworkBuilder sekcje. Nie są używane i nie powinny być tam. Jeśli konfiguracja jest oparta na jednym z tych przykładów, możesz również mieć takie sekcje. Kiedyś były ignorowane, ale w przypadku aktualizacji BrainScript zdefiniowanie nowej sieci w tych sekcjach ma teraz znaczenie (edytowanie modelu), więc nie są już ignorowane i dlatego należy je usunąć.

NDLNetworkBuilder reference (przestarzałe)

Składnia przestarzałego NDLNetworkBuilder elementu to:

NDLNetworkBuilder = [
    networkDescription = "yourNetwork.ndl"
]

Blok NDLNetworkBuilder ma następujące parametry:

  • networkDescription: ścieżka pliku opisu sieci. W przypadku przestarzałej NDLNetworkBuilderwersji niestandardowej było użycie rozszerzenia .ndlpliku . Jeśli nie networkDescription określono parametru, zakłada się, że opis sieci zostanie podkreślony w tym samym NDLNetworkBuilder podbloku, określony za pomocą poniższego parametru run . Należy pamiętać, że za pomocą parametru można określić tylko jedną ścieżkę networkDescription pliku. Aby załadować wiele plików makr, użyj parametru ndlMacros .

  • run: blok NDL, który zostanie wykonany. Jeśli zewnętrzny plik NDL jest określony za pośrednictwem parametru networkDescription , run parametr identyfikuje blok w tym pliku. Ten parametr zastępuje wszystkie run parametry, które mogą już istnieć w pliku. Jeśli nie networkDescription określono pliku, run parametr identyfikuje blok w bieżącym pliku konfiguracji.

  • load: bloki skryptów NDL do załadowania. Wiele bloków można określić za pośrednictwem listy rozdzielonej ciągiem ":". Bloki określone przez load parametr zwykle zawierają makra do użycia przez run blok. Podobnie jak w przypadku parametru run , load parametr identyfikuje bloki w zewnętrznym pliku NDL i zastępuje wszystkie load parametry, które mogą już istnieć w pliku, jeśli plik jest określony przez networkDescription parametr . Jeśli nie networkDescription określono pliku, load identyfikuje blok w bieżącym pliku konfiguracji.

  • ndlMacros: ścieżka pliku, w której można załadować makra NDL. Ten parametr jest zwykle używany do ładowania domyślnego zestawu makr NDL, które mogą być używane przez wszystkie skrypty NDL. Wiele plików NDL, z których każdy określa różne zestawy makr, można załadować, określając rozdzielaną listę ścieżek plików "+" dla tego ndlMacros parametru. Aby udostępnić makra innym blokom poleceń, takim jak bloki języka edycji modelu NDL (MEL), należy zdefiniować je na poziomie głównym pliku konfiguracji.

  • randomSeedOffset: wartość przesunięcia inicjującego losowe inicjowanie parametrów możliwych do nauki. Wartość domyślna to 0. Dzięki temu użytkownicy mogą uruchamiać eksperymenty z inną losową inicjacją.