のんびり肉体改造ブログ

30代社会人のトレーニング記録と雑記

ラビットチャレンジレポート(機械学習)

トレーニングにまつわるあれこれを掲載しているブログではありますが,都合によりこのような記事を掲載いたします.
現在一般社団法人ディープラーニング協会のE資格を取得するため,ラビットチャレンジなるプログラムを受講しています.本プログラムにおいてレポート公開が必須になっていることから,こちらのブログにてレポートします.

機械学習

線形回帰

複数の変数 x_i yの関係を以下のような式で表現したものを線形回帰と呼ぶ.

 y = a_0 + a_1 x_1 + a_2 x_2 + \dots + a_{n-1}  x_{n-1}

一般に以下のような行列の形で表現される.

 y = a^T x \\
where \quad
a=\begin{pmatrix}
a_{0}\\
a_{1}\\
\vdots\\
a_{n-1}
\end{pmatrix}
\quad
x=\begin{pmatrix}
x_{0}\\
x_{1}\\
\vdots\\
x_{n-1}
\end{pmatrix}

これを踏まえて,線形回帰モデルは以下のように表現される.

 \hat{y} = w^T x + w_0

以下のようなイメージ.パラメータxと重みwの内積でyは表現される.NNモデルの最終層と同じような印象.

f:id:CivilEng:20211102061534p:plain:w300

なお,真値 yとの誤差 εは以下のように表現される.

 ε = y - \hat{y}

xとyの組み合わせ(学習データ)と,重みwの関係は以下のように書ける

f:id:CivilEng:20211102062554p:plain:w300

この誤差を最小二乗法で最小化するような \hat{w}を探索する問題と整理することができる. 最小二乗法の式は以下.

f:id:CivilEng:20211102062937p:plain:w300

 \hat{w}は以下のように式展開して求められる.

f:id:CivilEng:20211102063024p:plain:w300

非線形回帰

複数の変数 x_iに基底関数を適用して,非線形化したものを非線形回帰と呼ぶ.線形回帰は恒等写像関数を適用したものと考えることができる.

 \displaystyle{y = w_0 + \sum_{j=1}^m w_j \varphi_j(x_i) + ε_i }

よく使われるのは,恒等写像関数(=線形回帰),

 \varphi_j = x

多項式,

 \varphi_j = x^j

ガウス型基底,

  \displaystyle{\varphi_j = exp{\frac{(x-\mu_j)^2}{2h_j}}}

ガウス型基底は多項式より柔軟な設定ができそうだが,調整するパラメータが多くて使うのが難しいそうな印象を受けた.

非線形回帰における過学習と未学習

表現力が不足していることで,学習データ・テストデータともに誤差が大きいことを未学習(underfitting)と呼ぶ. また,表現力が高すぎることで,学習データでは小さい誤差が得られるものの,テストデータに対しては誤差が大きい ことを,過学習(overfitting)と呼ぶ.

未学習に対する対策としては,

  • 基底関数を追加するなどして,表現力を高める

過学習に対する対策としては,

  • 学習データを増やす
  • 不要な基底関数を削減するなどして,表現力を抑制する
  • 正則化項を設ける

が挙げられる.正則化項Rは以下のように使われる.

 S_\gamma = (y - \Phi w)^T (y - \Phi w ) + \gamma R(w)

ただし \Phiはn行k列の基底関数

このとき,

 R(w) \leq r

をRidge推定量と呼ぶ.

ロジスティック回帰

ロジスティック回帰は分類問題を解くための機械学習モデル.線形結合をシグモイド関数に入力することで,0 or 1に分類する.
シグモイド関数は以下

 \displaystyle{\sigma (x) = \frac {1}{1+exp(-ax)}}

NNモデルの最終層に使われたりするので,馴染み深い.
ロジスティック回帰モデルは以下のように表現される.

 \displaystyle{P(Y=1|x) = \sigma (w_0 + w_1 x_1 + \dots + w_m x_m)}

このとき, x Yは既知で, wを求めることになる.
 wを求めるために,尤度関数を用いて,最尤推定を行う.すなわち,

f:id:CivilEng:20211108072535p:plain:w300

で,このようなpが得られるようなwを求める.最尤推定の詳細や勾配降下法は割愛.

主成分分析

主成分分析は多変量データの情報をより少数個の変数に圧縮する手法.

f:id:CivilEng:20211108075233p:plain:w300

f:id:CivilEng:20211108075311p:plain:w300

この線形変換する aについて,

 \newcommand{\argmax}{\mathop{\rm arg~max}\limits}
\argmax_{a \in \mathbb{R}^m} a_j^T Var(\bar{X})a_j

ただし,

 a_j^T a_j = 1

となる最適化問題を解けば良い.

K近傍法

割愛

K Means

これも割愛

SVM

SVMは分類問題で用いられる手法のひとつ.ここでは線形SVMについて述べる.
学習データが線形分離可能なときに,なるべくその距離が大きくなるように,2 つのクラスのデータを分離するような 2 つの平行な超平面を選択することができる.2 つの超平面の間はマージン,2 つの超平面の中間に位置する超平面は 最大マージン超平面と呼ぶ.また,マージン上のサンプルをサポートベクターと呼ぶ.

検証や最適化

検証

回帰モデルにおける検証方法として,ホールドアウト法とクロスバリデーションがある.
ホールドアウト法では有限のデータを学習用とテスト用の2つに分割し、「予測精度」や「誤り率」を推定する為に一度だけ使用する.
簡易な方法ではあるが,分割方法で性能評価がばらつき,データが大量にない場合は良い性能評価が得られない場合がある.
クロスバリデーションではデータをk分割して,k-1つを学習用,1つをテスト用として評価する行為をk回実施,その平均を性能評価とする.
分割による影響を抑えることができ,より一般的に利用されている検証方法.

最適化

ハイパーパラメータの最適化手法として,グリッドサーチやランダムサーチ,ベイズ最適化がある.

コード演習

以下にロジスティック回帰についてのコード演習を掲載する.scikit learnを使用した実装は実施経験があるので,今回はnumpyでの実装について確認した.

まずはimport

%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt

つぎに訓練データを生成するためのコード.
gen_dataは標準偏差を使って,平均0・分散1のデータと,平均1・分散1のデータを生成する関数.

n_sample = 100
half_n_sample = 50
var = .2

def gen_data(n_sample, half_n_sample):
    x0 = np.random.normal(size=n_sample).reshape(-1, 2) - 1.
    x1 = np.random.normal(size=n_sample).reshape(-1, 2) + 1.
    x_train = np.concatenate([x0, x1])
    y_train = np.concatenate([np.zeros(half_n_sample), np.ones(half_n_sample)]).astype(np.int)
    return x_train, y_train

def plt_data(x_train, y_train):
    plt.scatter(x_train[:, 0], x_train[:, 1], c=y_train, facecolor="none", edgecolor="b", s=50, label="training data")
    plt.legend()

gen_dataを使ってデータ生成.グラフで生成データを確認.

#データ作成
x_train, y_train = gen_data(n_sample, half_n_sample)
#データ表示
plt_data(x_train, y_train)

いい感じにばらついている.

f:id:CivilEng:20211109075209p:plain:w300

定数項 w_0に合わせて1で埋めた行列を追加する関数と,シグモイド関数,最適化手法としてSGD(最急降下法)を準備.

def add_one(x):
    return np.concatenate([np.ones(len(x))[:, None], x], axis=1)

def sigmoid(x):
    return 1 / (1 + np.exp(-x))

def sigmoid(x):
    return 1 / (1 + np.exp(-x))

def sgd(X_train,  y_train, max_iter, eta):
    w = np.zeros(X_train.shape[1])
    for _ in range(max_iter):
        w_prev = np.copy(w)
        sigma = sigmoid(np.dot(X_train, w))
        grad = np.dot(X_train.T, (sigma - y_train))
        w -= eta * grad
        if np.allclose(w, w_prev):
            return w
    return w

そして学習開始.学習率は0.01,イテレーションは100.

X_train = add_one(x_train)
max_iter=100
eta = 0.01
w = sgd(X_train, max_iter, eta)

最後にテストデータを作って学習結果を可視化する.
meshgridで-5~5の範囲で,10000サンプル生成.
probabilityを計算して,0.5より大きいのものを1,0.5以下を0とした.

xx0, xx1 = np.meshgrid(np.linspace(-5, 5, 100), np.linspace(-5, 5, 100))
xx = np.array([xx0, xx1]).reshape(2, -1).T
X_test = add_one(xx)
proba = sigmoid(np.dot(X_test, w))
y_pred = (proba > 0.5).astype(np.int)

下記でコンター作成.

plt.scatter(x_train[:, 0], x_train[:, 1], c=y_train)
plt.contourf(xx0, xx1, proba.reshape(100, 100), alpha=0.2, levels=np.linspace(0, 1, 3))

f:id:CivilEng:20211109112256p:plain:w300

scikit learnで実行した結果が以下.同じ結果が得られていることを確認した.
ちなみに学習データに対する正解率は0.92

from sklearn import metrics
proba = sigmoid(np.dot(X_train, w))
y_pred = (proba > 0.5).astype(np.int)
metrics.accuracy_score(y_train, y_pred)
>>0.92
from sklearn.linear_model import LogisticRegression
model=LogisticRegression(fit_intercept=True)
model.fit(x_train, y_train)
proba = model.predict_proba(xx)
y_pred = (proba > 0.5).astype(np.int)
plt.scatter(x_train[:, 0], x_train[:, 1], c=y_train)
plt.contourf(xx0, xx1, proba[:, 0].reshape(100, 100), alpha=0.2, levels=np.linspace(0, 1, 3))

f:id:CivilEng:20211109113914p:plain:w300