以下のやってみた日記は、機械学習初心者が書いているため、間違っている内容が含まれている可能性があります。 間違いがあれば、ご指摘頂ければうれしいです。
前回、CNTKを使って、シンプルなネットワークを構築して関数のフィッティングに挑戦した。 そのときには、SimpleNetworkBuilderという関数を使い、ある程度定義されたネットワークの中でパラメータを調整し、関数のフィッティングにチャレンジした。
でも、それだけでは面白くない。CNTKには、NDLNetworkBuilderという関数を使ってDNNを詳細に構築できる機能がある。これを使って、多項式にフィッティングするディープネットワーク構築に挑戦してみようじゃないか。
CNTKでディープネットワークを構築するためには
まずはサンプルを見ていこう。MNISTの02_ConvolutionあたりがNDL(Network Description Language)でネットワークを構築しているね。
CNTK/Examples/Image/MNIST at master · Microsoft/CNTK · GitHub
この例を見る限り、
- 入力ノード
- 出力ノード
を定義し、
- 内部に必要になるパラメータ
を定義してやれば良いらしい。さっそくやってみよう。
対象とする問題: 関数フィッティング
関数フィッティングについては、前回の日記で書いた。対象とする関数は以下のようなもので、100000点のサンプルデータが用意されている。
これを1次元の方程式と、2次元の方程式でどのようにしてフィッティングをするか考えてみよう。
ネットワークを定義する
CNTKでは、NDL(Network Description Language) によってニューラルネットを定義できる。詳細は、以下の資料を「熟読」する必要がある。ひえー。
http://research.microsoft.com/pubs/226641/CNTKBook-20160121.pdf
入力とラベルの定義
ここでは、ndlPolymacrosというsectionを作成した。
ndlPolyMacros = [ features = InputValue(1) labels = Input(1) ]
入力値は一次元で、ラベルも1種類だけである。
ネットワークの定義
以外とこれが簡単だ。モデル化したいネットワークを、そのまま構築してやれば良い。
二次多項式の場合
DNN=[ W1 = Parameter (1, 1, init="uniform", initValueScale=1.0) W2 = Parameter (1, 1, init="uniform", initValueScale=1.0) W3 = Parameter (1, 1, init="uniform", initValueScale=1.0) y0 = Times(W1, ElementTimes(features, features)) y1 = Times(W2, features) y = Plus(Plus(y0, y1), W3) ce = SquareError(labels, y) err = ErrorPrediction(labels, y) # Special Nodes FeatureNodes = (features) LabelNodes = (labels) CriterionNodes = (ce) EvalNodes = (err) OutputNodes = (y) ]
一次多項式の場合
DNN=[ W1 = Parameter (1, 1, init="uniform", initValueScale=1.0) W2 = Parameter (1, 1, init="uniform", initValueScale=1.0) y0 = Times(W1, features) y = Plus(y0, W2) ce = SquareError(labels, y) err = ErrorPrediction(labels, y) # Special Nodes FeatureNodes = (features) LabelNodes = (labels) CriterionNodes = (ce) EvalNodes = (err) OutputNodes = (y) ]
ネットワーク自体は、全く説明する必要がないんじゃないかな?
注意すべきこと
これ、見た目は一次元の変数で記述されているが、実際にはミニバッチの方式などで、複数のグループでデータが入ってくるらしい。 従って、この式自体が行列として表現されることに注意する必要がある。Times(行列積)などでは、次元が合うように調整しなければならない。
ネットワーク構築中のログ
Validating --> labels = InputValue -> [1 x *] Validating --> W1 = LearnableParameter -> [1 x 1] Validating --> features = InputValue -> [1 x *] Validating --> unnamed15 = ElementTimes(features[1 x *], features[1 x *]) -> [1 x *] Validating --> y0 = Times(W1[1 x 1], unnamed15[1 x *]) -> [1 x *] Validating --> W2 = LearnableParameter -> [1 x 1] Validating --> y1 = Times(W2[1 x 1], features[1 x *]) -> [1 x *] Validating --> unnamed18 = Plus(y0[1 x *], y1[1 x *]) -> [1 x *] Validating --> W3 = LearnableParameter -> [1 x 1] Validating --> y = Plus(unnamed18[1 x *], W3[1 x 1]) -> [1 x 1 x *] Validating --> ce = SquareError(labels[1 x *], y[1 x 1 x *]) -> [1]
この通り、例えば入力データであるfeatureは、[1xN]の行列になる。ここにハマるとなかなか大変だ。 従って、 と書くのはたやすいが(W1は1行1列)、実際には、
となっていることに注意が必要だ。この形式を崩さないためにも、二次関数の際にxのベクトルを崩さないように、"ElementTimes"という関数を利用して、行列積ではなく、行列要素の積を構築するようにしてある。
y0 = Times(W1, ElementTimes(features, features))
CNTKで学習させる
一次関数へのフィッティング
まずは一次関数への近似からだ。前回と同様に100000点で学習させ、1000点をテストする。
おお、良い感じにフィッティングしているね!前回の結果はさておき、一次関数でかなり近い形状でフィッティングしている。
二次関数へのフィッティング
お、よりそれっぽいね。という訳で、多項式へのフィッティングはうまくいっているみたいだ。実際にどのようなパラメータとして収束しているか、いろいろ調査してみよう。