你当前正在访问 Microsoft Azure Global Edition 技术文档网站。 如果需要访问由世纪互联运营的 Microsoft Azure 中国技术文档网站,请访问 https://docs.azure.cn

BrainScript 函数

函数是创建可重用模块的 BrainScript 方法。 函数是参数化表达式,可以直接定义,例如:

Sqr (x) = x * x

函数是一流的数据类型,因为函数可以接受和返回其他函数。

定义函数

有两种方法可以定义函数、标准语法和匿名 lambda 语法。

标准语法

命名函数是定义为这两种形式记录成员分配的记录成员:

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

这两种形式语法相同,但按照惯例,使用大括号{ }) 窗体的第二个 (用于返回具有内置可学习参数的函数对象,例如CNTK的预定义层

函数参数创建名称范围,如记录;即函数参数就像在封闭记录中定义为成员一样。

可选参数

函数可以具有可选参数。 这些值按其值之前传递。name= 如果未传递可选参数,则使用其默认值。 在函数定义中,可选参数使用表单 name=defaultValue声明。

下面是采用可选陡峭参数的 Softplus 函数的示例:

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

注意:可选参数的默认值无法访问其他参数。 例如,如果对于上述 Softplus 函数,你有一个定义要传递给函数的陡峭度的变量,只需说:

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

其中,第一 steepness 个是参数的名称,第二个是值传递的表达式。 名称不会冲突。

Lambda 语法

除了函数定义语法外,BrainScript 还允许匿名 lambda 使用语法 (x => f(x))(旨在模拟 C# )。 以下两个定义等效:

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

实际上,BrainScript 分析器在内部将后者转换为前形式。

lambda 语法当前限制为一个参数,也不支持可选参数。 具有多个参数或可选参数的 Lambda 必须定义为命名函数。

调用函数

函数按预期调用:通过传递括号中的参数;并将可选参数作为命名参数传递。 例如,一种自稳定器 (缩放技术与使用具有陡峭度 4 的 Softplus 的批处理规范化) 有点类似,将使用以下两个函数调用编写:

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

函数在调用时延迟执行。 具体来说,如果函数在 (内定义模型参数,即) 内调用模型参数的任何函数的 ParameterTensor{} 变体,则函数的每个调用都会生成这些模型参数的独立实例。 通常,函数调用的结果将分配给变量或作为函数参数传递。 此类值的多次使用不会导致重复执行。

纯洁度/引用透明度

尽管 BrainScript 具有功能语言的常见之处,但请注意,BrainScript 函数并不完全纯洁,但这可以产生一个非常具体的副作用:实例化新模型参数。 例如,虽然表达式2 * sqr(x)等效于sqr(x) + sqr(x)) sqr(x) = x*x (,但与) 的情况并非如此2 * ParameterTensor{N}ParameterTensor{N} + ParameterTensor{N}

按照约定,使用圆括号 ( ) ,其中提供了引用透明度,但大括号 { } 用于指示它的位置。

函数作为值

在 BrainScript 中,函数是值。 可以将命名函数分配给变量并作为参数传递。 事实上,命名函数只是具有类型为“function”的记录成员。函数名称查找与记录成员查找相同。 例如,这允许通过在记录表达式中定义函数来将函数分组到“命名空间”。

命名函数作为值

如果使用函数名称而不遵循括号,它将函数本身称为对象。

部分应用程序/Currying

BrainScript 目前没有语法支持部分应用程序或咖喱。 为了达到类似的效果,可以定义调用具有绑定/捕获参数的另一个函数的新函数:

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

示例

以下示例定义了常见网络层类型的简单层次结构:

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

可以使用如下方法:

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

这与 基本概念中的初始示例相同,但使用函数。

下一步:了解 模型编辑 或直接转到 完整函数引用

NDLNetworkBuilder (弃用的)

早期版本的CNTK使用现已弃用NDLNetworkBuilder,而不是 BrainScriptNetworkBuilderNDLNetworkBuilder函数定义在几种方面有所不同。 以下定义在以下方面 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

特别是,返回值是通过与函数同名的局部变量返回的;如果未找到此类变量,则返回最后定义的变量。 在 BrainScript 中,现在必须编写为:

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

特别是,必须使用语法显式选择.返回值Y,否则函数的值将是整个记录。