線形基底関数モデル② Fit_funcクラス

線形基底関数モデル② Fit_funcクラス

Fit_funcクラス

 前回記事では、入力変数 $x$ と目標データ $y$ の関係を基底関数 $\phi_k(x)$ の線形結合
 
\[f(x)=a_0\phi_0(x)+a_1\phi_1(x)+a_2\phi_2(x)+\cdots +a_{M-1}\phi_{M-1}(x)+a_M\]
によって近似するようなモデル(線形基底関数モデル)について学びました。今後、色々な基底関数を選びながら精度の検証を繰り返すことを考えると、任意の関数リストを与えて(平均2乗誤差を最小にする)近似関数を得られるようなクラス を実装しておいたほうがよさそうです (≫ Python におけるクラスの実装手順についてはこちらを参照してください)。

Fit_funcクラスの実装

 作成するクラスの概要は次のようになります。

・クラス名は Fit_func とします。
 (Python の慣例として、クラス名の先頭は大文字にします)

・インスタンスを生成するコンストラクタの引数には、入力データベクトル x と目標データベクトル y、および関数オブジェクトのシーケンスを渡します。

・入力ベクトル x, 目標ベクトル y および関数オブジェクトのシーケンスをデータ属性として付与します。

・以下のメソッドを実装します。
 Fit_func.params() : パラメータベクトルを取得
 Fit_func.line(x) : 配列 x に対する近似関数値を取得
 Fit_func.mse() : 平均2乗誤差の値を取得
 Fit_func.sd() : 標準偏差の値を取得

 関数オブジェクトのシーケンスとは、たとえば

  [np.sin, np.cos, np.log]

のような 関数オブジェクト を格納したリストのことです(タプルでもかまいません)。ここから任意の要素を取り出して引数 x を渡すことで初めて値が計算されるようになっています。

# https://python.atelierkobato.com/fitfunc/

# 線形基底関数モデル リストM3-B-1

# モジュールをインポート
import numpy as np
import matplotlib.pyplot as plt

#★★★★★★★★★★

# Fit_funcクラスを定義
class Fit_func:
    def __init__(self, x, y, f):
        self.xv = x
        self.yv = y
        self.bfunc = f

    def params(self):
        a = len(self.bfunc) #基底関数の数
        b = self.xv.shape[0]  #入力ベクトルxvの行数
        m1 = np.zeros((a, b))  #a×bサイズの0行列を定義
        for k in range(a):  #Φの転置行列を作成
            m1[k,:] = self.bfunc[k](self.xv)
        m2 = m1.dot(m1.T)
        m3 = np.linalg.inv(m2)
        m4 = m3.dot(m1)
        p = m4.dot(self.yv.T)  #パラメータベクトルを計算
        return p

    def line(self, xv2):
        prm = self.params()  #params()メソッドで得たパラメータベクトル
        s = len(self.bfunc)  #基底関数の数
        f = np.zeros(len(xv2))  #要素が0の配列
        for k in range(s):  #xv2に対する近似関数値を計算
            f += prm[k] * self.bfunc[k](xv2)
        return f

    def mse(self):
        delta = self.yv - self.line(self.xv)
        return np.mean(delta ** 2)

    def sd(self):
        s = np.sqrt(self.mse())
        return s

 これで Fit_func クラスを実装完了です。
 

Fit_funcクラスのインスタンスを生成

 前回と同じ基底関数 $x^2,\ x,\ 1$ を用意して同じ結果を得られるかを確認しておきましょう。すなわち 2次関数
 
\[f(x)=ax^2+bx+c\]
を近似曲線として採用し、パラメータベクトル $(a,b,c)$ を決定します。

# https://python.atelierkobato.com/fitfunc/

# 線形基底関数モデル リストM3-B-2

# 入力データベクトル
x = np.array([35, 16, 22, 43, 5,
              66, 20, 13, 52, 1,
              39, 62, 45, 33, 8,
              28, 71, 24, 18, 3])

# 目標データベクトル
y = np.array([85.19, 58.93, 64.27, 68.91, 21.27,
              68.88, 60.07, 55.18, 88.08, 8.89,
              82.31, 81.18, 80.76, 78.98, 37.55,
              75.9, 72.39, 69.51, 62.04, 12.47])

#★★★★★★★★★★

# 基底関数を準備
def phi_0(x):
    return x ** 2

def phi_1(x):
    return x

def phi_2(x):
    return 1

# 基底関数のリスト
bf = [phi_0, phi_1, phi_2]

# Fit_funcオブジェクトを作成
z = Fit_func(x, y, bf)

#★★★★★★★★★★

# params()メソッドで最適化されたパラメータを取得
prm = np.round(z.params(), 3)
print("p = {}".format(prm))

# mse()メソッドで平均2乗誤差を取得
mse = np.round(z.mse(), 3)

# sd()メソッドで標準偏差を取得
sd = np.round(z.sd(), 3)

print("MSE = {}, SD = {}".format(mse, sd))

#★★★★★★★★★★

# Figureを作成
fig = plt.figure(figsize = (8, 6))

# FigureにAxesを1つ追加
ax = fig.add_subplot(111)

# Axesのタイトルを設定
ax.set_title("Age vs. Weight", fontsize = 16)

# 目盛線を表示
ax.grid()

# 軸範囲を設定
ax.set_xlim([0, 70])
ax.set_ylim([0, 100])

# 軸ラベルを設定
ax.set_xlabel("Age", fontsize = 14)
ax.set_ylabel("Weight [kg]", fontsize = 14)

# 身長と体重データの散布図
ax.scatter(x, y, color = "blue")

# line()メソッドを用いて近似曲線を描画
x2 = np.linspace(0, 70, 100)
y2 = z.line(x2)
ax.plot(x2, y2, color = "red")

plt.show()
p = [-0.034  3.228 10.083]
MSE = 32.227, SD = 5.677

線形基底関数モデル 年齢と体重の多項式近似