FPGA開発日記

カテゴリ別記事インデックス https://msyksphinz.github.io/github_pages , English Version https://fpgadevdiary.hatenadiary.com/

Chainer, TensorFlow, CNTKで関数フィッティングの方法を比較する

これまで、様々なツールを用いて機械学習の勉強をしてきた。ツールが出る度に試しているのでミーハーなことこの上無いのだが、一応代表的なツールは見てきたつもりだ。

その中で入門として使ってきたのが、関数フィッティングだった。様々な関数を、多項式にフィッティングさせてみる。この題材を使って、各種機械学習フレームワークでどのような違いがあるのか、比較してまとめてみたいと思う。

関数フィッティング問題とは

とあるデータが与えられて、それがどのような多項式に近いのかを学習する。例えば矩形波のデータが与えられた場合、これを7次多項式にフィッティングさせる。

f:id:msyksphinz:20160716120332p:plain

{ \displaystyle
a_7 x^{7}+a_6 x^{6}+a_5 x^{5}+a_4 x^{4}+a_3 x^{3}+a_2 x^{2}+a_1 x+a_0
}

その結果、以下のような波形が得られる。

f:id:msyksphinz:20160716120531p:plain

これをどのように実現するか、ということだ。

基本的には、多項式のモデルを作成し、それに対して入力値と出力値の誤差が最小になるようにパラメータを調整する、という具合になる。

モデルの作成方法

Chainerでの多項式モデルの作成方法

msyksphinz.hatenablog.com

Chainerでは、多項式のモデルを作るというより、ニューラルネットワークをまずは作成し、そこに近い様に入力と出力の接続を構成する。

最初にネットワークの構成を定義し、次に計算方法(forward)を定義するという訳だ。ここでは線形関数を接続していき、全てのノードに対して入力値xを食わせることにより、多項式を実現している。

        super(Function3DimentionModel, self).__init__(
            fc1=chainer.functions.Linear(1, 1, 1),
            fc2=chainer.functions.Linear(1, 1),
            fc3=chainer.functions.Linear(1, 1),
            fc4=chainer.functions.Linear(1, 1),
            fc5=chainer.functions.Linear(1, 1),
            fc6=chainer.functions.Linear(1, 1),
            fc7=chainer.functions.Linear(1, 1),
    )
    def forward(self, x):
        h1 = self.fc1(x)
        h2 = self.fc2(h1*x)
        h3 = self.fc3(h2*x)
        h4 = self.fc4(h3*x)
        h5 = self.fc5(h4*x)
        h6 = self.fc6(h5*x)
        h7 = self.fc7(h6*x)
        return h7

TensorFlowでのモデルの作成方法

msyksphinz.hatenablog.com

TensorFlowでは、Pythonの記述を使って多項式をそのまま定義してしまった。ここで、ニューラルネットワークの構造とか、あまり指定することは無い。単純に関数を定義しただけだ。

W3 = tf.Variable(random.random())
W2 = tf.Variable(random.random())
W1 = tf.Variable(random.random())
W0 = tf.Variable(random.random())

y4 = W3*x_data*x_data*x_data+W2*x_data*x_data + W1*x_data + W0

CNTKでのモデルの作成方法

msyksphinz.hatenablog.com

CNTKの場合には、Pythonを使わずに独自の記述言語を利用する。以下は2次多項式の場合。

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)

こちらも、あまりネットワークの構成とか、気にせずにモデルを作成している。

最適化の行い方

Chainerの学習実施方法

Chainerで最適化を実行するためには、先程のネットワークにどんどんデータを入力していき、誤差を0の方向にするためにデータをバックプロパゲートしていく。

まるで本当にニューラルネットワークを触っているような、そんな感覚になる。

    def train(self, x_data, y_data):
        x = chainer.Variable(x_data.astype(np.float32), volatile=False)
        y = chainer.Variable(y_data.astype(np.float32), volatile=False)
        h = self.forward(x)

        optimizer.zero_grads()
        # error = chainer.functions.sigmoid(h, y)
        error = F.mean_squared_error(h, y)
        error.backward()
        optimizer.update()

TensorFlowでの学習実施方法

TensorFlowでの最適化を実行するのも同様だ。データを入力していき、誤差が0になるように関数の最適化を実行していく。このあたりのモデルは、Chainerととても良く似ている気がする。

loss = tf.reduce_mean(tf.square(y4 - y_data))
optimizer = tf.train.GradientDescentOptimizer(0.5)
train = optimizer.minimize(loss)

CNTKでの学習実施方法

CNTKでの最適化の実施方法だが、上記のChainerやTensorFlowとは異なり、ネットワークの設定パラメータとして記述していく。SGDを使いたいのなら、DNNのパラメータとして指定していく。 このあたりは、Pythonで手続として記述できないので、ちょっと敷居が高い。

Simple_Demo_Train = [
    action = "train"

    NDLNetworkBuilder = [
        networkDescription = "$ConfigDir$/Simple2.ndl"
    ]

    SGD = [
        # epochSize = 0 means epochSize is the size of the training set
        epochSize = 0
        minibatchSize = 2
        randomizeRange = 0
        learningRatesPerMB = 0.5:0.2*20:0.1
        momentumPerMB = 0.9
        dropoutRate = 0.0
        maxEpochs = 10
    ]

3つの機械学習ツールを比較して

ChainerとTensorFlowは、学習のための手続が非常に似ており、Pythonを使って手続を記述できることもあって敷居が低い。気軽に様々なことがトライできるのが利点かもしれない。

一方でCNTKは記述の方法にPythonが使えず、モデルの構築方法も敷居が高い。その分、Microsoftが言っているように、高速なネットワークを構築することが可能、ということかもしれないが、初心者にはちょっと敷居が高いかな。