Python(基本的なこと010: Tensorflow1.15_勾配降下法_簡単な方程式_試作)


■勾配降下法について、簡単な方程式で考えてみる。
前回までで、mnistのデータ、ソフトマックス関数や交差エントロピーについて見てきた。それぞれの部分で何をやっているかイメージがつかめてきたけど、下のコードの部分で何をやっているのかいまいちよく分からない。

def training(cost, global_step):
    optimizer = tf.train.GradientDescentOptimizer(learning_rate)
    train_op = optimizer.minimize(cost, global_step=global_step)
    return train_op

tf.train.GradientDescentOptimizer()から、勾配降下法(誤差(損失)が小さい方向に少しずつ移動して最小の値を探すもの)を使用しているはずだけど、mnistのサンプルではここの挙動がよく分からない。そのため、もっと簡単なデータでできないか試してみた。
使用したのは、下の方程式。最小となる点(x=0, y=5, z=10)から、xやyが大きくなる又は小さくなるに従ってzが大きくなる。

この式を使って、x, yをそれぞれ-49から50まで動かし、zの値を得る。(xの整数値)100×(yの整数値)100で10000の値が得られるので、その値から1000個分を訓練用としてピックアップする。データはExcelで作成。
次に、pythonのサンプルコードを下のように作成してみた。データ部分は省略。

import tensorflow as tf
import numpy as np

learning_rate = 0.1
training_epochs = 100
batch_size = 5
display_step = 1

batch_set = [[-21, 37], [-27, 27], ...(略)... , [-19, 26], [-47, -45]]

Excel_data = [[1475], [1223], ...(略)... , [812], [4719]]

def inference(x):
    init = tf.constant_initializer(value=0)
    W = tf.get_variable("W", [2, 1], initializer=init)
    output = tf.matmul(x, W)
    return output

def loss(output, y):
    loss = tf.reduce_sum(y - output)
    return loss

def training(cost, global_step):
    optimizer = tf.train.GradientDescentOptimizer(learning_rate)
    train_op = optimizer.minimize(cost, global_step=global_step)
    return train_op

with tf.Graph().as_default():
    x = tf.placeholder("float", [None, 2])
    y = tf.placeholder("float", [None, 1])
    output = inference(x)
    cost = loss(output, y)
    global_step = tf.Variable(1, name="global_step", trainable=False)
    train_op = training(cost, global_step)
    init_op = tf.global_variables_initializer()
    sess = tf.Session()
    sess.run(init_op)

    for epoch in range(training_epochs):
        avg_cost = 0
        total_batch = int(len(batch_set) / batch_size)
        for i in range(0, total_batch, batch_size):
            minibatch_x = np.array(batch_set[i:i + batch_size])
            minibatch_y = np.array(Excel_data[i:i + batch_size])
            sess.run(train_op, feed_dict={x: minibatch_x, y: minibatch_y})
            avg_cost += sess.run(
                cost, feed_dict={x: minibatch_x, y: minibatch_y}
            )
        if epoch % display_step == 0:
            print("Epoch: {:04d} cost: {:.9f}".format(epoch + 1, avg_cost))
            result = sess.run(output, feed_dict={x: [[0, 5]]})
            print("result", result)

        print("Optimization Finished!")

ここで、 batch_setは(x, y)のセット、Excel_dataは方程式から求めたzの値としている。
inference(x)で、batch_setを入力して、zの推定の値outputを求める(output = w1 * x + w2 * y)。その後、 loss(output, y)で、そのoutputの値とExcel_data(zの真値)との差異を求める。さらに、その差異が小さくなるように training(cost, global_step)の勾配降下法を動かしていく。こんな形で学習して、最終的にはいい感じのW(w1, w2)が得られると思っていたけど、実際に動かした結果が下の画像。

学習が終わった後のSessionを使って最小部分(x=0, y=5)の値を求めれば、最小の10に近いものが得られるかと期待したけど、実際は11350.034。しかも、学習を繰り返すごとに値が大きくなってしまっている。

データの数が不十分なのか、差異を求めるところがおかしいのか、いろいろと見ていくところはありそう。