Delen via


BrainScript Network Builder

Aangepaste netwerken worden beschreven in de aangepaste netwerkbeschrijvingstaal 'BrainScript' van CNTK. Als u een aangepast netwerk wilt definiëren, neemt u een sectie met de naam BrainScriptNetworkBuilder op in uw trainingsconfiguratie. Een gedetailleerde beschrijving van de netwerkbeschrijvingstaal vindt u op de pagina Basisconcepten en de bijbehorende subpagina's.

Er zijn twee vormen van het gebruik van de BrainScript-netwerkbouwer, een met haakjes (...)en een korte vorm met accolades {...}. Als u uw netwerk in een extern bestand wilt beschrijven, geeft u een blok op dat er ongeveer als volgt uit ziet:

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

where yourNetwork.bs bevat het netwerk dat wordt beschreven met behulp van BrainScript. Het bestand yourNetwork.bs wordt eerst gezocht in dezelfde map als het configuratiebestand en als dit niet wordt gevonden, in de map van het uitvoerbare CNTK-bestand. Zowel absolute als relatieve padnamen worden hier geaccepteerd. Betekent bijvoorbeeld bs/yourNetwork.bs een bestand dat zich bevindt in een map bs naast uw configuratiebestand (of een map bs in de uitvoerbare CNTK-map).

Opmerking: Tot CNTK 1.6 gebruikte BrainScript haken in plaats van accolades [...]{...}. Vierkante haken worden nog steeds geaccepteerd, maar afgeschaft.

U kunt uw netwerk ook inline definiëren, rechtstreeks in het configuratiebestand. Dit kan de configuratie vereenvoudigen als u niet van plan bent om hetzelfde hersenscript te delen in meerdere configuraties. Gebruik dit formulier:

BrainScriptNetworkBuilder = {
    # insert network description here
}

Dus hoe ziet de BrainScript-code eruit die tussen de haakjes staat? Ga direct naar Basisconcepten van BrainScript om erachter te komen.

Of blijf op deze pagina en lees verder over enkele minder vaak benodigde details voor u.

Het {...} bovenstaande formulier is eigenlijk slechts een korte hand hiervoor:

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

Ten slotte is het (...) formulier, als geavanceerd gebruik, niet beperkt tot het gebruik newvan . In plaats daarvan is elke BrainScript-expressie die resulteert in een object van ComputationNetwork , toegestaan binnen de haakjes. Bijvoorbeeld:

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

Dit is een geavanceerd gebruik dat soms ook voorkomt in de context van modelbewerking.

Volgende: Basisconcepten van BrainScript.

Legacy NDLNetworkBuilder

In oudere versies van CNTK heette NDLNetworkBuilderde netwerkbouwer . De definitietaal is een subset van BrainScript. De oude parser was minder capabel, maar ook meer vergevingsgezind. Er zijn ook andere kleine verschillen.

NDLNetworkBuilder is nu afgeschaft, maar vanwege de gelijkenis is het niet moeilijk om een upgrade uit te voeren naar BrainScriptNetworkBuilder. Hier volgt een handleiding voor het converteren NDLNetworkBuilder van netwerkbeschrijvingen naar BrainScriptNetworkBuilder's.

Bijwerken van NDLNetworkBuilder tot BrainScriptNetworkBuilder

Het converteren van een bestaande netwerkdefinitie voor de naar BrainScriptNetworkBuilder is in de NDLNetworkBuilder meeste gevallen eenvoudig. De belangrijkste wijzigingen zijn de omringende syntaxis. De beschrijving van het kernnetwerk zelf is grotendeels compatibel en waarschijnlijk identiek of bijna identiek als u geen gebruik maakt van de nieuwe taalfuncties.

Als u uw beschrijvingen wilt converteren, moet u overschakelen naar de opbouwfunctie van het netwerk, de buitenste syntaxis aanpassen en eventueel kleine aanpassingen aan uw netwerkcode zelf aanbrengen.

Stap 1. Schakelen tussen de netwerkbouwer. Vervang de NDLNetworkBuilder door het bijbehorende BrainScriptNetworkBuilder blok in het CNTK-configuratiebestand. Als uw netwerkbeschrijving zich in een afzonderlijk bestand bevindt:

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

(De wijziging van de bestandsnaamextensie is niet strikt noodzakelijk, maar wordt aanbevolen.)

Als uw netwerkbeschrijving zich in het .cntk configuratiebestand zelf bevindt:

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

Stap 2. Verwijderen load en run blokkeren. Met BrainScriptNetworkBuilderworden macro-/functiedefinities en hoofdcode gecombineerd. De load blokken en run moeten gewoon worden verwijderd. Bijvoorbeeld:

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

wordt eenvoudigweg:

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

Mogelijk hebt u de run variabele gebruikt om een van meerdere configuraties met een externe variabele te selecteren, bijvoorbeeld:

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

Dit patroon was vooral nodig omdat NDL geen voorwaardelijke expressies had. In BrainScript wordt dit nu geschreven met een if expressie:

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

Vaak lijken de geselecteerde modellen echter erg op elkaar, dus een betere manier is om hun beschrijvingen samen te voegen en in plaats daarvan alleen voorwaarden binnen te gebruiken voor waar ze verschillen. Hier volgt een voorbeeld waarin een parameter wordt gebruikt om te kiezen tussen een éénrichtings- en een bidirectionele LSTM:

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

Stap 3. De netwerkbeschrijving aanpassen. Met betrekking tot de netwerkbeschrijving (formules) zelf is BrainScript grotendeels compatibel met NDL. Dit zijn de belangrijkste verschillen:

  • De retourwaarde van macro's (functies) is niet langer de laatste variabele die erin is gedefinieerd, maar de volledige set variabelen. U moet de uitvoerwaarde aan het einde expliciet selecteren. Bijvoorbeeld:

      # 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
    

    Zonder deze wijziging zou de retourwaarde van de functie de hele record zijn en de typische fout die u krijgt, is dat een ComputationNode werd verwacht waar een ComputationNetwork was gevonden.

  • BrainScript staat geen functies met variabele aantallen parameters toe. Dit is voornamelijk van belang voor de Parameter() functie: een vectorparameter kan niet meer worden geschreven als Parameter(N), nu moet deze expliciet worden geschreven als een tensor ParameterTensor{N} of een matrix Parameter(N, 1)met één kolom . Zonder deze wijziging krijgt u een foutmelding over het niet overeenkomende aantal positionele parameters. Deze notatie werkt ook met NDL, dus u kunt deze wijziging eerst aanbrengen en testen met NDL voordat u deze converteert. Dit is ook een goede gelegenheid om de naam van de verouderde naam LearnableParameter() te wijzigen in ParameterTensor{}.

    Het is ook van belang voor de RowStack() functie, die in BrainScript één parameter gebruikt die een matrix van invoer is. De invoer moet worden gescheiden door een dubbele punt (:) in plaats van een komma, bijvoorbeeld RowStack (a:b:c) in plaats van RowStack (a, b, c).

  • Sommige standaardinstellingen zijn bijgewerkt, voornamelijk de optionele imageLayout parameter van Convolution(), de poolbewerkingen en ImageInput(). Voor NDL zijn deze standaard ingesteld op legacy, terwijl nu de standaardwaarde is die compatibel moet cudnn zijn met de cuDNN-convolution-primitieven. (In alle NDL-codevoorbeelden wordt deze parameter expliciet opgegeven zoals cudnn al.)

  • De parser van BrainScript is meer beperkend:

    • Id's zijn nu hoofdlettergevoelig. Ingebouwde functies maken gebruik van PascalCase (bijvoorbeeld RectifiedLinear()), en ingebouwde variabelen en parameternamen gebruiken camelCase (bijvoorbeeld modelPath, criterionNodes), evenals optietekenreeksen (init="fixedValue", tag="criterion"). Houd er rekening mee dat voor namen van optionele parameters onjuiste spelling niet altijd als een fout wordt gedetecteerd. In plaats daarvan worden sommige onjuist gespelde optionele parameters gewoon genegeerd. Een voorbeeld zijn de definities van 'speciale knooppunten'. De juiste spelling voor deze is nu:

        featureNodes    = ...
        labelNodes      = ...
        criterionNodes  = ...
        evaluationNodes = ...
        outputNodes     = ...
      
    • Verkorte alternatieve namen zijn niet meer toegestaan, zoals Const() moeten zijn Constant(), tag="eval" moeten zijn tag="evaluation"en evalNodes is nu evaluationNodes.

    • Sommige verkeerd gespelde namen zijn gecorrigeerd: criteria is nu criterion (eveneens criterionNodes), defaultHiddenActivity is nu defaultHiddenActivation.

    • Het = teken is niet meer optioneel voor functiedefinities.

    • Hoewel het is toegestaan om vierkante haken te gebruiken voor blokken ([ ... ]), wordt het afgeschaft. Gebruik accolades ({ ... }).

    • Optielabels moeten worden geciteerd als tekenreeksen, bijvoorbeeld init="uniform" in plaats init=uniformvan . Zonder de aanhalingstekens mislukt BrainScript met een foutbericht dat het symbool uniform onbekend is.

    • De BrainScript-primitieven die parameters (Input{} en ParameterTensor{}) maken, moeten accolades gebruiken voor hun argumenten (bijvoorbeeld f = Input{42}). Dit is een conventie die niet wordt afgedwongen, maar in de toekomst wordt aanbevolen.

    Deze meer beperkte syntaxis wordt nog steeds geaccepteerd door NDLNetworkBuilder, dus we raden u aan om eerst deze syntactische wijzigingen aan te brengen en ze te testen met NDL, voordat u daadwerkelijk overgaat naar BrainScript.

Stap 4. Verwijder NDLNetworkBuilder uit de secties 'schrijven' en 'testen'. Controleer de secties 'schrijven' en 'testen' voor NDLNetworkBuilder secties en verwijder deze. Sommige van onze NDL-voorbeelden hebben overbodige NDLNetworkBuilder secties. Ze worden niet gebruikt en moeten er niet zijn. Als uw configuratie is gebaseerd op een van deze voorbeelden, hebt u mogelijk ook dergelijke secties. Ze waren vroeger genegeerd, maar met de BrainScript-update heeft het definiëren van een nieuw netwerk in deze secties nu een betekenis (model bewerken), zodat ze niet langer worden genegeerd en daarom moeten worden verwijderd.

NDLNetworkBuilder verwijzing (afgeschaft)

De syntaxis van de afgeschafte NDLNetworkBuilder is:

NDLNetworkBuilder = [
    networkDescription = "yourNetwork.ndl"
]

Het NDLNetworkBuilder blok heeft de volgende parameters:

  • networkDescription: het bestandspad van het netwerkbeschrijvingsbestand. Met de afgeschafte NDLNetworkBuilder, was het gebruikelijk om de bestandsextensie .ndlte gebruiken . Als er geen networkDescription parameter is opgegeven, wordt ervan uitgegaan dat de netwerkbeschrijving is inlined in hetzelfde NDLNetworkBuilder subblok, opgegeven met de run onderstaande parameter. Houd er rekening mee dat er slechts één bestandspad kan worden opgegeven via de networkDescription parameter . Als u meerdere bestanden met macro's wilt laden, gebruikt u de ndlMacros parameter .

  • run: het blok van de NDL die wordt uitgevoerd. Als een extern NDL-bestand wordt opgegeven via de networkDescription parameter, identificeert de run parameter een blok in dat bestand. Deze parameter overschrijft alle run parameters die mogelijk al aanwezig zijn in het bestand. Als er geen networkDescription bestand is opgegeven, identificeert de run parameter een blok in het huidige configuratiebestand.

  • load: de blokken met NDL-scripts die moeten worden geladen. U kunt meerdere blokken opgeven via een gescheiden lijst :. De blokken die zijn opgegeven door de load parameter bevatten meestal macro's voor gebruik door het run blok. Vergelijkbaar met de run parameter identificeert de load parameter blokken in een extern NDL-bestand en overschrijft alle load parameters die mogelijk al in het bestand bestaan, als een bestand is opgegeven door de networkDescription parameter. Als er geen networkDescription bestand is opgegeven, load identificeert u een blok in het huidige configuratiebestand.

  • ndlMacros: het bestandspad waar NDL-macro's kunnen worden geladen. Deze parameter wordt meestal gebruikt voor het laden van een standaardset NDL-macro's die door alle NDL-scripts kunnen worden gebruikt. Meerdere NDL-bestanden, elk met verschillende sets macro's, kunnen worden geladen door een door '+' gescheiden lijst met bestandspaden voor deze ndlMacros parameter op te geven. Als u macro's wilt delen met andere opdrachtblokken, zoals de MEL-blokken (Model Editing Language) van NDL, moet u deze definiëren op het hoofdniveau van het configuratiebestand.

  • randomSeedOffset: een niet-negatieve willekeurige seed offsetwaarde bij het initialiseren van de leerbare parameters. De standaardwaarde is 0. Hierdoor kunnen gebruikers experimenten uitvoeren met verschillende willekeurige initialisatie.