LoginSignup
12
14

More than 5 years have passed since last update.

TensorFlowの高レベルAPIの使用方法2:Dataset APIを使ってみる

Posted at

TensorFlowにデータを食べさせるには色々なやり方がある。ここでは、Dataset APIを使った方法
https://www.tensorflow.org/programmers_guide/datasets
を用いてみる。Dataset APIを使うと、ランダムバッチなどを簡単に取り扱うことができる。Dataset APIに慣れると、カスタムEstimatorsに慣れやすい、はず。

これまでずっとやってきている、
https://qiita.com/cometscome_phys/items/95ed1b89acc7829950dd
でやっていたことを、Dataset APIを使ってやってみることにする。

バージョン

TensorFlow: 1.8
Python: 3.6.5

再現すべき関数

ここでは、ある関数
$$
y = a_0 x+ a_1 x^2 + b_0 + 3cos(20x)
$$
という関数を考える。ここで、最後のcosはノイズのようなものとして考えており、$a_0$と$a_1$と$b_0$によって得られる二次関数を得ることが目的となる。
データを300点作っておく。

test.py
import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline

n = 300
x0 = np.linspace(-2.0, 2.0, n)
a0 = 3.0
a1 = 2.0
b0 = 1.0
y0 = np.zeros(n)
y0[:] = a0*x0+a1*x0**2 + b0 + 3*np.cos(20*x0)

plt.plot(x0,y0 )
plt.show()
plt.savefig("graph.png")

この時のグラフは以下の通りである。

image.png

上のデータをフィッティングする際には、
$$
y = \sum_{k=0}^{k_{\rm max}} a_k x^k + b_0
$$
という形を考える。ここでは、$x^k$を基底関数として線形回帰をしていることになる。
詳しくは、
JuliaでTensorFlow その4: 線形基底関数を用いた回帰
https://qiita.com/cometscome_phys/items/92dba9f82cd58d877ec5
を参照。

インプットデータの生成

ここはこれまでの記事と同じである。
多項式をインプットデータとするので、

test.py
def make_phi(x0,n,k):    
    phi = np.array([x0**j for j in range(k)])
    return phi.T

という形でインプットデータを生成することにする。ここで$n$はデータの数である。
最大の次数を$3$とし、データの範囲は-2から2とすると

test.py
x0 = np.linspace(-2.0, 2.0, n)
k = 4
phi = make_phi(x0,n,k)

となる。
その時の教師データはy0である。

また、学習には使わない「テストデータ」を100点用意し、

test.py
n_test = 100
x0_test = np.linspace(-2.0, 2.0, n_test)
y0_test = np.zeros(n_test)
y0_test[:] = a0*x0_test+a1*x0_test**2 + b0 + 3*np.cos(20*x0_test)
phi_test = make_phi(x0_test,n_test,k)

としておく。

Dataset APIの使用

さて、Dataset APIを使ってみよう。
まず、学習データはランダムバッチを食べさせることにする。このとき、datasetは

test.py
batch_size = 20
dataset = tf.data.Dataset.from_tensor_slices({"phi":phi, "y0":y0}).shuffle(10).batch(batch_size)

となる。tf.data.Dataset.from_tensor_slicesはnumpy arrayからDataset フォーマットに変換するものである。また、インプットに名前をつけている。
ここで、shuffle(10)の10はバッファーサイズらしいが、この数字の具体的な意味についてはわからなかった。最後のbatch(batch_size)というのは、データをbatch_size分束ねることを意味している。
同様に、テストデータに対しても、

test.py
test_dataset = tf.data.Dataset.from_tensor_slices({"phi":phi_test, "y0":y0_test}).batch(n_test)

としておく。テストデータはランダムに取る必要がなく、誤差評価には全体を使いたいのでn_testをバッチサイズとしている。

次に、iteratorと呼ばれる、TensorFlowにデータを供給する部分を定義しておく。

test.py
iterator = tf.data.Iterator.from_structure(dataset.output_types,
                                           dataset.output_shapes)

ここで、学習データにもテストデータにも対応できるように、構造だけを指定している。
さらに、

test.py
train_init_op = iterator.make_initializer(dataset)
test_init_op = iterator.make_initializer(test_dataset)

を定義しておく。この二つは、使うデータをスイッチする役目を持っており、例えば、sess.run(train_init_op)とすることで学習データを対象とすることをTensorFlowに教える。
そして、

test.py
next_step= iterator.get_next()

がsess.runで呼び出された時、Datasetに格納されたデータが吐き出される。これは、呼ぶたびに次のバッチを呼び出す。一度使ったデータは使わないようなので、何回も呼んでいるとデータが空になる。空になったとき、学習データをすべて使ったことになる。

グラフの構築

グラフの構築は
https://qiita.com/cometscome_phys/items/95ed1b89acc7829950dd
と同じように、tf.layersを使う。

test.py
def build_graph_layers_class(d_input,d_middle,d_type):
    x = next_step["phi"]
    yout= next_step["y0"]
    hidden1 = tf.layers.Dense(units=d_middle,activation=tf.nn.relu)
    x1 = hidden1(x)
    outlayer = tf.layers.Dense(units=1,activation=None)
    y = outlayer(x1)

    diff = tf.subtract(y,yout)
    loss = tf.nn.l2_loss(diff)
    minimize = tf.train.AdamOptimizer().minimize(loss)
    return x,y,yout,diff,loss,minimize,hidden1,outlayer

となる。ここで、以前ではplaceholderだった部分が、next_stepに変わっている。これは、上で定義したnext_stepからデータをもらっていることを意味している。最初の定義のときに名前をつけたことにより、ここで名前を指定してインプットデータを取り出すことができる。
feedしていないのでなんとなく気持ち悪いような気もするが、以下のように考えるとまだましかもしれない。
このTensorFlowのグラフは、
minimizeにはlossが必要、lossにはdiffが必要、diffにはyとyoutが必要...という形につながっており、youtはnext_step["y0"]が必要、next_stepはiterator.get_next()が必要、という形でDatasetとつながっている。つまり、食べさせるデータもグラフで入れた、ということになる。

隠れ層1としてそのユニットを10であるとして、グラフを作る。

test.py
d_type = tf.float32
d_input = k
d_middle = 10
x,y,yout,diff,loss,minimize,hidden1,outlayer= build_graph_layers_class(d_input,d_middle,d_type)

学習

最後に、学習を行う。コードは以下のようになる。

test.py
np = 1000
with tf.Session() as sess:
    tf.global_variables_initializer().run()
    for i in range(np):
        sess.run(train_init_op)
        while True:
            try:
                sess.run(minimize)
                #print(sess.run(next_step))
                losstrain = sess.run(loss)/batch_size
                #print(losstrain)


            except tf.errors.OutOfRangeError:
                break

        sess.run(test_init_op)
        losstest = sess.run(loss)/n_test
        if i % 100 is 0:
            print("i",i,"loss",losstest)
    W = sess.run(hidden1.weights)
    print(W)
    a = sess.run(outlayer.weights)
    print(a)
    sess.run(test_init_op)
    yt = sess.run(y)

plt.plot(x0_test,y0_test )
plt.plot(x0_test,yt )
plt.show()
plt.savefig("graph2.png")

ここで、sess.run(train_init_op)で学習データを呼ぶことを宣言している。その後は、sess.run(minimize)で学習を進めるが、これを呼ぶたびにDataset APIがphiからサイズ20のランダムバッチを作って供給してくれる。while Trueとエラー分岐は、データが空っぽになるまでsess.run(minimize)を繰り返すことを意味しており、エラーがでたらwhileループを抜ける。
次のsess.run(test_init_op)では、テストデータを呼ぶことを宣言している。そして、sess.run(loss)でlossを計算している。
ここまで行った後、次の学習ステップに進む。これを、全部でnp回繰り返す。

得られたグラフは

image.png

となる。

12
14
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
14