2つの値の組の$n$個のデータがあるとする。$(x_1,y_1),\ (x_2, y_2),\ \cdots,\ (x_n,y_n)$
ここでは、$x$と$y$の関係が何かある関数$y=f(x)$で表されるとしてそれを探る問題を考えてみる。
準備として仮想的なデータを用意する。
$\sin x$の値に、ガウシアンノイズを載せたデータを返す関数make_data_by_sin_gaussian()を以下のように作成する。
import numpy as np
import random
def make_data_by_sin_gaussian(x):
# y=sin (x)を計算し、ガウス分布に従うノイズを加える
y = np.sin(x)
e = [random.gauss(0, 0.2) for i in range(len(y))]
# e = np.random.randn(len(x))*0.2
y += e
return y
# データ数
n_tr = 20
x = np.linspace(0., np.pi*2., n_tr)
y = make_data_by_sin_gaussian(x)
# データの描画
import matplotlib.pyplot as plt
plt.plot(x,y, "ob")
(注)
ノイズの部分
e = [random.gauss(0, 0.2) for i in range(len(y))]
は、numpyの乱数発生関数
e = np.random.randn(len(x))*0.2
でもよい。こちらの方が繰り返し記述がない分、シンプルかも。
データに潜む原理から、べき的な振る舞いを示しているものだと想定し、べき関数での近似を行ってみる。つまり、
$$ y(x, {\bf w}) = w_0 + w_1 x + w_2 x^2 + \cdots + w_M x^M = \sum_{j=0}^{M} w_j x^j $$置き、トレーニングデータ$\{(t_n, x_n)\}$, $n=1,\cdots, N$を使って、係数${\bf w} = (w_1,\ w_2,\ \cdots,\ w_n)$を 2乗誤差
$$ E({\bf w}) = \frac{1}{2} \sum_{n=1}^N \{y(x_n,{\bf w}) - t_n\}^2 $$を最小にするよう${\bf w}$を決める。
このことを最小2乗法と呼ぶ。
pythonのライブラリscikit-learnにあるlinear_modelを用いてみる
まずは、べき関数へのフィッティングをlinear_modelで行うために、$x$の値に対する各べきの値$x^1,\ x^2,\ x^3,\ \cdots,\ x^n$を行として並べた行列(Vandermonde行列)を作成しておく。
(注)データ分析に慣れている人向け説明:
$n$個の説明変数に対する係数$\{w_i\}$を線形重回帰(multiple linear regression)によって決めるということに相当する。
このことの必要性を理解するために、最初に社会的データの重回帰分析について説明する。
# トレーニングデータの数
n_tr = 10
x_tr = np.linspace(0., np.pi*2., n_tr)
# データの作成
y = make_data_by_sin_gaussian(x_tr) # データ
# 説明変数の作成 (x, x^2, ...x^m) 行列
m=1
power_matrix_x = np.vander(x_tr, m+1)
import sklearn.linear_model as lm
import matplotlib.pyplot as plt
# 関数フィッティング
lrp = lm.LinearRegression()
lrp.fit(power_matrix_x, y)
# 予測
x_lrp = np.linspace(0., np.pi*3.0, 100)
y_lrp = lrp.predict(np.vander(x_lrp, m+1))
# データの描画
plt.plot(x_tr, y, "ob", ms=8)
# 近似曲線の描画
plt.plot(x_lrp, y_lrp)
# 高次のべき多項式による近似
plt.plot(x_tr, y , 'ok', ms=7) # データの描画
for deg in [3,4]: # 複数の次数で近似
lrp.fit(np.vander(x_tr, deg +1), y)
y_lrp = lrp.predict(np.vander(x_lrp, deg+1))
plt.plot(x_lrp, y_lrp,
label='degree ' + str(deg))
plt.legend(loc=2)
plt.ylim(-1.2, 3.0)
# モデルの係数表示
#print(' '. join(['%.2f' % c for c in lrp.coef_]))
自然科学では、対象となる系のふるまいの裏にはあるシンプルな原理が存在していると考える。今の場合、その原理に基づけば系は$\sin x$のようにふるまうと想定されるとする。
自然科学の場合は、その原理に基づいて$\sin x$に振幅、位相を未定係数としておいて、その係数をデータから推定することを行う。
一方、原理がまったくわからない、あるいは、背後にある要素・要因が複雑で本質的に単純な原理で説明できないような対象の場合はどうであろうか。適用範囲を適切に設定すれば、べき関数であっても十分近似でき「予測」には有用であるといえる。
# もっと高次に
plt.plot(x_tr, y , 'ok', ms=7) # データの描画
for deg in [8,n_tr-1]: # 複数の次数で近似
lrp.fit(np.vander(x_tr, deg +1), y)
y_lrp = lrp.predict(np.vander(x_lrp, deg+1))
plt.plot(x_lrp, y_lrp,
label='degree ' + str(deg))
plt.legend()
plt.ylim(-1.2, 1.2)
べき関数の次数をデータの数と同じにすれば、原理的には全部のデータ点を通るようにフィッティングできる。が、それでよいか?
このようなフィッティングを「過学習」と呼ぶ。
過学習を防ぐために、高次の項を不必要に重視しないよう重みをつけたり次数を削減したりする方法が考えられている。Ridge回帰、Lasso回帰について次回学習する。
各自の経験に応じてでよいので、Jupyter Notebookで行ったことを記録し、html形式あるいはPDF形式にしてmoodleのページへ提出してください。
初めての人は、次のようなことをレポートしてもよい。
など
上でデータをmodelにあたえるときvanderという関数を使った。
中身を見ればわかるが、あるデータ値$x$に対して、べき関数フィッティングの場合、$x$, $x^2$, $\cdots$, $x^M$の値を横に並べ、各データを1行として、$N$個のデータを行として並べたものを与える。
このような行列を一般に、機械学習では計画行列(design matrix)とよぶ。
$$ y(x, {\bf w}) = w_1 \phi_1(x) + w_2 \phi_2(x) + \cdots + w_M \phi_M(x) $$のように$\phi_1(x)$~$\phi_M(x)$を用意して、その線形結合で目的変数を説明したいとき、 データが$\{x_1, x_2, \cdots, x_N\}$に対して計画行列は
$$ \Phi = \left( \begin{array}{cccc} \phi_1(x_1) & \phi_2(x_1) & \cdots & \phi_M (x_1) \\ \phi_1(x_2) & \phi_2(x_2) & \cdots & \phi_M (x_2) \\ \vdots & \vdots & & \vdots \\ \phi_1(x_N) & \phi_2(x_N) & \cdots & \phi_M (x_N) \end{array} \right) $$と定義される。
numpy.vander()はデータリストを与えると、これを一気に作ってくれる。(もちろんべきの基底の時のみだが。)