次の方法で共有


BrainScript 関数

関数は、再利用可能なモジュールを作成する BrainScript の方法です。 関数はパラメーター化された式であり、次のように簡単に定義できます。

Sqr (x) = x * x

関数は、関数が他の関数を受け入れて返すことができるという意味で、最上位クラスのデータ型です。

関数の定義

関数を定義するには、標準構文と匿名ラムダ構文の 2 つの方法があります。

標準構文

名前付き関数は、次の 2 つの形式のレコード メンバー割り当てとして定義されているレコード メンバーです。

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

2 つの形式は構文的に同じですが、規則により、2 つ目のフォーム (中かっこ{ }を使用) は、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)

1 つ目 steepness はパラメーターの名前で、2 つ目は値を渡す式です。 名前は競合しません。

ラムダ構文

関数定義構文に加えて、BrainScript では、C# を模倣するための構文 (x => f(x))を使用して匿名ラムダを使用できます。 次の 2 つの定義は同等です。

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

実際には、BrainScript パーサーは内部的に後者を元の形式に変換します。

ラムダ構文は現在、1 つのパラメーターに制限されており、省略可能なパラメーターもサポートしていません。 複数のパラメーターまたは省略可能なパラメーターを持つラムダは、名前付き関数として定義する必要があります。

関数の呼び出し

関数は想定どおりに呼び出されます。引数をかっこで渡します。および省略可能な引数を名前付きパラメーターとして渡します。 たとえば、急勾配 4 の Softplus を使用するセルフスタビライザー (バッチ正規化に多少似たスケーリング手法) は、次の 2 つの関数呼び出しで記述されます。

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

関数は呼び出し時に遅延実行されます。 具体的には、関数が内部のモデル パラメーター (つまり、内部で呼び出しを持つ関数の ParameterTensor{} バリアント) を定義する場合、関数の各呼び出しは、これらのモデル パラメーターの独立したインスタンスを生成します。 多くの場合、関数呼び出しの結果は変数に割り当てられるか、関数引数として渡されます。 このような値を複数回使用しても、繰り返し実行されません。

純度/参照の透明性

BrainScript には関数言語との共通点がありますが、BrainScript 関数は完全に純粋なものではなく、新しいモデル パラメーターのインスタンス化という 1 つの非常に具体的な副作用を持つことがあることに注意してください。 たとえば、式2 * sqr(x)が (with) とsqr(x) = x*x同等sqr(x) + sqr(x)ですが、vs. の場合2 * ParameterTensor{N}はそうではありません。 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では、次の代わりにBrainScriptNetworkBuilder非推奨NDLNetworkBuilderになりました。 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 は構文を使用して . 最後に明示的に選択する必要があります。それ以外の場合、関数の値はレコード全体になります。