函式是 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 目前沒有語法支援部分應用程式或 Currying。 若要達到類似的效果,您可以定義新的函式,以使用系結/擷取的參數呼叫另一個函式:
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 ,否則函式的值會是整個記錄。