TensorFlowで高レベルAPIのEstimatorsを使ってみる

TensorFlowには、Layersのさらに上にEstimatorsなるものがあるらしい。
https://www.tensorflow.org/get_started/get_started_for_beginners

Estimatorsを使うと、Layersでやっていたような層の設定すらいらないらしい。

そこで、
https://qiita.com/cometscome_phys/items/95ed1b89acc7829950dd
でやっていたことを、Estimatorsを使ってやってみることにする。

# 2018年6月13日追記について
pandasを使わずにnumpy arrayでやる方法がわかったので、最後に追記した。

#バージョン
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$によって得られる二次関数を得ることが目的となる。
データを10点作っておく。

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

n = 10
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")
```

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

![Unknown.png](https://qiita-image-store.s3.amazonaws.com/0/246113/bb2c50d8-4795-0851-9644-8b8cb6c61694.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
を参照。

#データの加工
チュートリアルを見た感じ、Estimatorsで使えるような形でデータを持っていなければならないらしい。
そこで、pandasを使ってデータを保持してみる。
まず、多項式をインプットデータとするので、

```python: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とすると

```python:test.py
x0 = np.linspace(-2.0, 2.0, n)
k = 4
phi = make_phi(x0,n,k)
```
となる。
その時の教師データはy0である。

このデータを、pandasのDataFrame形式に変換する。


```python:test.py
import pandas as pd
col=["0","1","2","3"]
train_x = pd.DataFrame(phi,columns=col)
train_y = pd.Series(y0)
```
ここで、columnsをちゃんと文字として指定しないと以後のEstimatorsが動かなかったので、定義している。

# Estimatorsの設定

次に、Estimatorsのチュートリアルにしたがって、おまじないを書く。ここはあまりわかっていない。

```python:test.py
my_feature_columns = []
for key in train_x.keys():    
    my_feature_columns.append(tf.feature_column.numeric_column(key=key))
```

# グラフの構築
Estimatorsのグラフの構築は

```python:test.py
hiddens = [10]
estimator = tf.estimator.DNNRegressor(
    feature_columns=my_feature_columns,
    hidden_units=hiddens)
```
で良い。DNNRegressorはニューラルネットワークを用いた回帰のグラフを作るものである。
ここで、hiddensを[10]としているが、[10,10]とすれば隠れ層二層になる。

# 学習
学習を行うためには、データをグラフに供給する必要がある。どのようなデータを供給するかについてEstimatorsでは関数で与える必要がある。ここで、

```python:test.py
def get_input_fn(train_x,train_y, batch_size=128,num_epochs=None, shuffle=True):
  return tf.estimator.inputs.pandas_input_fn(
      x=train_x,
      y=train_y,
      num_epochs=num_epochs,
      batch_size=batch_size,
      shuffle=shuffle)
```

という関数を作っておく。
そして、

```python:test.py
estimator.train(input_fn=get_input_fn(train_x,train_y,batch_size=1), steps=2000)
```
とすれば、$2000$回トレーニングすることになる。ここで、batch_size=1としたので、
適当に一つずつ取ってきていることになる。batch_sizeを変えることで、ランダムバッチ学習を簡単に実行することができる。

さて、学習した結果をプロットしてみよう。インプットデータを入れた際のニューラルネットワークによる計算結果は、

```python:test.py
import itertools
pd = estimator.predict(input_fn=get_input_fn(test_x,test_y, num_epochs=1, shuffle=False))
predictions = list(p["predictions"] for p in itertool
s.islice(pd, n))
plt.plot(x0,y0)
plt.plot(x0,predictions)
plt.show()
```

![image.png](https://qiita-image-store.s3.amazonaws.com/0/246113/f78542c5-79c8-ddd8-80eb-9bc25506c89b.png)

となる。

以上から、Estimatorsを使えば、層の設定すらせずに簡単にニューラルネットワークを用いた機械学習を実行することができる。この他にも、分類問題なども簡単にできる。
そして、自分のオリジナルのネットワークを作りたければ、
https://www.tensorflow.org/get_started/custom_estimators
を見れば作れるはずである。

# 2018年6月13日追記
pandasを使わずにnumpy arrayを使う方法がわかったので、以下に追記する。
tf.estimator.inputs.numpy_input_fnを使えばよい。

具体的には、

```python:test.py
my_feature_columns = []
for key in col:    
    my_feature_columns.append(tf.feature_column.numeric_column(key=key))
x ={}
for i,key in enumerate(col):
    x[key] = phi[:,i]

def get_input_fn(train_x,train_y, batch_size=128,num_epochs=None, shuffle=True):
  return tf.estimator.inputs.numpy_input_fn(
      x=train_x,
      y=train_y,
      num_epochs=num_epochs,
      batch_size=batch_size,
      shuffle=shuffle)

estimator.train(input_fn=get_input_fn(x,y0,batch_size=1), steps=2000)

pd = estimator.predict(input_fn=get_input_fn(x,y0, num_epochs=1, batch_size=1,shuffle=False))
```
として、train_xのかわりにx、train_yのかわりにy0を使えばよい。
xはPythonの辞書型で、それぞれの名前に対してnumpy配列が定義されている。