Compartir a través de


BrainScript Network Builder

Las redes personalizadas se describen en el lenguaje de descripción de red personalizado de CNTK "BrainScript". Para definir una red personalizada, incluya una sección denominada BrainScriptNetworkBuilder en la configuración de entrenamiento. Puede encontrar una descripción detallada en el lenguaje de descripción de red en la página Conceptos básicos y en las subpáginas correspondientes.

Hay dos formas de usar el generador de red BrainScript, uno mediante paréntesis (...)y un formato de mano corta mediante llaves {...}. Para describir la red en un archivo externo, especifique un bloque similar al siguiente:

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

donde yourNetwork.bs contiene la red descrita mediante BrainScript. El archivo yourNetwork.bs se busca primero en el mismo directorio que el archivo de configuración y, si no se encuentra, en el directorio del ejecutable CNTK. Los nombres de ruta de acceso absolutos y relativos se aceptan aquí. Por ejemplo, bs/yourNetwork.bs significa un archivo ubicado en un directorio bs junto al archivo de configuración (o bien un directorio bs dentro del directorio ejecutable de CNTK).

Nota: Hasta CNTK 1.6, BrainScript usó corchetes [...] en lugar de llaves {...}. Todavía se aceptan corchetes, pero están en desuso.

Como alternativa, puede definir la red insertada, directamente dentro del archivo de configuración. Esto puede simplificar la configuración si no tiene previsto compartir el mismo script de cerebro en varias configuraciones. Use este formulario:

BrainScriptNetworkBuilder = {
    # insert network description here
}

Por lo tanto, ¿qué aspecto tiene el código BrainScript? Para averiguarlo, vaya directamente a Conceptos básicos de BrainScript.

O manténgase en esta página y lea sobre algunos detalles menos necesarios para usted.

El {...} formulario anterior es realmente sólo una mano corta para esto:

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

Por último, como uso avanzado, el (...) formulario no se limita a usar new. En su lugar, cualquier expresión BrainScript que se evalúe como un objeto de ComputationNetwork se permite dentro de los paréntesis. Por ejemplo:

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

Se trata de un uso avanzado que a veces también se produce en el contexto de la edición de modelos.

A continuación: Conceptos básicos de BrainScript.

Legado NDLNetworkBuilder

En versiones anteriores de CNTK, el generador de red se denominaba NDLNetworkBuilder. Su lenguaje de definición es un subconjunto de BrainScript. El antiguo analizador era menos capaz, pero también más perdonado. También hay otras pequeñas diferencias.

NDLNetworkBuilder ahora está en desuso, pero debido a la similitud, no es difícil actualizar a BrainScriptNetworkBuilder. A continuación se muestra una guía sobre cómo convertir NDLNetworkBuilder descripciones de red en BrainScriptNetworkBuilder"s".

Actualización de a NDLNetworkBuilderBrainScriptNetworkBuilder

La conversión de una definición de red existente para en NDLNetworkBuilderBrainScriptNetworkBuilder es sencilla en la mayoría de los casos. Los cambios principales son la sintaxis circundante. La propia descripción de red principal es compatible en gran medida hacia arriba y es probable que sea idéntica o casi idéntica si no aprovecha las nuevas características del lenguaje.

Para convertir las descripciones, debe cambiar el generador de red, adaptar la sintaxis externa w.r.t y, posiblemente, realizar pequeñas adaptaciones en el propio código de red.

Paso 1. Cambio del generador de red. Reemplace por NDLNetworkBuilder el bloque correspondiente BrainScriptNetworkBuilder en el archivo de configuración de CNTK. Si la descripción de la red está en un archivo independiente:

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

(El cambio de la extensión de nombre de archivo no es estrictamente necesario, pero se recomienda).

Si la descripción de la red está en el propio archivo de .cntk configuración:

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

Paso 2. Quite load los bloques y run . Con BrainScriptNetworkBuilder, se combinan las definiciones de macro/función y el código principal. Los load bloques y run simplemente deben quitarse. Por ejemplo, esto:

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

simplemente se convierte en:

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

Es posible que haya usado la run variable para seleccionar una de varias configuraciones con una variable externa, por ejemplo:

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

Este patrón era principalmente necesario porque NDL no tenía expresiones condicionales. En BrainScript, esto ahora se escribiría con una if expresión:

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

Sin embargo, a menudo, los modelos seleccionados son muy similares, por lo que una mejor manera sería combinar sus descripciones y, en su lugar, usar condicionales dentro solo para donde difieren. Este es un ejemplo en el que se usa un parámetro para elegir entre un LSTM unidireccional y bidireccional:

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

Paso 3. Ajuste de la descripción de la red. Con respecto a la propia descripción de la red (fórmulas), BrainScript es en gran medida compatible con NDL. Estas son las principales diferencias:

  • El valor devuelto de macros (funciones) ya no es la última variable definida en ellas, pero todo el conjunto de variables. Debe seleccionar explícitamente el valor de salida al final. Por ejemplo:

      # 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
    

    Sin este cambio, el valor devuelto de la función sería todo el registro y el error típico que obtendrá es que se esperaba un ComputationNode donde se encontró un ComputationNetwork .

  • BrainScript no permite funciones con números variables de parámetros. Esto importa principalmente para la Parameter() función: un parámetro vector ya no se puede escribir como Parameter(N), ahora tiene que escribirse explícitamente como tensor ParameterTensor{N} o una matriz Parameter(N, 1)de 1 columna . Sin este cambio, obtendrá un error sobre el número de parámetros posicionales que no coinciden. Esta notación también funciona con NDL, por lo que puede realizar este cambio primero y probarlo con NDL antes de la conversión. También es una buena oportunidad para cambiar el nombre de los usos del nombre LearnableParameter() heredado a ParameterTensor{}.

    También es importante para la RowStack() función , que en BrainScript toma un único parámetro que es una matriz de entradas. Las entradas deben estar separadas por dos puntos (:) en lugar de una coma, por ejemplo RowStack (a:b:c) , en lugar de RowStack (a, b, c).

  • Algunos valores predeterminados se han actualizado, principalmente el parámetro opcional imageLayout de Convolution(), las operaciones de agrupación y ImageInput(). En el caso de NDL, estos valores predeterminados son legacy, mientras que ahora el valor predeterminado es cudnn que es necesario que sea compatible con los primitivos de convolución cuDNN. (Todos los ejemplos de código NDL especifican explícitamente este parámetro como cudnn ya).

  • El analizador de BrainScript es más restrictivo:

    • Los identificadores distinguen mayúsculas de minúsculas. Las funciones integradas usan PascalCase (por ejemplo RectifiedLinear(), ) y las variables integradas y los nombres de parámetro usan camelCase (por ejemplo modelPath, , criterionNodes), al igual que las cadenas de opción (init="fixedValue", tag="criterion"). Tenga en cuenta que para los nombres de parámetros opcionales, las ortografías incorrectas no siempre se detectan como un error. En su lugar, algunos parámetros opcionales escritos incorrectamente simplemente se omiten. Un ejemplo son las definiciones de "nodos especiales". Su ortografía correcta para esos es ahora:

        featureNodes    = ...
        labelNodes      = ...
        criterionNodes  = ...
        evaluationNodes = ...
        outputNodes     = ...
      
    • Ya no se permiten nombres alternativos abreviados, como Const() debe ser Constant(), tag="eval" debe ser tag="evaluation"y evalNodes ahora evaluationNodeses .

    • Se corrigieron algunos nombres mal escritos: criteria es ahora criterion (del mismo modo criterionNodes), defaultHiddenActivity ahora defaultHiddenActivationes .

    • El = signo ya no es opcional para las definiciones de función.

    • Aunque se permite usar corchetes para bloques ([ ... ]), está en desuso. Use llaves ({ ... }).

    • Las etiquetas de opción se deben citar como cadenas, por ejemplo init="uniform" , en lugar de init=uniform. Sin las comillas, BrainScript producirá un mensaje de error que indica que el símbolo uniform es desconocido.

    • Los primitivos brainScript que crean parámetros (Input{} y ParameterTensor{}) deben usar llaves para sus argumentos (por ejemplo, f = Input{42}). Se trata de una convención que no se aplica, pero que se recomienda en el futuro.

    Esta sintaxis más restringida sigue siendo aceptada por NDLNetworkBuilder, por lo que se recomienda realizar primero estos cambios sintácticos y probarlos con NDL, antes de cambiar realmente a BrainScript.

Paso 4. Quite NDLNetworkBuilder de las secciones "write" y "test". Revise las secciones de "escritura" y "prueba" para NDLNetworkBuilder las secciones y quítelas. Algunos de nuestros ejemplos de NDL de stock tienen secciones extrañas NDLNetworkBuilder . No se usan y no deben estar allí. Si la configuración se basa en uno de estos ejemplos, también puede tener tales secciones. Solían omitirse, pero con la actualización de BrainScript, definir una nueva red en estas secciones ahora tiene un significado (edición de modelos), por lo que ya no se omiten y, por lo tanto, deben quitarse.

NDLNetworkBuilder reference (en desuso)

La sintaxis del en desuso NDLNetworkBuilder es:

NDLNetworkBuilder = [
    networkDescription = "yourNetwork.ndl"
]

El NDLNetworkBuilder bloque tiene los parámetros siguientes:

  • networkDescription: la ruta de acceso del archivo de descripción de red. Con el en desuso NDLNetworkBuilder, era habitual usar la extensión .ndlde archivo . Si no se especifica ningún networkDescription parámetro, se supone que la descripción de la red se inserta en el mismo NDLNetworkBuilder subbloqueo, especificado con el run parámetro siguiente. Tenga en cuenta que solo se puede especificar una ruta de acceso de archivo a través del networkDescription parámetro . Para cargar varios archivos de macros, use el ndlMacros parámetro .

  • run: bloque del NDL que se ejecutará. Si se especifica un archivo NDL externo a través del networkDescription parámetro , el run parámetro identifica un bloque en ese archivo. Este parámetro invalida los run parámetros que pueden existir en el archivo. Si no se especifica ningún networkDescription archivo, el run parámetro identifica un bloque en el archivo de configuración actual.

  • load: bloques de scripts de NDL que se van a cargar. Se pueden especificar varios bloques a través de una lista separada por ":". Los bloques especificados por el load parámetro normalmente contienen macros para su uso por parte del run bloque. De forma similar al run parámetro , el load parámetro identifica los bloques de un archivo NDL externo e invalida los load parámetros que pueden existir en el archivo, si el networkDescription parámetro especifica un archivo. Si no se especifica ningún networkDescription archivo, load identifica un bloque en el archivo de configuración actual.

  • ndlMacros: la ruta de acceso del archivo donde se pueden cargar macros NDL. Este parámetro se usa normalmente para cargar un conjunto predeterminado de macros NDL que todos los scripts de NDL pueden usar. Se pueden cargar varios archivos NDL, cada uno especificando diferentes conjuntos de macros, especificando una lista separada "+" de rutas de acceso de archivo para este ndlMacros parámetro. Para compartir macros con otros bloques de comandos, como los bloques de lenguaje de edición de modelos (MEL) de NDL, debe definirlo en el nivel raíz del archivo de configuración.

  • randomSeedOffset: un valor de desplazamiento de inicialización aleatorio no negativo para inicializar los parámetros que se pueden aprender. El valor predeterminado es 0. Esto permite a los usuarios ejecutar experimentos con una inicialización aleatoria diferente.