Concetti di base di BrainScript

BrainScript--A Walk-Through

Questa sezione introduce i concetti di base del linguaggio "BrainScript". Una nuova lingua? Non preoccuparti & leggere, è molto dritto.

In CNTK le reti personalizzate vengono definite usando e BrainScriptNetworkBuilder descritte nel linguaggio di descrizione della rete CNTK "BrainScript". Analogamente, le descrizioni delle reti sono chiamate script del cervello.

BrainScript offre un modo semplice per definire una rete in modo simile al codice, usando espressioni, variabili, funzioni primitive e auto-definite, blocchi annidati e altri concetti ben definiti. È simile a un linguaggio di scripting nella sintassi.

OK, si ottengono i piedi bagnati con un esempio di BrainScript completo!

Definizione di rete di esempio di BrainScript completa

Nell'esempio seguente viene illustrata la descrizione di rete di una semplice rete neurale con un livello nascosto e un livello di classificazione. Verranno illustrati i concetti relativi a questo esempio. Prima di andare avanti, forse trascorrere alcuni minuti con l'esempio e provare a indovinare cosa significa. Potresti trovare, come hai letto, che hai indovinato la maggior parte di esso correttamente.

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

Nozioni di base sulla sintassi BrainScript

Prima di scavare a destra, alcune note generali sulla sintassi di BrainScript.

BrainScript usa una sintassi semplice che consente di esprimere le reti neurali in modo che sembrino formule matematiche. Di conseguenza, l'unità sintattica fondamentale è l'assegnazione, usata sia nelle assegnazioni di variabili che nelle definizioni delle funzioni. Ad esempio:

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

Righe, Commenti, Includi

Anche se un'assegnazione viene in genere scritta in una singola riga, le espressioni possono estendersi su più righe. Per inserire più assegnazioni su una singola riga, tuttavia, è necessario separarle in base a un punto e virgola. Ad esempio:

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

Oltre a richiedere un punto e virgola tra assegnazioni in assenza di un'interruzione di riga, BrainScript non è sensibile allo spazio vuoto.

BrainScript comprende i commenti line-end usando sia lo stile Python che lo stile #//C++. I commenti inline usano la sintassi C (/* this is a comment*/), ma a differenza di C, questi potrebbero non estendersi su più righe.

Per BrainScript incorporato nei file di configurazione CNTK (anziché in BrainScript letti da un file separato tramite una include direttiva), a causa di un'interazione con il parser di configurazione, esiste la restrizione aggiuntiva (un po' strana) che eventuali parentesi graffe, parentesi graffe o parentesi devono essere bilanciate all'interno di commenti e valori letterali stringa C/C++. Di conseguenza, nessun sorriso nei commenti in stile C/C++!

Una include "PATH" direttiva può essere usata in qualsiasi luogo per inserire il contenuto di un file al punto dell'istruzione. In questo caso, PATH può essere un percorso assoluto o relativo relativo (con o senza sottodirectory). Se si tratta di un percorso relativo, le posizioni seguenti vengono eseguite in ordine: directory di lavoro corrente; directory contenenti file esterni, se presenti; directory contenenti i file di configurazione; e infine la directory contenente l'eseguibile CNTK. Tutte le funzioni BrainScript predefinite sono incluse in questo modo da un file denominato CNTK.core.bs che si trova accanto all'eseguibile CNTK.

Espressioni

È quindi necessario conoscere le espressioni BrainScript, ovvero le formule che descrivono la rete. Le espressioni BrainScript sono scritte matematicamente in una sintassi simile ai linguaggi di programmazione più diffusi. Le espressioni più semplici sono valori letterali, ad esempio numeri e stringhe. Un esempio matematico è W1 * r + b, dove * fa riferimento a un prodotto scalare, matrice o tensore a seconda del tipo delle variabili. Un altro tipo comune di espressione è la chiamata alla funzione, ad esempio RectifiedLinear (.).

BrainScript è un linguaggio tipizzato dinamicamente. Un tipo di espressione importante è il record, definito usando la sintassi e accessibile tramite la {...} sintassi del punto. Ad esempio, r = { x = 13 ; y = 42 } assegna un record con due membri a r, in cui è possibile accedere al primo membro come r.x.

Oltre ai consueti operatori matematici, BrainScript ha un'espressione condizionale (if c then t else f), un'espressione di matrice e semplici lambda. Infine, per interfacciarsi con il codice CNTK C++, BrainScript può creare direttamente un'istanza di un set limitato di oggetti C++ predefiniti, principalmente le ComputationNode reti di calcolo composte da. Per altre informazioni, vedere Espressioni.

Le espressioni BrainScript vengono valutate per la prima volta. Lo scopo principale di BrainScript è descrivere la rete, quindi il valore di un'espressione spesso non è un valore finale, ma piuttosto un nodo in un grafico di calcolo per il calcolo posticipato (come in W1 * r + b). Solo le espressioni BrainScript dei scalari (ad esempio 28*28) sono "calcolate" al momento dell'analisi di BrainScript. Le espressioni che non vengono mai usate (ad esempio a causa di una condizione) non vengono mai valutate.

Nota: la versione ora deprecata NDLNetworkBuilder supporta solo la sintassi della chiamata alla funzione. Ad esempio, è necessario scrivere Plus (Times (W1, r), b1).

Variabili

Le variabili possono contenere il valore di qualsiasi espressione BrainScript (numero, stringa, record, matrice, lambda, CNTK C++) e vengono sostituiti quando usati in un'espressione. Le variabili non sono modificabili, ad esempio assegnate una sola volta. Ad esempio, la definizione di rete precedente inizia con:

SDim = 28*28  
HDim = 256
LDim = 10

In questo caso le variabili sono impostate su valori numerici scalari usati come parametri nelle espressioni successive. Questi valori sono le dimensioni degli esempi di dati, dei livelli nascosti e delle etichette usati nel training. Questa particolare configurazione di esempio è per il set di dati MNIST, che è una raccolta di [28 x 28]immagini -pixel. Ogni immagine è una cifra scritta a mano (0-9), quindi sono disponibili 10 etichette possibili che possono essere applicate a ogni immagine. La dimensione HDim di attivazione nascosta è una scelta utente.

La maggior parte delle variabili sono membri dei record (il blocco BrainScript esterno è implicitamente un record). Inoltre, le variabili possono essere argomenti di funzione o archiviati come elementi di matrici.

OK, pronto per passare attraverso la definizione del modello.

Definizione della rete

Una rete è principalmente descritta dalla formula del modo in cui gli output della rete vengono calcolati dagli input. Si chiama questa funzione modello, spesso definita come funzione effettiva in BrainScript. Nell'ambito della funzione modello, l'utente deve dichiarare i parametri del modello. Infine, è necessario definire gli input e i criteri/output della rete. Tutti questi sono definiti come variabili. Le variabili di input e criteri/output devono quindi essere comunicate al sistema.

Funzione del modello di rete e parametri del modello

La funzione modello contiene le formule di rete effettive e i rispettivi parametri del modello. L'esempio usa il prodotto matrice e l'aggiunta e la funzione "primitive" (predefinita) per la funzione energetica , quindi il core della funzione RectifiedLinear()di rete è costituito da queste equazioni:

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

I parametri del modello sono matrici, vettori di pregiudizio o qualsiasi altro tensore che costituisce il modello appreso al termine del training. I tensori dei parametri modello vengono usati per trasformare i dati di esempio di input nell'output desiderato e vengono aggiornati dal processo di apprendimento. La rete di esempio precedente contiene i parametri matrice seguenti:

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

In questo caso, W0 è la matrice di peso ed b0 è il vettore di distorsione. ParameterTensor{} indica una primitiva CNTK speciale, che crea un'istanza di un tensore vettore, matrice o tensore arbitrario e accetta i parametri delle dimensioni come matrice BrainScript (numeri concatenati da due punti :). La dimensione di un vettore è un singolo numero, mentre deve essere specificata una dimensione matrice come (numRows:numCols). Per impostazione predefinita, i parametri vengono inizializzati con numeri casuali uniformi quando si crea un'istanza diretta e heNormal quando vengono usati tramite livelli, ma esistono altre opzioni (vedere qui) per l'elenco completo. A differenza delle funzioni regolari, ParameterTensor{} accetta i relativi argomenti in parentesi graffe anziché parentesi graffe. Le parentesi graffe sono la convenzione BrainScript per le funzioni che creano parametri o oggetti, anziché le funzioni.

Questo viene quindi sottoposto a wrapping in una funzione BrainScript. Le funzioni BrainScript vengono dichiarate nel formato f(x) = an expression of x. Ad esempio, Sqr (x) = x * x è una dichiarazione di funzione BrainScript valida. Non poteva essere molto più semplice e diretto, vero?

Ora, la funzione effettiva del modello dell'esempio precedente è un po'più complessa:

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

L'esterno { ... } e quella finale .z merita una spiegazione. I curli { ... } esterni e il loro contenuto definiscono effettivamente un record con 6 membri record (W0, b0rW1b1, e ).z Tuttavia, il valore della funzione del modello è solo z. Tutti gli altri sono interni alla funzione. Di conseguenza, viene usato .z per selezionare il membro del record da restituire. Questa è solo la sintassi punto per accedere ai membri del record. In questo modo, gli altri membri del record non sono accessibili dall'esterno. Ma continuano a esistere come parte dell'espressione per calcolare z. Il { ... ; x = ... }.x modello è un modo per usare le variabili locali.

Si noti che la sintassi del record non è necessaria. In alternativa, model(features) potrebbe anche essere stato dichiarato senza la deviazione tramite il record, come singola espressione:

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

Questo è molto più difficile da leggere e, più importante, non consente di usare lo stesso parametro in più posizioni nella formula.

Input

Gli input nella rete sono definiti dai dati di esempio e dalle etichette associate agli esempi:

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

Input{} è la seconda primitiva CNTK speciale necessaria per la definizione del modello (la prima è Parameter{}). Crea una variabile che riceve l'input dall'esterno della rete: dal lettore. L'argomento di Input{} è la dimensione dei dati. In questo esempio l'input features avrà le dimensioni dei dati di esempio ,definiti in variabile SDim, e l'input labels avrà le dimensioni delle etichette. I nomi delle variabili degli input devono corrispondere alle voci corrispondenti nella definizione di lettore.

Criteri di training e output di rete

È comunque necessario dichiarare come l'output della rete interagisce con il mondo. La funzione modello calcola i valori logit (probabilità di log non normalizzati). Questi valori logit possono essere usati per

  • definire il criterio di training,
  • misura accuratezza e
  • calcolare la probabilità rispetto alle classi di output fornite da un input, per basare una decisione di classificazione su (si noti che il posterior z di log nonmalizzato può spesso essere usato per la classificazione diretta).

La rete di esempio usa etichette di categoria, rappresentate come vettori a caldo. Per l'esempio MNIST, questi verranno visualizzati come matrice di 10 valori a virgola mobile, tutti zero tranne per la categoria di etichette appropriata che è 1,0. Le attività di classificazione come le nostre usano comunemente la SoftMax() funzione per ottenere le probabilità per ogni etichetta. La rete viene quindi ottimizzata per ottimizzare la probabilità di log della classe corretta (tra entropia) e ridurre al minimo quella di tutte le altre classi. Questo è il criterio di training o la funzione di perdita. In CNTK queste due azioni sono in genere combinate in una funzione per l'efficienza:

ce = CrossEntropyWithSoftmax (labels, z)

CrossEntropyWithSoftmax() la funzione accetta l'input, calcola la SoftMax() funzione, calcola l'errore dal valore effettivo usando l'entropia incrociata e tale segnale di errore viene usato per aggiornare i parametri nella rete tramite la propagazione indietro. Di conseguenza, nell'esempio precedente, il valore normalizzato Softmax() , calcolato come P, non viene usato durante il training. Tuttavia, sarà necessario usare la rete (anche in molti casi, z è spesso sufficiente per la classificazione; in questo caso, z se stesso sarà l'output).

CNTK usa La discesa sfumatura stochastica (SGD) come algoritmo di apprendimento. SGD deve calcolare la sfumatura della funzione obiettivo rispetto a tutti i parametri del modello. Importante, CNTK non richiede agli utenti di specificare tali sfumature. Invece, ogni funzione predefinita in CNTK ha anche una funzione controparte derivata e il sistema esegue automaticamente l'aggiornamento della propagazione indietro dei parametri di rete. Non è visibile all'utente. Gli utenti non devono mai essere interessati alle sfumature. Mai.

Oltre al criterio di training, i tassi di errore stimati vengono spesso calcolati durante la fase di training per convalidare il miglioramento del sistema perché il training sta andando ulteriormente. Questa operazione viene gestita in CNTK usando la funzione seguente:

errs = ClassificationError (labels, z)

Le probabilità prodotte dalla rete vengono confrontate con l'etichetta effettiva e viene calcolata la frequenza di errore. Questa operazione viene in genere visualizzata dal sistema. Anche se questo è utile, non è obbligatorio usare ClassificationError().

Comunicazione di input, output e criteri al sistema

Dopo aver definito tutte le variabili, è necessario indicare al sistema quale delle variabili deve trattare come input, output e criteri. Questa operazione viene eseguita definendo 5 variabili speciali che devono avere esattamente questi nomi:

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

I valori sono matrici, dove i valori devono essere separati da due punti (i due punti : sono un operatore BrainScript che forma una matrice concatenando due valori o matrici). Questa operazione viene illustrata in precedenza per outputNodes, che dichiara sia zP come output.

Nota: l'oggetto deprecato NDLNetworkBuilder ha richiesto che gli elementi della matrice siano separati da virgole.

Riepilogo dei nomi speciali

Come abbiamo visto sopra, ci sono 7 nomi speciali che dobbiamo essere consapevoli di, che trasportano proprietà "magic" :

  • ParameterTensor{}: dichiara e inizializza un parametro learnable.
  • Input{}: dichiara una variabile che ottiene la connessione e l'alimentazione da un lettore dati.
  • featureNodes, labelNodescriterionNodes, , evaluationNodese outputNodes: dichiara al sistema quale delle variabili usare come input, output e criteri.

Inoltre, sono disponibili 3 funzioni speciali con "magic" predefinite in CNTK, illustrate altrove:

  • Constant(): dichiara una costante.
  • PastValue() e FutureValue(): accedere a una variabile in una funzione di rete in un passaggio temporale diverso, per formare cicli ricorrenti.

Qualsiasi altro nome predefinito è una funzione primitiva predefinita, ad esempio o Convolution() con un'implementazione C++, una funzione di libreria predefinita realizzata in BrainScriptBS.RNNs.LSTMP(), ad Sigmoid() esempio , o un record che funge da spazio dei nomi per le funzioni della libreria , ad esempio BS.RNNs. Per un elenco completo, vedere BrainScript-Full-Function-Reference .

Successiva: Espressioni BrainScript