Compartir a través de


Funciones de BrainScript

Las funciones son la forma de BrainScript de crear módulos reutilizables. Las funciones son expresiones parametrizadas y se pueden definir de forma directa, por ejemplo:

Sqr (x) = x * x

Las funciones son tipos de datos de primera clase en el sentido de que las funciones pueden aceptar y devolver otras funciones.

Definición de funciones

Hay dos maneras de definir funciones, sintaxis estándar y sintaxis lambda anónima.

Sintaxis estándar

Las funciones con nombre son miembros de registro que se definen como asignaciones de miembros de registro de estas dos formas:

f (arg1, arg2, ..., optionalArg1=optionalArg1Default, ...) = expression of args
f {arg1, arg2, ..., optionalArg1=optionalArg1Default, ...} = expression of args

Las dos formas son sintácticamente idénticas, pero por convención, la segunda (mediante llaves { }) se usa para funciones que devuelven objetos de función con parámetros de aprendizaje integrados, como las capas predefinidas de CNTK.

Los argumentos de función crean un ámbito de nombre como un registro; Es decir, los argumentos de función son como si se definiesen como miembros en un registro envolvente.

Parámetros opcionales

Las funciones pueden tener parámetros opcionales. Estos se pasan antes de su valor por name=. Si no se pasa un parámetro opcional, se usa su valor predeterminado. En la definición de función, los parámetros opcionales se declaran con el formato name=defaultValue.

Este es un ejemplo de una función Softplus que toma un parámetro de empinado opcional:

Softplus (x, steepness=1) =
    if steepness == 1                # use the simpler formula if no steepness specified
    then Log (Constant(1) + Exp (x)) # softplus(x)
    else {                           # version with steepness: 1/s softplus (s x)
        steepnessAsConstant = Constant (steepness)
        y = Reciprocal (steepnessAsConstant) .* Softplus (steepnessAsConstant .* x)
        # Note: .* denotes an elementwise product
    }.y

Nota: Los valores predeterminados de los parámetros opcionales no pueden tener acceso a otros parámetros. Por ejemplo, si para la función Softplus anterior tiene una variable que define la empinada que desea pasar a la función, simplemente diga:

steepness = 4
z = Softplus (x, steepness=steepness)

donde el primero steepness es el nombre del parámetro y el segundo es la expresión para que el valor se pase también. Los nombres no entrarán en conflicto.

Sintaxis lambda

Además de la sintaxis de definición de función, BrainScript permite expresiones lambda anónimas mediante la sintaxis (x => f(x)), que está pensada para imitar C#. Las dos definiciones siguientes son equivalentes:

sqr(x) = x * x
sqr = (x => x * x) 

En realidad, el analizador brainScript transforma internamente el segundo en la forma anterior.

La sintaxis lambda está restringida actualmente a un parámetro y tampoco admite parámetros opcionales. Las expresiones lambda con más de un parámetro o con parámetros opcionales deben definirse como funciones con nombre.

Invocar funciones

Las funciones se invocan como espera: pasando los argumentos entre paréntesis; y pasar argumentos opcionales como parámetros con nombre. Por ejemplo, un auto-estabilizador (una técnica de escalado algo similar a la normalización por lotes) mediante Softplus con empinamiento 4 se escribiría con las dos invocaciones de función siguientes:

beta = ParameterTensor {1, initValue=1}
xStabilized = x .* Softplus (beta, steepness=4)

Las funciones se ejecutan de forma diferida tras la invocación. En concreto, si una función define parámetros de modelo dentro de (es decir, las variantes de ParameterTensor{} y cualquier función que tenga llamadas a ella dentro), cada invocación de la función produce una instancia independiente de estos parámetros del modelo. A menudo, el resultado de una invocación de función se asigna a una variable o se pasa como argumento de función. Varios usos de este valor no dan lugar a una ejecución repetida.

Transparencia referencial/pureza

Aunque BrainScript tiene similitudes con lenguajes funcionales, tenga en cuenta que las funciones de BrainScript no son completamente puras, en que pueden tener un efecto secundario muy específico: el de crear instancias de nuevos parámetros de modelo. Por ejemplo, aunque la expresión 2 * sqr(x) es equivalente a sqr(x) + sqr(x) (con sqr(x) = x*x), no es el caso de 2 * ParameterTensor{N} frente a ParameterTensor{N} + ParameterTensor{N}.

Por convención, use paréntesis redondos ( ) donde se proporciona transparencia referencial, pero llaves { } para indicar dónde no está.

Funciones como valores

En BrainScript, las funciones son valores. Una función con nombre se puede asignar a una variable y pasarla como argumento. De hecho, las funciones con nombre son simplemente miembros de registro que tienen el tipo "function". La búsqueda de nombre de función es la misma que la búsqueda de miembros de registro. Esto permite, por ejemplo, agrupar funciones en "espacios de nombres" definiéndolas dentro de una expresión de registro.

Funciones con nombre como valores

Si se usa un nombre de función sin seguir paréntesis, hará referencia a la propia función como un objeto.

Aplicación parcial/Cursing

BrainScript actualmente no tiene compatibilidad sintáctica con aplicación parcial o cursamiento. Para lograr un efecto similar, se puede definir una nueva función que llama a otra función con parámetros enlazados o capturados:

Layer (x, m, n, f) = f (ParameterTensor {(m:n)} * x + ParameterTensor {n})
Sigmoid512Layer (x) = Layer (x, 512, 512, Sigmoid)

Ejemplos

En el ejemplo siguiente se define una jerarquía simple de tipos de capa de red comunes:

Layers = {
    # base feed-forward without and with parameterized energy function
    # AffineLayer() defines model parameters inside.
    AffineLayer (x, m, n) = ParameterTensor {(m:n), init='heNormal'} * x
                            + ParameterTensor {n, initValue=0}
    # NLLayer applies a non-linearity on top of an affine layer
    NLLayer (x, m, n, f) = f (AffineLayer (x, m, n))
    # a few non-linearities
    SigmoidLayer (x, m, n)  = NLLayer (x, m, n, Sigmoid) # pass Sigmoid() function as the non-linaerity
    SoftplusLayer (x, m, n) = NLLayer (x, m, n, (x => Log (Constant(1) + Exp(x)))/*Softplus as lambda*/)
    ReLULayer (x, m, n)     = NLLayer (x, m, n, RectifiedLinear)
    SoftmaxLayer (x, m, n)  = NLLayer (x, m, n, Softmax)
}

que se podría usar de la siguiente manera:

# constants defined
# Sample, Hidden, and Label dimensions
SDim = 28*28 # feature dimension
HDim = 256   # hidden dimension
LDim = 10    # number of classes

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

# layers
h = Layers.ReLULayer (features, HDim, SDim)
z = Layers.AffineLayer (h, LDim, HDim) # input to softmax; same as log softmax without normalization  

# output and criteria
P    = Softmax (z)
ce   = CrossEntropyWithSoftmax (labels, z)
errs = ErrorPrediction         (labels, z)

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

Este es el mismo que el ejemplo inicial de Conceptos básicos, pero mediante funciones.

Siguiente: Obtenga información sobre la edición de modelos o vaya directamente a La referencia de función completa.

NDLNetworkBuilder (en desuso)

Las versiones anteriores de CNTK usaban el objeto ahora en desuso NDLNetworkBuilder en lugar de BrainScriptNetworkBuilder. NDLNetworkBuilderLa definición de función de la función difiere de varias maneras. La definición siguiente es válida en NDLNetworkBuilder:

FF (X1, W1, B1) # no equal sign
[ # both curly braces and brackets are allowed
    T = Times (W1, X1)
    FF = Plus (T, B1)
] # return value is FF, and not the entire record

Especialmente, el valor devuelto se devuelve a través de la variable local que tiene el mismo nombre que la función; o si no se encuentra dicha variable, se devolverá la variable definida por última vez. En BrainScript, ahora debe escribirse como:

FF (X1, W1, B1) =
{
    T = Times (W1, X1)
    Y = Plus (T, B1)
}.Y

Especialmente, el valor Y devuelto debe seleccionarse explícitamente al final mediante . la sintaxis ; de lo contrario, el valor de la función sería todo el registro.