TensorFlow 2.x の Low-level API を使って多項式近似を求める

# はじめに

中井悦司著の『TensorFlowとKerasで動かしながら学ぶ ディープラーニングの仕組み ～畳み込みニューラルネットワーク徹底解説～』の勉強メモです。

この書籍の第1章では、TensorFlow の **Low-level API** を使って、**最小2乗法による多項式近似曲線を求める内容**を扱っています。ただ、そこに掲載されているコードが TensorFlow 1.x 対応版だったので、それを **TensorFlow 2.x 対応**に書き直してみました（なお、同書籍の第2章以降は、TF 2.x 対応のコードになっています）。

書籍に掲載の TensorFlow 1.x 対応のコードは、著者のGitHub（[https://github.com/enakai00/colab_tfbook/tree/tf2.0/Chapter01](https://github.com/enakai00/colab_tfbook/tree/tf2.0/Chapter01)）から確認できます。

実行環境には、Google Colab. を利用しました。

## TF Low-level API を使ってやりたいこと

次のように、月別の平均気温が与えられているとき、月を変数 $x$ とした **4次の多項式近似曲線**のパラメータ（$w_0,w_1,w_2,w_3,w_4$）を数値的に求めるという内容です。

$$ f(x) = w_0 + w_1x + w_2x^2 + w_3x^3 + w_4x^4 $$


![fig.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152805/e3278888-737a-2477-d295-9a03215f0682.png)

> temp = [5.2, 5.7, 8.6, 14.9, 18.2, 20.4, 25.5, 26.4, 22.8, 17.5, 11.1, 6.6]

# サンプルコードを、そのまま TF 2.x 環境で実行してみる。失敗。

案外、そのままでも動くんじゃないかと思って、上記のコードをコピーして、Google Colab. の TF 2.x 環境で実行してみました。

その結果、次のようなエラーが発生。

> x = tf.placeholder(tf.float32, [None, 5])
> AttributeError: module 'tensorflow' has no attribute 'placeholder'

上記のエラーをヒントに色々と試行錯誤しましたが、ちょっとした改変では対応できそうもない、という結論になりました。

# TF 2.x にあわせて大幅に書き替え

書き替えは、TF公式の [こちら](https://www.tensorflow.org/guide/eager#variables%E3%81%A8%E3%82%AA%E3%83%97%E3%83%86%E3%82%A3%E3%83%9E%E3%82%A4%E3%82%B6) のコードを参考にしました。**Low-level API** を使って・・・がコンセプトなのですが Keras が含まれてしまいました。

```:GoogleColab.でTF2.xに切り替えるマジックコマンド
%tensorflow_version 2.x
```

```py:4次の近似曲線のパラメータを求める
import tensorflow as tf
import numpy as np

# f(x) = w0 + w1*x + w2*x^2 + w3*x^3 + w4*x^4 
class Model(tf.keras.Model):
  def __init__(self):
    super(Model, self).__init__()
    self.w = tf.Variable(tf.zeros([5, 1]), name='w') # w0,w1,..,w4
  def call(self, inputs):
    return tf.matmul(inputs, self.w)

# トレーニングデータ（入力）
train_x  = np.array([[mon**n for n in range(0, 5)] for mon in range(1, 13)])
train_x =train_x.astype(np.float32)
#print(train_x.shape) # 実行結果 -> (12,5)

# トレーニングデータ（正解値）
train_t = np.array([5.2, 5.7, 8.6, 14.9, 18.2, 20.4, 25.5, 26.4, 22.8, 17.5, 11.1, 6.6])
train_t =train_t.reshape([12, 1]) 
#print(train_t.shape) # 実行結果 -> (12, 1)

# 損失関数（誤差の2乗和）
loss = lambda model, x, t : tf.reduce_sum(tf.square(model(x)-t))

# 傾きを計算
def grad(model, x, t):
  with tf.GradientTape() as tape:
    loss_value = loss(model, x, t)
  return tape.gradient(loss_value, [model.w])

model = Model()
optimizer = tf.keras.optimizers.Adam()

# 学習ループ
print(f'Initial Loss: {loss(model, train_x, train_t):.5f}')
i = 0
for _ in range(10000):
  i += 1
  grads = grad(model, train_x, train_t)
  optimizer.apply_gradients(zip(grads, [model.w]))
  if i % 1000 == 0:
    print (f'Step: {i}, Loss: {loss(model, train_x, train_t):.5f}')

print(f'Final loss: {loss(model, train_x, train_t):.5f}')
print(f'w = {model.w.numpy()}')
```

```:実行結果
Initial Loss: 3442.96973
Step: 1000, Loss: 206.62106
Step: 2000, Loss: 107.81633
Step: 3000, Loss: 53.71612
Step: 4000, Loss: 37.42331
Step: 5000, Loss: 33.97096
Step: 6000, Loss: 33.05221
Step: 7000, Loss: 32.35994
Step: 8000, Loss: 31.79436
Step: 9000, Loss: 31.35630
Step: 10000, Loss: 31.01644
Final loss: 31.01644
w = [[ 1.3081207e+00]
 [ 1.4444064e+00]
 [ 8.0244839e-01]
 [-8.5857309e-02]
 [ 9.1365178e-04]]
```

求められたパラメータ $w$ を使って近似曲線を描きます。

```py:近似曲線付きのグラフを描く
import japanize_matplotlib
import matplotlib.pyplot as plt

w = model.w.numpy().flatten()
predict = lambda x : np.dot(np.array([x**n for n in range(0, 5)]),w)

plt.figure(dpi=96)
plt.scatter(range(1, 13), train_t)
xs = np.linspace(1, 12, 100)
ys = [ predict(x) for x in xs ]
plt.plot(xs, ys)
plt.xlim(1, 12)
plt.xticks(range(1, 13))
plt.xlabel('Month')
plt.ylabel('気温（℃）')
```
![ダウンロード.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152805/0995cc62-291c-b4a7-14ef-17cb92212c44.png)

# 問題点

- [こちら](https://github.com/enakai00/colab_tfbook/tree/tf2.0/Chapter01)の TF 1.x 対応のコードよりも、実行速度が遅い。時間がかかる。