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

■勾配降下法について、簡単な方程式で考えてみる。
前回、簡単な方程式として、2次関数(x^2 + (y-5)^2 + 10)を使ってtensorflowの利用を考えてみた。その後もう少し調べてみたら、偏微分がどうこうと複雑な話も出てきたので、さらに簡単な次の方程式で考えることにする。

この1次関数のようなデータであれば、最小2乗法から近似式を得ることもできるので、わざわざTensorflowを使って計算する必要はないと思う。ただ、最小2乗法の考え方を損失関数にするなど、コードの中で使用する式は明確なので、検証の材料にはちょうどいいと思う。
作成したサンプルコードは下の通り(データは省略)。データは、x=1~100, y=1~100として上の方程式からzの値を求め、その10000のセットよりランダムで1000選んだもの。例えば、[x, y] = [100, 98]なら、3 * 100 + 98 + 7 = 405となる。

import tensorflow as tf
import numpy as np
import matplotlib.pyplot as mp

learning_rate = 0.00001
training_epochs = 100
batch_size = 10
display_step = 1

# y=0, z=3x+7
x_axis = [[1], [2], [3], [4], [5], [6], [7], [8], [9], [10], [11], [12], [13], [14], [15], [16], [17], [18], [19], [20],
          [21], [22], [23], [24], [25], [26], [27], [28], [29], [30], [31], [32], [33], [34], [35], [36], [37], [38],
          [39], [40], [41], [42], [43], [44], [45], [46], [47], [48], [49], [50], [51], [52], [53], [54], [55], [56],
          [57], [58], [59], [60], [61], [62], [63], [64], [65], [66], [67], [68], [69], [70], [71], [72], [73], [74],
          [75], [76], [77], [78], [79], [80], [81], [82], [83], [84], [85], [86], [87], [88], [89], [90], [91], [92],
          [93], [94], [95], [96], [97], [98], [99], [100]]
z_axis = [[10], [13], [16], [19], [22], [25], [28], [31], [34], [37], [40], [43], [46], [49], [52], [55], [58], [61],
          [64], [67], [70], [73], [76], [79], [82], [85], [88], [91], [94], [97], [100], [103], [106], [109], [112],
          [115], [118], [121], [124], [127], [130], [133], [136], [139], [142], [145], [148], [151], [154], [157],
          [160], [163], [166], [169], [172], [175], [178], [181], [184], [187], [190], [193], [196], [199], [202],
          [205], [208], [211], [214], [217], [220], [223], [226], [229], [232], [235], [238], [241], [244], [247],
          [250], [253], [256], [259], [262], [265], [268], [271], [274], [277], [280], [283], [286], [289], [292],
          [295], [298], [301], [304], [307]]
z_tf_axis = []

batch_set = [[100, 98], [100, 96], [100, 65], 省略, [1, 47], [1, 45], [1, 43]]

Excel_data = [[405], [403], [372], 省略 , [57], [55], [53]]


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

def loss(output, actual_data):
    loss = 1 / 2 * tf.reduce_sum(tf.math.square(actual_data - 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():
    input_data = tf.placeholder("float", [None, 2])
    actual_data = tf.placeholder("float", [None, 1])
    output = inference(input_data)
    cost = loss(output, actual_data)
    global_step = tf.Variable(0, 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_xy = np.array(batch_set[i:i + batch_size])
            minibatch_z = np.array(Excel_data[i:i + batch_size])
            sess.run(train_op, feed_dict={input_data: minibatch_xy, actual_data: minibatch_z})
            avg_cost += sess.run(
                cost, feed_dict={input_data: minibatch_xy, actual_data: minibatch_z}
            )

        if epoch % display_step == 0:
            print("Epoch: {:04d} cost: {:.9f}".format(epoch + 1, avg_cost))
        print("Optimization Finished!")

    for var in range(1, 101):
        result = sess.run(output, feed_dict={input_data: [[var, 0]]})
        z_tf_axis.append(result[0])

    mp.plot(x_axis, z_axis, color="red")
    mp.plot(x_axis, z_tf_axis, color="orange")
    mp.grid()
    mp.show()

ここで、 batch_setは(x, y)のセット、Excel_dataは方程式から求めたzの値。
inference(input_data)で、batch_setを入力して、zの推定の値outputを求める(output = w1 * x + w2 * y + b)。一度に入れるデータはbatch_sizeの分だけ、つまり、batch_sizeが5なら、(x, y)を5セット入れ、5つのzの値を得る。その後、loss(output, actual_data)で、そのoutputの値とExcel_data(zの真値)との差異を求める。5つのzの値を得る場合、それぞれの差異の総計をloss(cost)とする。そのlossが小さくなるように training(cost, global_step)の勾配降下法を動かしていく。
データ数は1000としているため、batch_sizeが5の場合、200回繰り返し、総計のloss(avg_cost)を求める。
この手順をtraining_epochsの数だけ繰り返して学習を進めていく。

最後に、y=0の時(z=3x+7)のx, zの結果と、tensorflowで得られるx, zの結果をグラフにして比較した。
learning_rate, training_epochs, batch_sizeを何度か変えながら、結果を見てみると、learning_rate = 0.00001, training_epochs = 100, batch_size = 10で下のような結果となった(赤が方程式、オレンジがtensorflow)。

ぱっと見いい感じで近似されているように見える。ただ、ちょっとずれが起こっているのが気になる。使用したデータは方程式から作っており、現実にはあり得ないほど精度が高いものなのに...。
このときの学習の損失を見てみると、12回目で0.506と小さくなり、それ以降は少し増え100回目まで0.606あたりで増減を繰り返す結果となった。学習自体は12回までで十分ということなのかな。

検証には十分使えそうなので、もととなるデータの精度を悪いものにしたり、learning_rate, training_epochs, batch_sizeを変えたりしながら、結果がどうなるか見ていってみようか。