你当前正在访问 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
,而不是 BrainScriptNetworkBuilder
。 NDLNetworkBuilder
函数定义在几种方面有所不同。 以下定义在以下方面 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
,否则函数的值将是整个记录。