Generatore di reti BrainScript

Le reti personalizzate sono descritte nel linguaggio di descrizione della rete personalizzato di CNTK "BrainScript". Per definire una rete personalizzata, includere una sezione denominata BrainScriptNetworkBuilder nella configurazione di training. La descrizione dettagliata del linguaggio di descrizione di rete è disponibile nella pagina Concetti di base e nelle pagine secondarie corrispondenti.

Esistono due forme di utilizzo del generatore di rete BrainScript, una che usa parentesi (...)e una forma a mano breve usando parentesi {...}graffe. Per descrivere la rete in un file esterno, specificare un blocco simile al seguente:

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

dove yourNetwork.bs contiene la rete descritta usando BrainScript. Il file yourNetwork.bs viene cercato per primo nella stessa directory del file di configurazione e, se non viene trovato, nella directory dell'eseguibile CNTK. I nomi dei percorsi assoluti e relativi sono accettati qui. Ad esempio, bs/yourNetwork.bs significa un file che si trova in una directory bs accanto al file di configurazione (o in alternativa una directory bs all'interno della directory eseguibile CNTK).

Nota: fino a CNTK 1.6, BrainScript usa parentesi [...] quadre anziché parentesi graffe {...}. Le parentesi quadre sono ancora accettate ma deprecate.

In alternativa, è possibile definire la rete inline, direttamente all'interno del file di configurazione. In questo modo è possibile semplificare la configurazione se non si prevede di condividere lo stesso script cerebrale tra più configurazioni. Usare questo modulo:

BrainScriptNetworkBuilder = {
    # insert network description here
}

Quindi, cosa sembra che il codice BrainScript si inserisca tra parentesi quadre? Per scoprire, passare direttamente a BrainScript Basic Concepts (Concetti di base di BrainScript).

Oppure resta in questa pagina e leggi alcuni dettagli meno frequentemente necessari per te.

Il {...} modulo precedente è in realtà solo una breve mano per questo:

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

Infine, come uso avanzato, il (...) modulo non è limitato all'uso newdi . Invece, qualsiasi espressione BrainScript che restituisce un oggetto di ComputationNetwork è consentita all'interno delle parentesi. Ad esempio:

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

Si tratta di un uso avanzato che talvolta si verifica anche nel contesto della modifica del modello.

Avanti: Concetti di base di BrainScript.

Eredità NDLNetworkBuilder

Nelle versioni precedenti di CNTK, il generatore di rete è stato chiamato NDLNetworkBuilder. Il linguaggio di definizione è un subset di BrainScript. Il vecchio parser era meno capace, ma anche più forgiving. Ci sono anche altre piccole differenze.

NDLNetworkBuilder è ora deprecato, ma a causa della somiglianza, non è difficile eseguire l'aggiornamento a BrainScriptNetworkBuilder. Di seguito è riportata una guida su come convertire NDLNetworkBuilder le descrizioni di rete in BrainScriptNetworkBuilder's.

Aggiornamento da NDLNetworkBuilder a BrainScriptNetworkBuilder

La conversione di una definizione di rete esistente per in NDLNetworkBuilderBrainScriptNetworkBuilder è semplice nella maggior parte dei casi. Le modifiche principali sono la sintassi circostante. La descrizione della rete principale è in gran parte compatibile verso l'alto e probabilmente identica o quasi identica se non si sfruttano le nuove funzionalità del linguaggio.

Per convertire le descrizioni, è necessario cambiare il generatore di rete, adattare la sintassi esterna w.r.t ed eventualmente apportare adattamenti secondari al codice di rete stesso.

Passaggio 1. Cambio del generatore di rete. Sostituire con NDLNetworkBuilder il blocco corrispondente BrainScriptNetworkBuilder nel file di configurazione CNTK. Se la descrizione di rete si trova in un file separato:

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

La modifica dell'estensione del nome file non è strettamente necessaria, ma consigliata.

Se la descrizione di rete si trova nel .cntk file di configurazione stesso:

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

Passaggio 2. Rimuovere load e run bloccare. Con BrainScriptNetworkBuilder, le definizioni di macro/funzione e il codice principale vengono combinati. I load blocchi e run devono essere semplicemente rimossi. Ad esempio:

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

diventa semplicemente:

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

È possibile che sia stata usata la run variabile per selezionare una delle più configurazioni con una variabile esterna, ad esempio:

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

Questo modello era per lo più necessario perché NDL non disponeva di espressioni condizionali. In BrainScript, questo sarebbe stato scritto con un'espressione if :

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

Tuttavia, spesso, i modelli selezionati sono molto simili, quindi un modo migliore sarebbe unire le relative descrizioni e usare invece le condizionali all'interno solo per dove differiscono. Ecco un esempio in cui viene usato un parametro per scegliere tra un LSTM unidirezionale e un LSTM bidirezionale:

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

Passaggio 3. Modificare la descrizione della rete. Per quanto riguarda la descrizione di rete (formule), BrainScript è in gran parte compatibile con NDL. Ecco le principali differenze:

  • Il valore restituito delle macro (funzioni) non è più l'ultima variabile definita, ma l'intero set di variabili. È necessario selezionare in modo esplicito il valore di output alla fine. Ad esempio:

      # 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
    

    Senza questa modifica, il valore restituito dalla funzione sarà l'intero record e l'errore tipico che si otterrà è che è stato previsto dove ComputationNode è stato trovato un oggetto ComputationNetwork .

  • BrainScript non consente funzioni con numeri variabili di parametri. Questo aspetto è importante principalmente per la Parameter() funzione: un parametro vector non può più essere scritto come Parameter(N), ora deve essere scritto in modo esplicito come tensore ParameterTensor{N} o una matrice Parameter(N, 1)a 1 colonna . Senza questa modifica, verrà visualizzato un errore relativo alla mancata corrispondenza del numero di parametri posizionali. Questa notazione funziona anche con NDL, quindi è possibile apportare questa modifica per prima e testarla con NDL prima della conversione. Si tratta anche di una buona opportunità per rinominare qualsiasi uso del nome LearnableParameter() legacy in ParameterTensor{}.

    È importante anche per la RowStack() funzione , che in BrainScript accetta un singolo parametro che è una matrice di input. Gli input devono essere separati da due punti (:) anziché da una virgola, ad esempio RowStack (a:b:c) anziché RowStack (a, b, c)da .

  • Alcune impostazioni predefinite sono state aggiornate, principalmente il parametro facoltativo imageLayout di Convolution(), le operazioni di pooling e ImageInput(). Per NDL, queste impostazioni predefinite sono , mentre ora è necessario che l'impostazione predefinita legacycudnn sia compatibile con le primitive di convoluzione cuDNN. Tutti gli esempi di codice NDL specificano in modo esplicito questo parametro come cudnn già.

  • Il parser di BrainScript è più restrittivo:

    • Gli identificatori fanno ora distinzione tra maiuscole e minuscole. Le funzioni predefinite usano PascalCase (ad esempio RectifiedLinear()) e le variabili predefinite e i nomi dei parametri usano camelCase (ad esempio modelPath, criterionNodes), come le stringhe di opzione (init="fixedValue", tag="criterion"). Si noti che per i nomi dei parametri facoltativi, le ortografie non corrette non vengono sempre rilevate come errore. Alcuni parametri facoltativi digitati in modo non corretto vengono invece ignorati. Un esempio sono le definizioni "nodi speciali". L'ortografia corretta per quelle è ora:

        featureNodes    = ...
        labelNodes      = ...
        criterionNodes  = ...
        evaluationNodes = ...
        outputNodes     = ...
      
    • I nomi alternativi abbreviati non sono più consentiti, ad esempio Const() dovrebbe essere tag="evaluation"Constant(), tag="eval" e evalNodes ora evaluationNodesè .

    • Alcuni nomi digitati in modo non corretto sono stati corretti: criteria è ora criterion (analogamente criterionNodes), defaultHiddenActivity è ora defaultHiddenActivation.

    • Il = segno non è più facoltativo per le definizioni di funzione.

    • Anche se è consentito usare parentesi quadre per i blocchi ([ ... ]), è deprecato. Usare parentesi graffe ({ ... }).

    • Le etichette di opzione devono essere racchiuse tra virgolette come stringhe, ad esempio init="uniform" anziché init=uniform. Senza le virgolette, BrainScript avrà esito negativo con un messaggio di errore che indica che il simbolo uniform è sconosciuto.

    • Le primitive BrainScript che creano parametri (Input{} e ParameterTensor{}) devono usare le parentesi graffe per i relativi argomenti ( ad esempio f = Input{42}). Si tratta di una convenzione che non viene applicata ma consigliata in futuro.

    Questa sintassi più limitata è ancora accettata da NDLNetworkBuilder, quindi è consigliabile prima apportare queste modifiche sintattiche e testarle con NDL, prima di passare effettivamente a BrainScript.

Passaggio 4. Rimuovere NDLNetworkBuilder da sezioni "write" e "test". Esaminare le sezioni "write" e "test" per NDLNetworkBuilder le sezioni e rimuoverle. Alcuni dei nostri esempi NDL azionari hanno sezioni estranee NDLNetworkBuilder . Non vengono usati e non devono essere presenti. Se la configurazione è basata su uno di questi esempi, potrebbero essere presenti anche sezioni di questo tipo. Essi erano stati ignorati, ma con l'aggiornamento BrainScript, definendo una nuova rete in queste sezioni ora ha un significato (modifica del modello), quindi non vengono più ignorati e pertanto devono essere rimossi.

NDLNetworkBuilder riferimento (deprecato)

La sintassi dell'oggetto deprecato NDLNetworkBuilder è:

NDLNetworkBuilder = [
    networkDescription = "yourNetwork.ndl"
]

Il NDLNetworkBuilder blocco ha i parametri seguenti:

  • networkDescription: percorso del file di descrizione di rete. Con l'oggetto deprecato NDLNetworkBuilder, è stato consuetudine usare l'estensione .ndldi file . Se non networkDescription è specificato alcun parametro, si presuppone che la descrizione della rete venga inlinedata nello stesso NDLNetworkBuilder blocco secondario, specificata con il run parametro seguente. Si noti che è possibile specificare un solo percorso file tramite il networkDescription parametro . Per caricare più file di macro, usare il ndlMacros parametro .

  • run: blocco del file NDL che verrà eseguito. Se viene specificato un file NDL esterno tramite il networkDescription parametro, il run parametro identifica un blocco nel file. Questo parametro esegue l'override di tutti run i parametri che potrebbero già esistere nel file. Se non viene specificato alcun networkDescription file, il run parametro identifica un blocco nel file di configurazione corrente.

  • load: blocchi di script NDL da caricare. È possibile specificare più blocchi tramite un elenco separato ":". I blocchi specificati dal load parametro in genere contengono macro da usare dal run blocco. Analogamente al run parametro, il load parametro identifica i blocchi in un file NDL esterno ed esegue l'override networkDescription di tutti load i parametri che potrebbero già esistere nel file, se un file viene specificato dal parametro . Se non networkDescription viene specificato alcun file, load identifica un blocco nel file di configurazione corrente.

  • ndlMacros: percorso del file in cui possono essere caricate le macro NDL. Questo parametro viene in genere usato per caricare un set predefinito di macro NDL che possono essere usate da tutti gli script NDL. È possibile caricare più file NDL, ognuno dei quali specifica diversi set di macro, specificando un elenco separato "+" di percorsi file per questo ndlMacros parametro. Per condividere macro con altri blocchi di comando, ad esempio i blocchi MEL (Model Editing Language) di NDL, è necessario definirlo a livello radice del file di configurazione.

  • randomSeedOffset: valore di offset di inizializzazione casuale non negativo per inizializzare i parametri appresi. Il valore predefinito è 0. Ciò consente agli utenti di eseguire esperimenti con inizializzazione casuale diversa.