Dela via


BrainScript Network Builder

Anpassade nätverk beskrivs i CNTK:s anpassade nätverksbeskrivningsspråk "BrainScript". Om du vill definiera ett anpassat nätverk inkluderar du ett avsnitt med namnet BrainScriptNetworkBuilder i din träningskonfiguration. Detaljerad beskrivning av nätverksbeskrivningsspråket finns på sidan Grundläggande begrepp och motsvarande undersidor.

Det finns två former av att använda BrainScript-nätverksverktyget, en med parenteser (...)och ett kortformulär med klammerparenteser {...}. Om du vill beskriva nätverket i en extern fil anger du ett block som liknar detta:

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

där yourNetwork.bs innehåller nätverket som beskrivs med Hjälp av BrainScript. Filen yourNetwork.bs letar först efter i samma katalog som konfigurationsfilen, och om den inte hittas, i katalogen för den körbara CNTK-filen. Både absoluta och relativa sökvägar accepteras här. T.ex. bs/yourNetwork.bs innebär en fil som finns i en katalog bs bredvid din konfigurationsfil (eller alternativt en katalog bs i den körbara CNTK-katalogen).

Obs! Fram till CNTK 1.6 använde BrainScript hakparenteser [...] i stället för klammerparenteser {...}. Hakparenteser accepteras fortfarande men är inaktuella.

Du kan också definiera nätverket infogat direkt i konfigurationsfilen. Detta kan förenkla konfigurationen om du inte planerar att dela samma hjärnskript i flera konfigurationer. Använd det här formuläret:

BrainScriptNetworkBuilder = {
    # insert network description here
}

Så hur ser BrainScript-koden ut så där inom hakparenteserna? Du kan ta reda på det genom att gå direkt till Grundläggande begrepp i BrainScript.

Eller stanna kvar på den här sidan och läs vidare om några mindre ofta nödvändiga detaljer för dig.

Formuläret {...} ovan är egentligen bara en kort hand för detta:

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

Slutligen, som en avancerad användning, (...) är formuläret inte begränsat till att använda new. I stället tillåts alla BrainScript-uttryck som utvärderas till ett objekt i ComputationNetwork inom parenteserna. Ett exempel:

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

Detta är en avancerad användning som ibland även sker i samband med modellredigering.

Nästa: Grundläggande begrepp i BrainScript.

Legacy NDLNetworkBuilder

I äldre versioner av CNTK kallades NDLNetworkBuildernätverksverktyget . Dess definitionsspråk är en delmängd av BrainScript. Den gamla parsern var mindre kapabel, men också mer förlåtande. Det finns också andra små skillnader.

NDLNetworkBuilder är nu inaktuell, men på grund av likheten är det inte svårt att uppgradera till BrainScriptNetworkBuilder. Följande är en guide om hur du konverterar NDLNetworkBuilder nätverksbeskrivningar till BrainScriptNetworkBuilder's.

Uppdatera från NDLNetworkBuilder till BrainScriptNetworkBuilder

Det är i de flesta fall enkelt att konvertera en befintlig nätverksdefinition för NDLNetworkBuilder till BrainScriptNetworkBuilder . De viktigaste ändringarna är den omgivande syntaxen. Själva beskrivningen av kärnnätverket är i stort sett uppåtkompatibel och sannolikt identisk eller nästan identisk om du inte drar nytta av de nya språkfunktionerna.

Om du vill konvertera dina beskrivningar måste du växla nätverksbyggaren, anpassa den yttre syntaxen för w.r.t. och eventuellt göra mindre anpassningar av själva nätverkskoden.

Steg 1. Växla nätverksverktyget. NDLNetworkBuilder Ersätt med motsvarande BrainScriptNetworkBuilder block i CNTK-konfigurationsfilen. Om nätverksbeskrivningen finns i en separat fil:

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

(Ändringen av filnamnstillägget är inte absolut nödvändigt men rekommenderas.)

Om nätverksbeskrivningen finns i själva konfigurationsfilen .cntk :

# 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")
}

Steg 2. Ta bort load och run block. Med BrainScriptNetworkBuilderkombineras makro-/funktionsdefinitioner och huvudkod. Blocken load och run måste helt enkelt tas bort. Det här kan till exempel vara:

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

blir helt enkelt:

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

Du kan ha använt variabeln run för att välja en av flera konfigurationer med en extern variabel, t.ex.

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

Det här mönstret var mest nödvändigt eftersom NDL inte hade villkorsuttryck. I BrainScript skulle detta nu skrivas med ett if uttryck:

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$'")
)

Men ofta är de valda modellerna mycket lika, så ett bättre sätt är att sammanfoga deras beskrivningar och i stället använda villkor inuti endast för var de skiljer sig åt. Här är ett exempel där en parameter används för att välja mellan en enkelriktad och dubbelriktad LSTM:

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

Steg 3. Justera nätverksbeskrivningen. När det gäller själva nätverksbeskrivningen (formler) är BrainScript i stort sett uppåt kompatibelt med NDL. Det här är de största skillnaderna:

  • Returvärdet för makron (funktioner) är inte längre den sista variabeln som definierats i dem, utan hela uppsättningen variabler. Du måste uttryckligen välja utdatavärdet i slutet. Ett exempel:

      # 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
    

    Utan den här ändringen skulle funktionens returvärde vara hela posten, och det vanliga felet du får är att en ComputationNode förväntades där en ComputationNetwork hittades.

  • BrainScript tillåter inte funktioner med variabelt antal parametrar. Detta gäller främst för Parameter() funktionen: En vektorparameter kan inte längre skrivas som Parameter(N), den måste nu uttryckligen skrivas som en tensor ParameterTensor{N} eller en matris med 1 kolumner Parameter(N, 1). Utan den här ändringen får du ett felmeddelande om att antalet positionsparametrar inte matchar. Den här notationen fungerar också med NDL, så du kan göra den här ändringen först och testa den med NDL innan du konverterar. Det här är också ett bra tillfälle att byta namn på alla användningsområden för det äldre namnet LearnableParameter() till ParameterTensor{}.

    Det är också viktigt för RowStack() funktionen, som i BrainScript tar en enda parameter som är en matris med indata. Indata måste avgränsas med ett kolon (:) i stället för ett kommatecken, t.ex. RowStack (a:b:c) i stället för RowStack (a, b, c).

  • Vissa standardvärden har uppdaterats, främst den valfria imageLayout parametern Convolution()för , poolåtgärderna och ImageInput(). För NDL har dessa standardvärdet legacy, medan nu är cudnn standardvärdet som måste vara kompatibelt med cuDNN-faltningsprimitiver. (Alla NDL-kodexempel anger uttryckligen den här parametern som cudnn redan.)

  • BrainScript-parsern är mer restriktiv:

    • Identifierare är nu skiftlägeskänsliga. Inbyggda funktioner använder PascalCase (t.ex. RectifiedLinear()) och inbyggda variabler och parameternamn använder camelCase (t.ex. modelPath, criterionNodes), liksom alternativsträngar (init="fixedValue", tag="criterion"). Observera att felaktiga stavningar inte alltid fångas upp som ett fel för namn på valfria parametrar. I stället ignoreras vissa felaktigt stavade valfria parametrar. Ett exempel är definitionerna "särskilda noder". Deras rätta stavning för dessa är nu:

        featureNodes    = ...
        labelNodes      = ...
        criterionNodes  = ...
        evaluationNodes = ...
        outputNodes     = ...
      
    • Förkortade alternativa namn är inte längre tillåtna, till exempel Const() ska vara Constant(), tag="eval" ska vara tag="evaluation"och evalNodes är nu evaluationNodes.

    • Några felstavade namn korrigerades: är nu criterion (på samma sätt criterionNodes), defaultHiddenActivity är nu defaultHiddenActivation. criteria

    • Tecknet = är inte längre valfritt för funktionsdefinitioner.

    • Även om det är tillåtet att använda hakparenteser för block ([ ... ]), är det inaktuellt. Använd klammerparenteser ({ ... }).

    • Alternativetiketter måste anges som strängar, t.ex. init="uniform" i stället init=uniformför . Utan citattecken misslyckas BrainScript med ett felmeddelande om att symbolen uniform är okänd.

    • BrainScript-primitiverna som skapar parametrarna (Input{} och ParameterTensor{}) bör använda klammerparenteser för sina argument (t.ex. f = Input{42}). Detta är en konvention som inte tillämpas men som rekommenderas framöver.

    Den här mer begränsade syntaxen accepteras fortfarande av NDLNetworkBuilder, så vi rekommenderar att du först gör dessa syntaktiska ändringar och testar dem med NDL innan du faktiskt ändrar till BrainScript.

Steg 4. Ta bort NDLNetworkBuilder från avsnitten "write" och "test". Läs avsnitten "skriva" och "testa" för NDLNetworkBuilder avsnitt och ta bort dem. Några av våra NDL-exempel för lager har överflödiga NDLNetworkBuilder avsnitt. De används inte och bör inte finnas där. Om konfigurationen baseras på något av dessa exempel kan du även ha sådana avsnitt. De brukade ignoreras, men med BrainScript-uppdateringen har definitionen av ett nytt nätverk i dessa avsnitt nu en betydelse (modellredigering), så de ignoreras inte längre och bör därför tas bort.

NDLNetworkBuilder referens (inaktuell)

Syntaxen för den inaktuella NDLNetworkBuilder är:

NDLNetworkBuilder = [
    networkDescription = "yourNetwork.ndl"
]

Blocket NDLNetworkBuilder har följande parametrar:

  • networkDescription: sökvägen till nätverksbeskrivningsfilen. Med det inaktuella NDLNetworkBuildervar det vanligt att använda filnamnstillägget .ndl. Om det inte finns någon networkDescription angiven parameter antas nätverksbeskrivningen vara inlindad i samma NDLNetworkBuilder underblock, som anges med parametern run nedan. Observera att endast en filsökväg kan anges via parametern networkDescription . Om du vill läsa in flera filer med makron använder du parametern ndlMacros .

  • run: blocket för den NDL som ska köras. Om en extern NDL-fil anges via parametern networkDescription identifierar parametern run ett block i filen. Den här parametern åsidosätter alla run parametrar som redan finns i filen. Om ingen networkDescription fil anges identifierar parametern run ett block i den aktuella konfigurationsfilen.

  • load: blocken i NDL-skript som ska läsas in. Flera block kan anges via en ":" avgränsad lista. De block som anges av parametern load innehåller vanligtvis makron som ska användas av run blocket. Precis som parametern run identifierar parametern load block i en extern NDL-fil och åsidosätter alla load parametrar som redan finns i filen, om en fil anges av parametern networkDescription . Om ingen networkDescription fil anges load identifierar du ett block i den aktuella konfigurationsfilen.

  • ndlMacros: filsökvägen där NDL-makron kan läsas in. Den här parametern används vanligtvis för att läsa in en standarduppsättning NDL-makron som kan användas av alla NDL-skript. Flera NDL-filer, som var och en anger olika uppsättningar makron, kan läsas in genom att ange en "+" avgränsad lista över filsökvägar för den här ndlMacros parametern. Om du vill dela makron med andra kommandoblock, till exempel NDL:s MEL-block (Model Editing Language), bör du definiera det på rotnivå i konfigurationsfilen.

  • randomSeedOffset: ett icke-negativt förskjutningsvärde för slumpmässigt startvärde vid initiering av de inlärbara parametrarna. Standardvärdet är 0. På så sätt kan användare köra experiment med olika slumpmässig initiering.