BrainScript の基本的な概念
BrainScript--A Walk-Through
このセクションでは、"BrainScript" 言語の基本的な概念について説明します。 新しい言語ですか? 読む & 心配しないでください、それは非常に簡単です。
CNTK では、カスタム ネットワークは を使用して BrainScriptNetworkBuilder
定義され、CNTK ネットワーク記述言語 "BrainScript" で説明されています。同様に、ネットワークの説明は脳スクリプトと呼ばれます。
BrainScript は、式、変数、プリミティブ関数と自己定義関数、入れ子になったブロック、その他よく理解されている概念を使用して、コードのような方法でネットワークを定義する簡単な方法を提供します。 構文のスクリプト言語に似ています。
それでは、完全な BrainScript の例で足を濡らしましょう。
完全な BrainScript ネットワーク定義の例
次の例は、1 つの非表示レイヤーと 1 つの分類レイヤーを持つ単純なニューラル ネットワークのネットワークの説明を示しています。 この例に沿って概念について説明します。 先に進む前に、例を使用して数分を費やし、それが何を意味するのかを推測してみてください。 読み上げたように、そのほとんどが正しく推測されていることがわかります。
BrainScriptNetworkBuilder = { # (we are inside the train section of the CNTK config file)
SDim = 28*28 # feature dimension
HDim = 256 # hidden dimension
LDim = 10 # number of classes
# define the model function. We choose to name it 'model()'.
model (features) = {
# model parameters
W0 = ParameterTensor {(HDim:SDim)} ; b0 = ParameterTensor {HDim}
W1 = ParameterTensor {(LDim:HDim)} ; b1 = ParameterTensor {LDim}
# model formula
r = RectifiedLinear (W0 * features + b0) # hidden layer
z = W1 * r + b1 # unnormalized softmax
}.z
# define inputs
features = Input {SDim}
labels = Input {LDim}
# apply model to features
z = model (features)
# define criteria and output(s)
ce = CrossEntropyWithSoftmax (labels, z) # criterion (loss)
errs = ErrorPrediction (labels, z) # additional metric
P = Softmax (z) # actual model usage uses this
# connect to the system. These five variables must be named exactly like this.
featureNodes = (features)
inputNodes = (labels)
criterionNodes = (ce)
evaluationNodes = (errs)
outputNodes = (P)
}
BrainScript 構文の基本
説明する前に、BrainScript の構文に関するいくつかの一般的な注意事項を確認してください。
BrainScript は、数式のようにニューラル ネットワークを表現することを目的とした単純な構文を使用します。 したがって、基本的な構文単位は代入であり、変数の代入と関数定義の両方で使用されます。 例:
Softplus (x) = Log (1 + Exp (x))
h = Softplus (W * v + b)
行、コメント、含める
通常、代入は 1 行で記述されますが、式は複数の行にまたがる場合があります。 ただし、複数の割り当てを 1 行に配置するには、セミコロンで区切る必要があります。 例:
SDim = 28*28 ; HDim = 256 ; LDim = 10 # feature, hidden, and label dimension
改行がない場合は、割り当ての間にセミコロンを必要とする以外に、BrainScript は空白を区別しません。
BrainScript では、Python スタイルと C++ スタイル #
の両方を使用して、行末コメントを //
理解します。 インライン コメントでは C 構文 (/* this is a comment*/
) が使用されますが、C とは異なり、これらは複数行にまたがるわけではありません。
CNTK 構成ファイルに埋め込まれた BrainScript の場合 (ディレクティブを介して include
別のファイルから読み取られる BrainScript とは対照的に)、構成パーサーとの対話により、かっこ、中かっこ、または角かっこを C/C++ スタイルのコメントと文字列リテラル内でバランスを取る必要があるという追加の制限があります。 したがって、C/C++スタイルのコメントにはスマイリーはありません。
ディレクティブは include "PATH"
、ステートメントのポイントにファイルの内容を挿入するために、任意の場所で使用できます。 ここでは、 PATH
絶対パスまたは相対相対パス (サブディレクトリの有無にかかわらず) を指定できます。 相対パスの場合は、現在の作業ディレクトリの順序で次の場所が検索されます。外部インクルード ファイルを含むディレクトリ (存在する場合)。構成ファイルを含むディレクトリ。最後に、CNTK 実行可能ファイルを含むディレクトリ。 組み込みの BrainScript 関数はすべて、CNTK 実行可能ファイルの横にある と呼ばれる CNTK.core.bs
ファイルからこの方法で含まれます。
式
次に、BrainScript 式 (ネットワークを記述する数式) について知る必要があります。
BrainScript 式 は、一般的なプログラミング言語に似た構文で、数学に似た形で記述されます。 最も簡単な式は、数値や文字列などのリテラルです。 数学のような例は です W1 * r + b
。ここで *
、変数の型に応じてスカラー、マトリックス、またはテンソル積を参照します。 もう 1 つの一般的な式は、関数の呼び出しです (例: RectifiedLinear (.)
)。
BrainScript は、動的に型指定された言語です。 重要な式の種類は、 構文を使用して定義され、ドット構文を介 {...}
してアクセスされるレコードです。 たとえば、 r = { x = 13 ; y = 42 }
は 2 つのメンバーを持つレコードを に r
割り当てます。ここで、最初のメンバーには として r.x
アクセスできます。
BrainScript には、通常の算術演算子に加えて、条件式 (if c then t else f
)、配列式、単純なラムダがあります。 最後に、CNTK C++ コードとインターフェイスするために、BrainScript は、定義済みの C++ オブジェクト (主に計算ネットワークで構成される) の制限されたセットを ComputationNode
直接インスタンス化できます。 詳細については、「 式」を参照してください。
BrainScript 式は、最初の使用時に評価されます。 BrainScript の主な目的はネットワークを記述することです。そのため、式の値は最終的な値ではなく、遅延計算用の計算グラフ内のノード (のように W1 * r + b
) になります。 BrainScript の解析時には、スカラーの BrainScript 式 (例: 28*28
) のみが '計算' されます。 使用されない式 (条件など) は評価されません。
注: 現在非推奨になった NDLNetworkBuilder
バージョンでは、関数呼び出し構文のみがサポートされています。たとえば、 を記述 Plus (Times (W1, r), b1)
する必要があります。
変数
変数は、任意の BrainScript 式 (数値、文字列、レコード、配列、ラムダ、CNTK C++ オブジェクト) の値を保持でき、式で使用すると置換されます。 変数は不変です。つまり、1 回だけ割り当てられます。 たとえば、上記のネットワーク定義は次で始まります。
SDim = 28*28
HDim = 256
LDim = 10
ここで、 変数 は、後続の式でパラメーターとして使用されるスカラー数値に設定されます。 これらの値は、トレーニングで使用されるデータ サンプル、非表示レイヤー、ラベルのディメンションです。 この特定のセットアップ例は、-pixel 画像の[28 x 28]
コレクションである MNIST データセット用です。 各画像は手書きの数字 (0 から 9) であるため、各画像に適用できるラベルは 10 個あります。 非表示のアクティブ化ディメンション HDim
は、ユーザーの選択です。
ほとんどの変数はレコード メンバーです (外側の BrainScript ブロックは暗黙的にレコードです)。 さらに、変数は関数引数にすることも、配列要素として格納することもできます。
OK、モデル定義を実行する準備ができました。
ネットワークの定義
ネットワークは、主に入力からネットワークの出力を計算する方法の数式によって記述されます。 これを モデル関数と呼びます。これは、多くの場合、BrainScript で実際の関数として定義されます。 モデル関数の一部として、ユーザーは モデル パラメーターを宣言する必要があります。 最後に、ネットワークの 入力と 条件/出力を定義する必要があります。 これらはすべて変数として定義されます。 その後、入力変数と抽出条件/出力変数をシステムに伝達する必要があります。
ネットワークのモデル関数とモデル パラメーター
モデル関数には、実際のネットワーク数式とそれぞれのモデル パラメーターが含まれています。 この例では、マトリックス積と加算を使用し、エネルギー関数の 'プリミティブ' (組み込み) 関数 RectifiedLinear()
を使用するため、ネットワーク関数のコアは次の式で構成されます。
r = RectifiedLinear (W0 * features + b0)
z = W1 * r + b1
モデル パラメーター は、トレーニングの完了時に学習したモデルを構成するマトリックス、バイアス ベクトル、またはその他のテンソルです。 model-parameter テンソルは、入力サンプル データを目的の出力に変換する場合に使用され、学習プロセスによって更新されます。 上記のネットワークの例には、次のマトリックス パラメーターが含まれています。
W0 = ParameterTensor {(HDim:SDim)}
b0 = ParameterTensor {(HDim)}
この場合、 W0
は重み行列であり、 b0
バイアス ベクトルです。 ParameterTensor{}
は、ベクター、マトリックス、または任意ランクテンソルをインスタンス化し、次元パラメーターを BrainScript 配列 (コロン :
で連結された数値) として受け取る特殊な CNTK プリミティブを表します。 ベクトルの次元は 1 つの数値ですが、行列ディメンションは として (numRows:numCols)
指定する必要があります。 既定では、パラメーターは、直接インスタンス化された場合、およびheNormal
レイヤーで使用される場合は均一な乱数で初期化されますが、完全な一覧には他のオプションが存在します (こちらを参照)。
通常の関数とは異なり、 ParameterTensor{}
はかっこではなく中かっこで引数を受け取ります。 中かっこは、関数ではなく、パラメーターまたはオブジェクトを作成する関数の BrainScript 規則です。
これはすべて BrainScript 関数にラップされます。 BrainScript 関数は、 という形式 f(x) = an expression of x
で宣言されています。 たとえば、 Sqr (x) = x * x
は有効な BrainScript 関数宣言です。 はるかに簡単で直接的にすることはできませんでしたか?
ここで、上の例の実際のモデル関数はもう少し複雑です。
model (features) = {
# model parameters
W0 = ParameterTensor {(HDim:SDim)} ; b0 = ParameterTensor {HDim}
W1 = ParameterTensor {(LDim:HDim)} ; b1 = ParameterTensor {LDim}
# model formula
r = RectifiedLinear (W0 * features + b0) # hidden layer
z = W1 * r + b1 # unnormalized softmax
}.z
外側 { ... }
とその最終版 .z
は、いくつかの説明に値します。 外側のカーリー{ ... }
とそのコンテンツは、実際には 6 つのレコード メンバー (W0
、b0
b1
W1
r
および) を持つレコードを定義します。z
ただし、モデル関数の値は単なる z
値です。それ以外の値はすべて関数の内部です。 したがって、 を使用 .z
して、返すレコード メンバーを選択します。 これは、レコード メンバーにアクセスするためのドット構文にすぎません。 この方法では、他のレコード メンバーには外部からアクセスできません。 ただし、 を計算する式の一部として存在し続けます z
。
この { ... ; x = ... }.x
パターンは、ローカル変数を使用する方法です。
レコード構文は必要ありません。 または、 model(features)
を 1 つの式として、レコード経由で迂回せずに宣言することもできます。
model (features) = ParameterTensor {(LDim:HDim)} * (RectifiedLinear (ParameterTensor {(HDim:SDim)}
* features + ParameterTensor {HDim})) + ParameterTensor {LDim}
これは読みにくく、さらに重要なことに、数式内の複数の場所で同じパラメーターを使用することはできません。
入力
ネットワークへの入力は、サンプル データとサンプルに関連付けられているラベルによって定義されます。
features = Input {SDim}
labels = Input {LDim}
Input{}
は、モデル定義に必要な 2 番目の特殊な CNTK プリミティブです (1 つ目は です Parameter{}
)。 これは、リーダーからネットワークの外部から入力を受け取る変数を作成します。 の Input{}
引数はデータ ディメンションです。 この例では、 features
入力にはサンプル データのディメンション (変数 SDim
で定義) があり、 labels
入力にはラベルのディメンションが含まれます。 入力の変数名は、リーダー定義内の対応するエントリと一致する必要があります。
トレーニング条件とネットワーク出力
ネットワークの出力が世界とどのように相互作用するかを宣言する必要があります。 このモデル関数は、logit 値 (正規化されていないログ確率) を計算します。 これらの logit 値は、次の場合に使用できます。
- トレーニング条件を定義します。
- 測定精度、および
- 入力を指定した出力クラスに対する確率を計算し、分類の決定に基づいて計算します (正規化されていないログ事後機能
z
は、多くの場合、分類に直接使用できることに注意してください)。
ネットワークの例では、カテゴリ ラベルを使用します。これは、1 つのホット ベクターとして表されます。 MNIST の例では、これらは 10 個の浮動小数点値の配列として表示され、 1.0 の適切なラベル カテゴリを除き、すべて 0 になります。
当社のような分類タスクでは、通常、 関数を SoftMax()
使用して各ラベルの確率を取得します。 その後、ネットワークは、正しいクラスのログ確率 (クロスエントロピ) を最大化し、他のすべてのクラスのログ確率を最小限に抑えるように最適化されます。 これはトレーニング 条件、つまり 損失関数です。 CNTK では、通常、これら 2 つのアクションが 1 つの関数に組み合わされて効率が向上します。
ce = CrossEntropyWithSoftmax (labels, z)
CrossEntropyWithSoftmax()
関数は入力を受け取り、関数を SoftMax()
計算し、クロスエントロピを使用して実際の値からエラーを計算し、そのエラー信号を使用して、バック伝達を介してネットワーク内のパラメーターを更新します。 したがって、上記の例では、 としてP
計算した正規化されたSoftmax()
値はトレーニング中に使用されません。 ただし、ネットワーク を使用 する場合は必要になります (多くの場合、 z
分類には十分です。その場合はそれ z
自体が出力になることに注意してください)。
CNTK では、学習アルゴリズムとして 確率勾配降下法 (SGD) が使用されます。 SGD では、すべてのモデル パラメーターに対して目的関数の勾配を計算する必要があります。 重要なのは、CNTK では、ユーザーがこれらのグラデーションを指定する必要はありません。 代わりに、CNTK の各組み込み関数にも対応する派生関数があり、システムはネットワーク パラメーターのバック伝達更新を自動的に実行します。 これはユーザーには表示されません。 ユーザーはグラデーションに関心を持つ必要はありません。 今まで。
トレーニング条件に加えて、トレーニングが進むにつれてシステムの改善を検証するために、多くの場合、トレーニング フェーズ中に予測エラー率が計算されます。 これは、次の関数を使用して CNTK で処理されます。
errs = ClassificationError (labels, z)
ネットワークによって生成される確率は実際のラベルと比較され、エラー率が計算されます。 これは通常、システムによって表示されます。 これは便利ですが、 を使用 ClassificationError()
することは必須ではありません。
入力、出力、条件をシステムに伝達する
すべての変数が定義されたので、どの変数を入力、出力、条件として扱うべきかをシステムに指示する必要があります。 これは、厳密に次の名前を持つ必要がある 5 つの特別な変数を定義することによって行われます。
featureNodes = (features)
labelNodes = (labels)
criterionNodes = (ce)
evaluationNodes = (errs)
outputNodes = (z:P)
値は配列であり、値はコロンで区切る必要があります (コロン :
は、2 つの値または配列を連結して配列を形成する BrainScript 演算子です)。 これは、 と のoutputNodes
両方z
P
を出力として宣言する に対して上に示されています。
(注: 非推奨 NDLNetworkBuilder
では、代わりに配列要素をコンマで区切る必要があります)。
特殊名の概要
上で説明したように、"マジック" プロパティを持つ 7 つの特別な名前を認識する必要があります。
ParameterTensor{}
: 学習可能なパラメーターを宣言して初期化します。Input{}
: 接続され、データ リーダーからフィードされる変数を宣言します。featureNodes
、labelNodes
、criterionNodes
、evaluationNodes
、および : 入力、出力、およびoutputNodes
条件として使用する変数をシステムに宣言します。
さらに、CNTK には、他の場所で説明されている"マジック" が組み込まれた 3 つの特別な関数があります。
Constant()
: 定数を宣言します。PastValue()
および : フォームの繰り返しFutureValue()
ループの場合は、別の時間ステップでネットワーク関数の変数にアクセスします。
その他の定義済みの名前は、 や Sigmoid()
Convolution()
C++ 実装の組み込みのプリミティブ関数、BrainScript で実現される定義済みのライブラリ関数 ( などBS.RNNs.LSTMP()
)、またはライブラリ関数の名前空間として機能するレコード (例: ) です。 BS.RNNs
完全な一覧については、「 BrainScript-Full-Function-Reference」を参照してください 。
次へ: BrainScript 式