Функции BrainScript
Функции — это способ создания многократно используемых модулей BrainScript. Функции являются параметризованными выражениями и могут быть определены прямым образом, например:
Sqr (x) = x * x
Функции — это типы данных первого класса в том смысле, что функции могут принимать и возвращать другие функции.
Определение функций
Существует два способа определения функций, стандартного синтаксиса и анонимного лямбда-синтаксиса.
Стандартный синтаксис
Именованные функции — это члены записей, которые определяются как назначения элементов записи из этих двух форм:
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
— имя параметра, а второй — выражение для передаваемого значения.
Имена не конфликтуют.
Лямбда-синтаксис
Помимо синтаксиса определения функции, BrainScript позволяет анонимным лямбда-выражениям использовать синтаксис(x => f(x))
, который предназначен для имитации C#. Следующие два определения эквивалентны:
sqr(x) = x * x
sqr = (x => x * x)
На самом деле, средство синтаксического анализа BrainScript внутренне преобразует последний в ранеешнюю форму.
Лямбда-синтаксис в настоящее время ограничен одним параметром, а также не поддерживает необязательные параметры. Лямбда-выражения с несколькими параметрами или необязательными параметрами должны быть определены как именованные функции.
Вызов функций
Функции вызываются должным образом: передавая аргументы в круглые скобки; и передача необязательных аргументов в виде именованных параметров. Например, самостабилизатор (метод масштабирования несколько похож на пакетную нормализацию) с использованием Softplus с крутостью 4 будет записан с помощью следующих двух вызовов функций:
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}
относится к vs. ParameterTensor{N} + ParameterTensor{N}
По соглашению используйте круглые скобки ( )
, в которых указана ссылочная прозрачность, но фигурные скобки { }
, чтобы указать, где она отсутствует.
Функции в качестве значений
В BrainScript функции являются значениями. Именованной функции можно назначить переменной и передать в качестве аргумента. На самом деле именованные функции — это просто элементы записи, имеющие тип "function". Поиск по имени функции совпадает с поиском элемента записи. Это позволяет, например, группировать функции в "пространства имен", определяя их внутри выражения записи.
Именованные функции в качестве значений
Если имя функции используется без следующих скобок, она будет ссылаться на саму функцию как объект.
Частичное приложение или карриинг
В настоящее время 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
должно быть явно выбрано в конце с помощью .
синтаксиса, в противном случае значение функции будет всей записью.