Sdílet prostřednictvím


Základní koncepty BrainScriptu

BrainScript – Walk-Through

Tato část představuje základní koncepty jazyka "BrainScript". Nový jazyk? Nemějte obavy, & čtěte dál, je to velmi jednoduché.

V CNTK se vlastní sítě definují pomocí jazyka BrainScriptNetworkBuilder a popsaného v jazyce popisu sítě CNTK "BrainScript". Podobně popisy sítí se označují jako brain scripts (brain scripts).

BrainScript poskytuje jednoduchý způsob, jak definovat síť v podobě kódu pomocí výrazů, proměnných, primitivních a samodefinovaných funkcí, vnořených bloků a dalších dobře srozumitelných konceptů. V syntaxi vypadá podobně jako skriptovací jazyk.

Dobře, navlhčíme si nohy pomocí kompletního příkladu BrainScriptu!

Kompletní ukázková definice sítě v BrainScriptu

Následující příklad ukazuje popis sítě jednoduché neurální sítě s jednou skrytou a jednou klasifikační vrstvou. Koncepty si vysvětlíme v tomto příkladu. Než budete pokračovat, možná strávíte pár minut s příkladem a zkuste uhodnout, co to znamená. Můžete zjistit, jak jste četli, že jste většinu z nich uhodli správně.

BrainScriptNetworkBuilder = {   # (we are inside the train section of the CNTK config file)

    SDim = 28*28 # feature dimension
    HDim = 256   # hidden dimension
    LDim = 10    # number of classes

    # define the model function. We choose to name it 'model()'.
    model (features) = {
        # model parameters
        W0 = ParameterTensor {(HDim:SDim)} ; b0 = ParameterTensor {HDim}
        W1 = ParameterTensor {(LDim:HDim)} ; b1 = ParameterTensor {LDim}

        # model formula
        r = RectifiedLinear (W0 * features + b0) # hidden layer
        z = W1 * r + b1                          # unnormalized softmax
    }.z

    # define inputs
    features = Input {SDim}
    labels   = Input {LDim} 

    # apply model to features
    z = model (features)

    # define criteria and output(s)
    ce   = CrossEntropyWithSoftmax (labels, z)  # criterion (loss)
    errs = ErrorPrediction         (labels, z)  # additional metric
    P    = Softmax (z)     # actual model usage uses this

    # connect to the system. These five variables must be named exactly like this.
    featureNodes    = (features)
    inputNodes      = (labels)
    criterionNodes  = (ce)
    evaluationNodes = (errs)
    outputNodes     = (P)
}

Základy syntaxe BrainScriptu

Než se pustíme přímo do toho, pár obecných poznámek o syntaxi BrainScriptu.

BrainScript používá jednoduchou syntaxi, která je zaměřená na to, aby bylo možné vyjádřit neurální sítě způsobem, který vypadá jako matematické vzorce. Základní syntaktická jednotka je tedy přiřazení, které se používá jak v přiřazení proměnných, tak v definicích funkcí. Příklad:

Softplus (x) = Log (1 + Exp (x))
h = Softplus (W * v + b)

Řádky, komentáře, zahrnutí

I když se přiřazení obvykle píše na jednom řádku, výrazy mohou překlenovat více řádků. Pokud ale chcete na jeden řádek umístit více přiřazení, musíte je oddělit středníkem. Příklad:

SDim = 28*28 ; HDim = 256 ; LDim = 10    # feature, hidden, and label dimension

Kromě vyžadování středníku mezi přiřazeními bez přerušení čáry není BrainScript citlivý na prázdné znaky.

BrainScript rozumí komentářům na konci řádku pomocí stylu # Pythonu i jazyka //C++. Vložené komentáře používají syntaxi jazyka C (/* this is a comment*/), ale na rozdíl od jazyka C nemusí překlenovat více řádků.

Pro BrainScript vložený do konfiguračních souborů CNTK (na rozdíl od BrainScriptu přečteného ze samostatného souboru prostřednictvím include direktivy) platí kvůli interakci s analyzátorem konfigurace (poněkud zvláštní) další omezení, že všechny závorky, složené závorky nebo hranaté závorky musí být vyváženy v komentářích stylu C/C++ a řetězcových literálech. Proto žádné smajlíky v komentářích ve stylu C/C++!

Direktiva include "PATH" může být použita na libovolném místě k vložení obsahu souboru v místě příkazu. PATH Tady může být absolutní nebo relativní relativní cesta (s podadresáři nebo bez). Pokud se jedná o relativní cestu, prohledávají se následující umístění v pořadí: aktuální pracovní adresář; adresářů obsahujících vnější včetně souborů, pokud existují; adresářů obsahujících konfigurační soubory; a nakonec adresář obsahující spustitelný soubor CNTK. Všechny integrované funkce BrainScriptu jsou zahrnuty tímto způsobem ze souboru s názvem CNTK.core.bs , který se nachází vedle spustitelného souboru CNTK.

Výrazy

Dále potřebujete vědět o výrazech v BrainScriptu – vzorcích, které popisují vaši síť. BrainScriptové výrazy jsou napsané jako matematické v syntaxi podobné oblíbeným programovacím jazykům. Nejjednodušší výrazy jsou literály, například čísla a řetězce. Matematický příklad je W1 * r + b, kde * odkazuje na skalární, maticový nebo tenzorový součin v závislosti na typu proměnných. Dalším běžným druhem výrazu je vyvolání funkce, např. RectifiedLinear (.).

BrainScript je jazyk dynamického typu. Důležitým typem výrazu je záznam definovaný pomocí {...} syntaxe a přístupný prostřednictvím syntaxe tečky. Například r = { x = 13 ; y = 42 } přiřadí záznam se dvěma členy k r, kde k prvnímu členu lze přistupovat jako r.x.

Kromě obvyklých matematických operátorů má BrainScript podmíněný výraz (if c then t else f), maticový výraz a jednoduché lambda. A konečně, pro rozhraní s kódem CNTK C++ může BrainScript přímo vytvořit instanci omezené sady předdefinovaných objektů jazyka C++, převážně ComputationNode z těchto výpočetních sítí. Další podrobnosti najdete v tématu Výrazy.

BrainScript výrazy se vyhodnocují při prvním použití. Hlavním účelem BrainScriptu je popsat síť, takže hodnota výrazu často není konečná hodnota, ale spíše uzel ve výpočetním grafu pro odložené výpočty (například ).W1 * r + b V době analýzy BrainScriptu se "počítají" pouze výrazy BrainScriptu skalárů (např. 28*28). Výrazy, které se nikdy nepoužívají (například kvůli podmínce), se nikdy nevyhodnocují.

Poznámka: Nyní zastaralá NDLNetworkBuilder verze podporovala pouze syntaxi volání funkcí. Například by bylo nutné napsat Plus (Times (W1, r), b1).

Proměnné

Proměnné mohou obsahovat hodnotu libovolného výrazu BrainScriptu (číslo, řetězec, záznam, pole, lambda, objekt C++ CNTK) a při použití ve výrazu se nahradí. Proměnné jsou neměnné, tj. přiřazené pouze jednou. Například výše uvedená definice sítě začíná na:

SDim = 28*28  
HDim = 256
LDim = 10

Zde jsou proměnné nastaveny na skalární číselné hodnoty, které se používají jako parametry v následných výrazech. Tyto hodnoty jsou dimenze ukázek dat, skrytých vrstev a popisků používaných při trénování. Tento konkrétní příklad nastavení je pro datovou sadu MNIST, což je kolekce [28 x 28]obrázků -pixel. Každý obrázek je rukou psaná číslice (0–9), takže existuje 10 možných popisků, které se dají použít u každého obrázku. Dimenze skryté aktivace HDim je volbou uživatele.

Většina proměnných jsou členové záznamu (vnější blok BrainScriptu je implicitně záznam). Kromě toho mohou být proměnné argumenty funkce nebo uložené jako prvky polí.

Ok, můžete si projít definici modelu.

Definování sítě

Síť je primárně popsána pomocí vzorců, jak se výstupy sítě počítají ze vstupů. Říkáme tomu funkce modelu, která je v BrainScriptu často definována jako skutečná funkce. Jako součást funkce modelu musí uživatel deklarovat parametry modelu. Nakonec je potřeba definovat vstupy sítě a kritéria/výstupy. Všechny tyto proměnné jsou definovány jako proměnné. Vstupní proměnné a proměnné kritérií/výstupu se pak musí sdělit systému.

Funkce modelu sítě a parametry modelu

Funkce modelu obsahuje skutečné síťové vzorce a příslušné parametry modelu. Náš příklad používá maticový součin a sčítání a pro energetickou funkci RectifiedLinear()"primitivní" (integrovanou) funkci , takže jádro síťové funkce se skládá z těchto rovnic:

r = RectifiedLinear (W0 * features + b0)
z = W1 * r + b1 

Parametry modelu jsou matice, vektory předsazení nebo jakýkoli jiný tenzor, který tvoří naučený model po dokončení trénování. Tenzory parametrů modelu se používají při transformaci vstupních ukázkových dat na požadovaný výstup a aktualizují se procesem učení. Výše uvedená ukázková síť obsahuje následující parametry matice:

W0 = ParameterTensor {(HDim:SDim)}
b0 = ParameterTensor {(HDim)}

V tomto případě W0 je matice hmotnosti a b0 je vektor předsazení. ParameterTensor{} označuje speciální primitivu CNTK, který vytvoří instanci vektoru, matice nebo tensoru libovolného pořadí a vezme parametry dimenze jako pole BrainScript (čísla zřetězená dvojtečkami :). Dimenze vektoru je jedno číslo, zatímco dimenze matice by měla být zadána jako (numRows:numCols). Ve výchozím nastavení se parametry inicializují s jednotnými náhodnými čísly, když se vytvoří instance přímo a heNormal když se používají ve vrstvách, ale pro úplný seznam existují další možnosti (viz zde). Na rozdíl od běžných funkcí ParameterTensor{} přebírá argumenty ve složených závorkách místo v závorkách. Složené závorky jsou konvencí BrainScriptu pro funkce, které vytvářejí parametry nebo objekty, na rozdíl od funkcí.

To se pak všechno zabalí do funkce BrainScriptu. Funkce BrainScriptu jsou deklarovány ve tvaru f(x) = an expression of x. Například Sqr (x) = x * x je platná deklarace funkce BrainScriptu. Nemohlo by to být mnohem jednodušší a přímé, že?

Teď je skutečná funkce modelu našeho výše uvedeného příkladu trochu složitější:

model (features) = {
    # model parameters
    W0 = ParameterTensor {(HDim:SDim)} ; b0 = ParameterTensor {HDim}  
    W1 = ParameterTensor {(LDim:HDim)} ; b1 = ParameterTensor {LDim}

    # model formula
    r = RectifiedLinear (W0 * features + b0) # hidden layer
    z = W1 * r + b1                          # unnormalized softmax
}.z

Vnější { ... } a poslední .z si zaslouží vysvětlení. Vnější curlies { ... } a jejich obsah ve skutečnosti definují záznam se 6 členy záznamu (W0, b0, W1b1, r, a z). Hodnota funkce modelu je však pouze z; všechny ostatní jsou interní funkce. Proto používáme .z k výběru člena záznamu, o který se staráme. Toto je pouze syntaxe tečky pro přístup k členům záznamu. Ostatní členové záznamu tak nebudou přístupní z vnějšku. Ale i nadále existují jako součást výrazu pro výpočet z. Vzor { ... ; x = ... }.x je způsob použití místních proměnných.

Všimněte si, že syntaxe záznamu není nutná. model(features) Případně by bylo možné deklarovat i bez objížďky přes záznam jako jeden výraz:

model (features) = ParameterTensor {(LDim:HDim)} * (RectifiedLinear (ParameterTensor {(HDim:SDim)}
                   * features + ParameterTensor {HDim})) + ParameterTensor {LDim}

To je mnohem těžší číst, a co je důležitější, neumožní použít stejný parametr na více místech ve vzorci.

Vstupy

Vstupy do sítě jsou definovány ukázkovými daty a popisky přidruženými k vzorkům:

features = Input {SDim}
labels   = Input {LDim}

Input{} je druhá speciální primitiva CNTK potřebná pro definici modelu (první je Parameter{}). Vytvoří proměnnou, která přijímá vstup mimo síť: od čtenáře. Argumentem je Input{} dimenze dat. V tomto příkladu features bude mít vstup rozměry ukázkových dat (která jsme definovali v proměnné SDim) a labels vstup bude mít rozměry popisků. Očekává se, že názvy proměnných vstupů odpovídají odpovídajícím položkám v definici čtečky.

Kritéria trénování a síťové výstupy

Stále potřebujeme deklarovat, jak výstup sítě komunikuje se světem. Naše funkce modelu vypočítá logit hodnoty (nenormalizované pravděpodobnosti protokolů). Tyto hodnoty logit lze použít k

  • definovat kritérium školení,
  • přesnost měření a
  • výpočet pravděpodobnosti nad výstupními třídami při zadání vstupu, na základě kterého se založí rozhodnutí o klasifikaci (všimněte si, že posterior z nenormalizovaného protokolu lze často použít pro přímou klasifikaci).

V ukázkové síti se používají popisky kategorií, které jsou reprezentovány jako vektory 1-hot. V příkladu MNIST se zobrazí jako pole 10 hodnot s plovoucí desetinnou čárkou, které jsou všechny nulové s výjimkou správné kategorie popisku, která je 1,0. Klasifikační úlohy, jako je ta naše, běžně používají SoftMax() funkci k získání pravděpodobností pro každý popisek. Síť je pak optimalizována tak, aby maximalizovala pravděpodobnost protokolu správné třídy (křížová entropie) a minimalizovala pravděpodobnost všech ostatních tříd. Toto je naše trénovací kritérium neboli ztrátová funkce. V CNTK se tyto dvě akce z důvodu efektivity obvykle kombinují do jedné funkce:

ce = CrossEntropyWithSoftmax (labels, z)

CrossEntropyWithSoftmax() funkce přebírá vstup, vypočítá SoftMax() funkci, vypočítá chybu ze skutečné hodnoty pomocí křížové entropie a tento chybový signál se používá k aktualizaci parametrů v síti prostřednictvím zpětného šíření. Proto se ve výše uvedeném příkladu během trénování nepoužívá normalizovaná Softmax() hodnota, kterou jsme vypočítávali jako P. Bude však potřeba pro použití sítě (opět mějte na paměti, z že v mnoha případech je často dostačující pro klasifikaci; z v takovém případě by sama o sobě byla výstupem).

CNTK používá jako algoritmus učení stochastický gradientní sestup (SGD ). SGD potřebuje vypočítat gradient objektivní funkce s ohledem na všechny parametry modelu. Důležité je, že CNTK nevyžaduje, aby uživatelé tyto přechody určili. Každá integrovaná funkce v CNTK má také odvozenou funkci a systém automaticky provede aktualizaci zpětného šíření parametrů sítě. Uživatel tuto možnost nevidí. Uživatelé se nikdy nemusí zabývat přechody. Někdy.

Kromě kritéria trénování se během trénovací fáze často počítají predikované míry chyb, aby se ověřilo zlepšení systému v průběhu dalšího trénování. To se v CNTK zpracovává pomocí následující funkce:

errs = ClassificationError (labels, z)

Pravděpodobnosti vytvořené sítí se porovnávají se skutečným popiskem a vypočítá se míra chyb. To se obvykle zobrazuje v systému. I když je to užitečné, není nutné používat ClassificationError().

Komunikace vstupů, výstupů a kritérií do systému

Teď, když jsou všechny proměnné definované, musíme systému říct, které z proměnných by měl považovat za vstupy, výstupy a kritéria. To se provádí definováním 5 speciálních proměnných, které musí mít přesně tyto názvy:

featureNodes    = (features)
labelNodes      = (labels)
criterionNodes  = (ce)
evaluationNodes = (errs)
outputNodes     = (z:P)

Hodnoty jsou matice, kde hodnoty by měly být oddělené dvojtečkami (dvojtečka : je brainscriptový operátor, který vytvoří pole zřetězením dvou hodnot nebo matic). To je znázorněno výše pro outputNodes, který deklaruje jak , tak z jako P výstupy.

(Poznámka: Zastaralé NDLNetworkBuilder vyžaduje, aby prvky pole byly oddělené čárkami.)

Souhrn speciálních názvů

Jak jsme viděli výše, musíme znát 7 speciálních názvů, které mají "magické" vlastnosti:

  • ParameterTensor{}: Deklaruje a inicializuje učitelný parametr.
  • Input{}: Deklaruje proměnnou, která se připojí a předá ze čtečky dat.
  • featureNodes, , labelNodescriterionNodes, evaluationNodesa outputNodes: Deklaruje systému, které z našich proměnných použít jako vstupy, výstupy a kritéria.

Kromě toho existují další 3 speciální funkce s integrovanými "magic" v CNTK, které jsou popsány jinde:

  • Constant(): Deklaruje konstantu.
  • PastValue() a FutureValue(): Přístup k proměnné v síťové funkci v jiném časovém kroku pro opakující se smyčky formulářů.

Jakýkoli jiný předdefinovaný název je buď předdefinovaná primitivní funkce, jako Sigmoid() je nebo Convolution() s implementací jazyka C++, předdefinovaná funkce knihovny realovaná v BrainScriptu, jako BS.RNNs.LSTMP()je , nebo záznam, který funguje jako obor názvů pro funkce knihovny (např. BS.RNNs). Úplný seznam najdete v tématu BrainScript-Full-Function-Reference .

Další: Výrazy v jazyce BrainScript