はじめに
中井悦司著の『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)から確認できます。
実行環境には、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 $$
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公式の こちら のコードを参考にしました。Low-level API を使って・・・がコンセプトなのですが Keras が含まれてしまいました。
%tensorflow_version 2.x
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$ を使って近似曲線を描きます。
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('気温(℃)')
問題点
- こちらの TF 1.x 対応のコードよりも、実行速度が遅い。時間がかかる。