Udostępnij przez


Edytowanie modelu za pomocą języka BrainScript

(Uwaga: Starsze wersje CNTK używane "MEL" (język edycji modelu) do tego celu. Nadal trwa konwertowanie przykładów. Aby uzyskać dokumentację dotyczącą mel, zobacz tutaj.)

CNTK umożliwia edytowanie modeli po fakcie. Jest to realizowane przez utworzenie nowego modelu podczas klonowania (części) istniejącego modelu z zastosowanymi modyfikacjami. W tym celu CNTK udostępnia trzy podstawowe funkcje:

  • BS.Network.Load() do załadowania istniejącego modelu
  • BS.Network.CloneFunction() wyodrębnić sekcję istniejącego modelu do ponownego użycia
  • BS.Network.Edit() sklonować model z zastosowanymi modyfikacjami węzła po węźle

Operacja edycji nie jest osobnym krokiem. Zamiast tego polecenie, które powinno odpracować zmodyfikowany model, nie określi modelPath do załadowania modelu, ale raczej BrainScriptNetworkBuilder sekcji, która ładuje model wewnątrz i tworzy nowy model od załadowanego modelu na bieżąco.

Przykład: dyskryminacyjne wstępne szkolenie

Dyskryminacyjne wstępne szkolenie to technika, w której sieć głęboka jest tworzona przez szkolenie sekwencji płytkich sieci. Zacznij od sieci 1-ukrytej warstwy, wytrenuj do częściowej zbieżności, a następnie usuń warstwę wyjściową, dodaj nową warstwę ukrytą i dodaj nową warstwę wyjściową. Powtarzaj, aż zostanie osiągnięta żądana liczba ukrytych warstw.

Załóżmy, że jest to bardzo prosty model początkowy

BrainScriptNetworkBuilder = [
    N = 40; M = 9000; H = 512
    W1   = Parameter (H, N); b1   = Parameter (H)
    Wout = Parameter (M, H); bout = Parameter (M)
    x = Input (N, tag=‘feature’) ; labels = Input (M, tag=‘labels’)
    h1 = Sigmoid (W1 * x  + b1)
    z  = Wout * h1 + bout
    ce = CrossEntropyWithSoftmax (labels, z, tag=‘criterion’)
]

Wytrenujmy ten model i zapiszmy go w obszarze "model.1.dnn". Następnie chcemy wytrenować model z dwoma ukrytymi warstwami, w których pierwsza warstwa ukryta jest inicjowana z wartości wytrenowanych powyżej. W tym celu utworzymy oddzielną akcję szkoleniową, która tworzy nowy model, ale ponownie używa części poprzedniej, w następujący sposób:

BrainScriptNetworkBuilder = {
    # STEP 1: load 1-hidden-layer model
    inModel = BS.Network.Load ("model.1.dnn")
    # get its h1 variable --and also recover its dimension
    h1 = inModel.h1
    H = h1.dim
    # also recover the number of output classes
    M = inModel.z.dim

    # STEP 2: create the rest of the extended network as usual
    W2   = Parameter (H, H); b2   = Parameter (H)
    Wout = Parameter (M, H); bout = Parameter (M)
    h2 = Sigmoid (W2 * h1  + b2)
    z  = Wout * h2 + bout
    ce = CrossEntropyWithSoftmax (labels, z, tag=‘criterion’)
}

Najpierw krok 1 używa Load() do załadowania sieci do zmiennej BrainScript. Sieć zachowuje się jak rekord BrainScript, w którym wszystkie węzły najwyższego poziomu (wszystkie węzły, które nie zawierają . lub [ w nazwach węzłów), są dostępne za pośrednictwem składni rekordów. Nowa sieć może odwoływać się do dowolnego węzła w załadowanej sieci. W tym przykładzie załadowana sieć zawiera węzeł h1, który jest wynikiem pierwszej ukrytej warstwy, a węzeł z, który jest nienormalizowanym prawem tylnym dziennika klas wyjściowych (dane wejściowe do funkcji Softmax). Dostęp do obu węzłów można uzyskać z języka BrainScript za pomocą składni kropki, np. inModel.h1 i inModel.z.

Należy pamiętać, że stałe nie są przechowywane w modelach, więc ani N, ani M nie są dostępne w modelu. Możliwe jest jednak odtworzenie ich z załadowanego modelu. W tym celu węzły obliczeniowe zachowują się również jak rekordy BrainScript i uwidaczniają właściwość dim.

Następnie krok 2 konstruuje resztę nowej sieci przy użyciu zwykłego języka BrainScript. Należy pamiętać, że w tej nowej sekcji po prostu użyto h1 węzła z modelu wejściowego jako danych wejściowych, podobnie jak w przypadku dowolnego innego węzła. Odwoływanie się do węzła z sieci wejściowej spowoduje automatyczne utworzenie wszystkich węzłów, od których ten węzeł zależy również od części nowo utworzonej sieci. Na przykład węzeł wejściowy x automatycznie stanie się częścią nowej sieci.

Należy również pamiętać, że warstwa wyjściowa jest konstruowana na nowo. W ten sposób jego parametry modelu zostaną nowo utworzone. (Aby tego nie zrobić i zamiast tego ponownie użyć istniejących parametrów, można użyć inModel.Wout, ale należy pamiętać, że nie ma sensu z punktu widzenia projektu sieci w tym konkretnym przykładzie).

Przykład: używanie wstępnie wytrenowanego modelu

Poniżej przedstawiono przykład użycia wstępnie wytrenowanego modelu (z pliku "./featext.dnn") jako wyodrębniacza funkcji:

BrainScriptNetworkBuilder = {
    # STEP 1: load existing model
    featExtNetwork = BS.Network.Load ("./featext.dnn")

    # STEP 2: extract a read-only section that is the feature extractor function
    featExt = BS.Network.CloneFunction (
                  featExtNetwork.input,    # input node that AE model read data from
                  featExtNetwork.feat,     # output node in AE model that holds the desired features
                  parameters="constant")   # says to freeze that part of the network

    # STEP 3: define the part of your network that uses the feature extractor
    # from the loaded model, which above we isolated into featExt().
    # featExt() can be used like any old BrainScript function.
    input = Input (...)
    features = featExt (input)  # this will instantiate a clone of the above network

    # STEP 4: and add the remaining bits of the network in BrainScript, e.g.
    h = Sigmoid (W_hid * features + b_hid) # whatever your hidden layer looks like
    z = W_out * h + b_out
    ce = CrossEntropyWithSoftmax (labels, z)
    criterionNodes = (ce)
}

KROK 1 używa Load() do załadowania sieci do zmiennej BrainScript.

KROK 2 używa CloneFunction() do sklonowania sekcji związanej z wyodrębnianiem funkcji z załadowanej sieci, która jest podgrafem, który łączy featExtNetwork.input z featExtNetwork.feat. Ponieważ określono parameters="constant", wszystkie parametry, od których featExtNetwork.feat zależy, są również klonowane i wykonywane tylko do odczytu.

W kroku 3 i 4 nowa sieć jest zdefiniowana. Dzieje się tak jak w przypadku każdego innego modelu BrainScript, ale teraz możemy użyć funkcji featExt() w ten sposób.

Problemy z nazwami węzłów .[ i ]

Aby odwołać się do węzła w sieci zawierającej . lub [ lub ], zastąp te znaki _. Na przykład jeśli network zawiera węzeł o nazwie result.z, network.result.z zakończy się niepowodzeniem; zamiast tego należy powiedzieć network.result_z.

Przykład: modyfikowanie węzłów istniejącej sieci

Aby zmodyfikować wewnętrzne części istniejącej sieci, należy sklonować sieć, podczas gdy modyfikacje są stosowane w ramach procesu klonowania. Jest to realizowane przez BS.Network.Edit(). Edit() będzie iterować we wszystkich węzłach sieci i będzie oferować każdy węzeł po jednym do funkcji lambda przekazywanych przez obiekt wywołujący. Te funkcje lambda mogą następnie sprawdzać węzeł i zwracać węzeł niezmodyfikowany lub zwracać nowy węzeł w jego miejscu. Edit() będzie iterować węzły w nieokreślonej kolejności. Jeśli węzeł zastępczy odwołuje się do węzła sieciowego, który z kolei został zastąpiony, Edit(), jako ostatni krok, zaktualizuje wszystkie takie odwołania do odpowiednich zastąpień (aka "zrób to dobrze").

TODO: Przykład.

Dalej: pełną dokumentację funkcji