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 NDLNetworkBuilder
BrainScriptNetworkBuilder
La conversión de una definición de red existente para en NDLNetworkBuilder
BrainScriptNetworkBuilder
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ó unComputationNetwork
.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 comoParameter(N)
, ahora tiene que escribirse explícitamente como tensorParameterTensor{N}
o una matrizParameter(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 nombreLearnableParameter()
heredado aParameterTensor{}
.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 ejemploRowStack (a:b:c)
, en lugar deRowStack (a, b, c)
.Algunos valores predeterminados se han actualizado, principalmente el parámetro opcional
imageLayout
deConvolution()
, las operaciones de agrupación yImageInput()
. En el caso de NDL, estos valores predeterminados sonlegacy
, mientras que ahora el valor predeterminado escudnn
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 comocudnn
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 ejemplomodelPath
, ,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 serConstant()
,tag="eval"
debe sertag="evaluation"
yevalNodes
ahoraevaluationNodes
es .Se corrigieron algunos nombres mal escritos:
criteria
es ahoracriterion
(del mismo modocriterionNodes
),defaultHiddenActivity
ahoradefaultHiddenActivation
es .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 deinit=uniform
. Sin las comillas, BrainScript producirá un mensaje de error que indica que el símbolouniform
es desconocido.Los primitivos brainScript que crean parámetros (
Input{}
yParameterTensor{}
) 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 desusoNDLNetworkBuilder
, era habitual usar la extensión.ndl
de archivo . Si no se especifica ningúnnetworkDescription
parámetro, se supone que la descripción de la red se inserta en el mismoNDLNetworkBuilder
subbloqueo, especificado con elrun
parámetro siguiente. Tenga en cuenta que solo se puede especificar una ruta de acceso de archivo a través delnetworkDescription
parámetro . Para cargar varios archivos de macros, use elndlMacros
parámetro .run
: bloque del NDL que se ejecutará. Si se especifica un archivo NDL externo a través delnetworkDescription
parámetro , elrun
parámetro identifica un bloque en ese archivo. Este parámetro invalida losrun
parámetros que pueden existir en el archivo. Si no se especifica ningúnnetworkDescription
archivo, elrun
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 elload
parámetro normalmente contienen macros para su uso por parte delrun
bloque. De forma similar alrun
parámetro , elload
parámetro identifica los bloques de un archivo NDL externo e invalida losload
parámetros que pueden existir en el archivo, si elnetworkDescription
parámetro especifica un archivo. Si no se especifica ningúnnetworkDescription
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 estendlMacros
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 es0
. Esto permite a los usuarios ejecutar experimentos con una inicialización aleatoria diferente.