Поделиться через


Функции 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 должно быть явно выбрано в конце с помощью . синтаксиса, в противном случае значение функции будет всей записью.