社畜エンジニア発掘戦線

駆けだしAIエンジニア

③線形回帰問題(損失関数の可視化)

第三問


今回も線形回帰問題ですが、イテレーションごとの損失量の推移に着目したいと思います。前回の最後にちょろっと書きましたが、まだパラメータの数が多くないのでいろいろとグラフ化して視覚的に変化を追うことができます。最初にトライしたバイアスなしの問題をベースに可視化問題にチャレンジしてみます。

参照問題 -> ①線形回帰問題(データフィッティング) - 社畜エンジニア発掘戦線

今回もPYTHONISTA3でコードを書きながら解いていってみましょう。

設問1.ガウシアンノイズを付加したy = 3xに準ずるデータセットを作成し、バイアスなしでフィッティング直線を出力せよ

1回目の問題と同じです、ガバっとコピペしちゃいましょう。

import numpy as np
import matplotlib.pyplot as plt

#dataset
num_of_sam = 20
x = np.random.uniform(0, 2, num_of_sam)
noise = np.random.normal(0, 0.2, num_of_sam)
t = 3*x+noise

#initial_setting
w = 1
learning_rate = 0.004
E_save = []

#iteration
num_of_itr = 25
for i in range(num_of_itr):
    y = w*x
    E = np.sum((y-t)**2)
    E_save = np.append(E_save, E)
    w = w - learning_rate*2*np.sum(x*(y-t))

#plot
X_line = np.linspace(0, 2, 5)
Y_line = w*X_line

plt.plot(x, t, 'o')
plt.plot(X_line, Y_line)
plt.show()

f:id:sutokun:20190204033844p:plain
既にやっているので大丈夫ですね。

設問2.パラメータ:重み(w)に対する損失量の推移をグラフにプロットし、最小値となるwを求めよ

重みはイテレーションごとに都度更新されますが、点ではなく広い範囲でwを変化させて損失量がどのように推移するかを可視化します。損失関数にはシグマが含まれているのでwの関数としてさらっと書くのは困難です。wを繰り返しひとつずつ変化させてそれぞれの損失量を求めていきましょう。繰り返しと言えばfor文ですね。

まずは変化させるw(weight:x軸)とそれに対応する損失量(E_cost_weight:y軸)を保存するリストを設定しておきましょう。

weight = []
E_loss_weight = []

次にweightを何点(num_of_w)刻むか、どれぐらい細かく刻むか(resolution)を設定します。今回は0から10まで0.1ずつ45点刻めるように設定しましょうか。

num_of_w = 45
resolution = 0.1

ではforの中身を書いていきましょう。forが1回計算するごとに損失量を計算し、その時のweightとE_loss_weightを保存していきます。

for j in range(num_of_w):
    y_weight = (j*resolution)*x
    E_weight = np.sum((y_weight-t)**2)

    #strage data
    weight = np.append(weight, j*resolution)
    E_loss_weight = np.append(E_loss_weight, E_weight) 

ではグラフ化してみます。

plt.plot(weight, E_loss_weight)
plt.show()

w=3.0で最小値をとる2次関数となりました。確かにy=3xをベースにデータセットを用意したので、最小値がw=3.0になるのは納得ですね。
f:id:sutokun:20190204033919p:plain
これで設問2は終了です。

設問3.設問2のグラフにイテレーションごとの損失量をプロットせよ

設問2で損失量をプロットしたグラフに、実際にイテレーションを回して得られる損失量がどう推移するのか同じグラフにプロットしてみましょう。

まず、同じグラフにプロットするために、イテレーションごとにパラメータ(w)も保存しなければいけません。設問1のコードに追記します、と言っても2行だけですが。

#initial value
w = 1
learning_rate = 0.02
E_loss = [] 
w_itr = [] #空のリストを用意

#iteration
num_of_itr = 10
for i in range(num_of_itr):
    y = w*x
    E = np.sum((y-t)**2)
    E_loss = np.append(E_cost, E) 
    w_itr = np.append(w_itr, w) #イテレーションごとのwを保存
    w = w - learning_rate*2*np.sum(x*(y-t))

では準備が整ったのでプロットしてみましょう。

num_of_w = 45
resolution = 0.1
plt.plot(w_itr, E_save, 'o')
plt.plot(weight, E_loss_weight)
plt.show()

f:id:sutokun:20190204033959p:plain
これを見ると、赤丸(イテレーションごとの損失量)が損失関数(二次曲線)上を転がって最小値(w=3.0)へ向かっていることが分かります。

これで設問3は終了です。

まとめ

これで第三問が完了しました。今回も最後にイテレーションごとに損失量が推移する様子をGIFアニメ化しました。(これはまだiPadでも耐えれる処理量)
f:id:sutokun:20190204034916g:plain
重みパラメータと損失量がどのような関係で推移していくのかを視覚的に捉えることは重要です。今後、損失量が収束せずに頭が爆発することもあると思いますが、そんなときに一度基本に立ち返って「どのように損失関数が収束するのか」を捉える手がかりになればと思います。

全体コード
github.com

次の問題
①非線形回帰問題(データフィッティング) - 社畜エンジニア発掘戦線

元の記事
PYTHONISTA3を使って機械学習(ディープラーニング) - 社畜エンジニア発掘戦線

Twitter
世界の社畜 (@sekai_syachiku) | Twitter