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 new
van . 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 NDLNetworkBuilder
de 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 BrainScriptNetworkBuilder
worden 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 eenComputationNetwork
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 alsParameter(N)
, nu moet deze expliciet worden geschreven als een tensorParameterTensor{N}
of een matrixParameter(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 naamLearnableParameter()
te wijzigen inParameterTensor{}
.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, bijvoorbeeldRowStack (a:b:c)
in plaats vanRowStack (a, b, c)
.Sommige standaardinstellingen zijn bijgewerkt, voornamelijk de optionele
imageLayout
parameter vanConvolution()
, de poolbewerkingen enImageInput()
. Voor NDL zijn deze standaard ingesteld oplegacy
, terwijl nu de standaardwaarde is die compatibel moetcudnn
zijn met de cuDNN-convolution-primitieven. (In alle NDL-codevoorbeelden wordt deze parameter expliciet opgegeven zoalscudnn
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 (bijvoorbeeldmodelPath
,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 zijnConstant()
,tag="eval"
moeten zijntag="evaluation"
enevalNodes
is nuevaluationNodes
.Sommige verkeerd gespelde namen zijn gecorrigeerd:
criteria
is nucriterion
(eveneenscriterionNodes
),defaultHiddenActivity
is nudefaultHiddenActivation
.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 plaatsinit=uniform
van . Zonder de aanhalingstekens mislukt BrainScript met een foutbericht dat het symbooluniform
onbekend is.De BrainScript-primitieven die parameters (
Input{}
enParameterTensor{}
) maken, moeten accolades gebruiken voor hun argumenten (bijvoorbeeldf = 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 afgeschafteNDLNetworkBuilder
, was het gebruikelijk om de bestandsextensie.ndl
te gebruiken . Als er geennetworkDescription
parameter is opgegeven, wordt ervan uitgegaan dat de netwerkbeschrijving is inlined in hetzelfdeNDLNetworkBuilder
subblok, opgegeven met derun
onderstaande parameter. Houd er rekening mee dat er slechts één bestandspad kan worden opgegeven via denetworkDescription
parameter . Als u meerdere bestanden met macro's wilt laden, gebruikt u dendlMacros
parameter .run
: het blok van de NDL die wordt uitgevoerd. Als een extern NDL-bestand wordt opgegeven via denetworkDescription
parameter, identificeert derun
parameter een blok in dat bestand. Deze parameter overschrijft allerun
parameters die mogelijk al aanwezig zijn in het bestand. Als er geennetworkDescription
bestand is opgegeven, identificeert derun
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 deload
parameter bevatten meestal macro's voor gebruik door hetrun
blok. Vergelijkbaar met derun
parameter identificeert deload
parameter blokken in een extern NDL-bestand en overschrijft alleload
parameters die mogelijk al in het bestand bestaan, als een bestand is opgegeven door denetworkDescription
parameter. Als er geennetworkDescription
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 dezendlMacros
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 is0
. Hierdoor kunnen gebruikers experimenten uitvoeren met verschillende willekeurige initialisatie.