Python(基本的なこと014: Tensorflow1.15と最小2乗法)

■サンプルデータに対してTensorflowを試す
まとめとして最小2乗法のサンプルデータに対してTensorflowを使ってみる。
データは下のようなもの。30人に対して100点を満点とする数学と国語のテストを行ったという想定。

番号数学国語
16183
23638
37575
46484
56675
64031
74261
88798
978100
105469
115453
125746
135852
146071
155430
166352
176588
186788
196334
206949
214433
227574
238484
248488
258498
266538
276751
289035
299791
3010077

データ自体は、少し相関するように作成した。プロットしてみると下のような感じ(数学をx軸、国語をy軸とする)。

まず最小2乗法の推定の式を求める。
ExcelのINTERCEPT関数(切片)とSLOPE関数(傾き)が使用できそうなので、数学をx、国語をyとして上のデータを入れると次の式が得られる。

y = 0.824193751 * x + 9.837997239

この式を上の図にのせてみる(x = 3, 6, 9, 12 ~ 90とこれらの値を上の式に入れたときのyをプロットして線を引く)。点の傾向と直線を見るとそれらしい式と分かる。

また、最小2乗法は、この推定式と実際の点との差の合計(ここでは30人分の合計)を最小にするものであり、上の表と式から合計を求めると4995.302798となった(何となく合計を1/2しているけど)。これを求める式をtensorflowの損失関数とする。

では、tensorflowを使ってみる。コードは一番下に記載。
推定式、損失関数は下の通り。

損失関数が最も小さくなるように、学習率(learning_rate)の数値に応じて少しずつ、学習回数(training_epochs)の分だけ探っていく。learning_rate = 0.00001で実行すると、training_epochs 35万回あたりで損失関数の値が4995.303222656となり、それ以降は安定した。
tensorflowから得られたグラフは下の通り(オレンジ色)。

最小2乗法で得られたものと重なっていることが分かる。
損失関数の値はわずかに最小2乗法のものと異なるので、細かくみていけば(グラフ上では見えないぐらいの)若干のずれはあるだろうけど、tensorflowを使って最小2乗法を再現することができた。

今回までtensorflow v1.15を使用しているけど、すでにv2が出ており仕様も変わっているそう。v1.15についてはこれで一端終わりにする。いまだにlearning_rate, training_epochsの値はどうするのが適切なのかよく分からないけど、tensorflowのHelloworldレベルのものはできたと思う。

以下、コード。

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

learning_rate = 0.00001
training_epochs = 500000
batch_size = 30
display_step = 1000

x_axis = [[3], [6], [9], [12], [15], [18], [21], [24], [27], [30], [33], [36], [39], [42], [45], [48], [51], [54], [57],
          [60], [63], [66], [69], [72], [75], [78], [81], [84], [87], [90]]
# 最小2乗法で得た式に上のx_axisのデータを入れて得た値。x_axisとz_axi
y_axis_leastsqaure = [[12.31057849], [14.78315974], [17.255741], [19.72832225], [22.2009035], [24.67348475],
                      [27.14606601],
                      [29.61864726], [32.09122851], [34.56380976], [37.03639102], [39.50897227], [41.98155352],
                      [44.45413477],
                      [46.92671602], [49.39929728], [51.87187853], [54.34445978], [56.81704103], [59.28962229],
                      [61.76220354],
                      [64.23478479], [66.70736604], [69.1799473], [71.65252855], [74.1251098], [76.59769105],
                      [79.07027231],
                      [81.54285356], [84.01543481]]
y_axis_tf = []

# データ
math_point_data = [[61], [36], [75], [64], [66], [40], [42], [87], [78], [54], [54], [57], [58], [60], [54], [63], [65],
                   [67],
                   [63], [69], [44], [75], [84], [84], [84], [65], [67], [90], [97], [100]]
JapLang_point_data = [[83], [38], [75], [84], [75], [31], [61], [98], [100], [69], [53], [46], [52], [71], [30], [52],
                      [88],
                      [88], [34], [49], [33], [74], [84], [88], [98], [38], [51], [35], [91], [77]]

##############################################################################################################
##############################################################################################################
# tensorflowの部分
def inference(input_data):
    init = tf.constant_initializer(value=0)
    W = tf.get_variable("W", [1, 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, 1])
    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(math_point_data) / batch_size)
        for i in range(0, total_batch, batch_size):
            minibatch_xy = np.array(math_point_data[i:i + batch_size])
            minibatch_z = np.array(JapLang_point_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(3, 91, 3):
        result = sess.run(output, feed_dict={input_data: [[var]]})
        y_axis_tf.append(result[0])
    ##############################################################################################################
    ##############################################################################################################

    # 検証のためのプロット
    mp.plot(x_axis, y_axis_leastsqaure, color="red")
    mp.plot(x_axis, y_axis_tf, color="orange")
    mp.scatter(math_point_data, JapLang_point_data, marker='.')
    mp.grid()
    mp.show()