はじめに
今回は下記サイトの記事を見ていきます。
qiita.com
最急降下法(Gradient Descent)のみとなります。
データ
dataは下記サイトから100件
https://raw.githubusercontent.com/pandas-dev/pandas/master/pandas/tests/data/iris.csv
# 2 クラスにするため、setosa, versicolor のデータのみ抽出 data = iris[:100] # 説明変数は 2つ = 2 次元 columns = ['PetalWidth', 'PetalLength'] x = data[columns] # データ (説明変数) y = data['Name'] # ラベル (目的変数)
PetalLength,PetalWidth,Name 1.4,0.2,Iris-setosa 1.4,0.2,Iris-setosa 1.3,0.2,Iris-setosa ︙ 4.7,1.4,Iris-versicolor 4.5,1.5,Iris-versicolor 4.9,1.5,Iris-versicolor
ロジスティクス回帰なので、0 と 1 の2値にします。
y = (y == 'Iris-setosa').astype(int)
PetalLength,PetalWidth,y 1.4,0.2,1 1.4,0.2,1 1.3,0.2,1 ︙ 4.7,1.4,0 4.5,1.5,0 4.9,1.5,0
ロジスティック回帰
def p_y_given_x(x, w, b): # x, w, b から y の予測値 (yhat) を計算 def sigmoid(a): return 1.0 / (1.0 + np.exp(-a)) return sigmoid(np.dot(x, w) + b)
ロジスティック回帰は、以下の数式で表現できます。
シグモイド関数 と同じ形であり、内に重回帰を入れた形式となります。
今回のプログラムは、 で計算となり、上記式を変数に合わせて変更したのが下記の数式となります。
- : 真のラベル y の予測値ベクトル。次元は (入力データ数, 1)
- : シグモイド関数。計算結果を (0, 1) 区間に写像する
- : 入力データ。次元は (入力データ数, 説明変数の数)
- : 係数ベクトル。次元は(クラス数 = 2)
- : バイアス (スカラー)
内積
np.dot(x, w) は、内積です。
、 として次の例を考えます。
、
この場合、次の式の計算を行います。
勾配計算
勾配(gradient) を計算します。
def grad(x, y, w, b): # 現予測値から勾配を計算 error = y - p_y_given_x(x, w, b) w_grad = -np.mean(x.T * error, axis=1) b_grad = -np.mean(error) return w_grad, b_grad
np.mean は平均を求めます。axis=1とすると、行ごとに平均値を計算します。
x.Tは転置行列にします。これにより100行2列から2行100列になります。
- : 誤差 … y の値は 0 から 1 であり、 p_y_given_x関数の戻り値は0.0~1.0の値を返します。
- :重みの勾配。100個分に対し重みを計算します。
- : バイアスの勾配。切片なので変数は1つです。
データ件数が多くなると件数に比例して値が大きくなってしまうので平均を取ることでデータ件数の影響をなくします。
最急降下法(Gradient Descent)
def gd(x, y, w, b, eta=0.1, num=100): for i in range(1, num): # 入力をまとめて処理 w_grad, b_grad = grad(x, y, w, b) w -= eta * w_grad b -= eta * b_grad e = np.mean(np.abs(y - p_y_given_x(x, w, b))) yield i, w, b, e
- : 学習係数 です。
- : 勾配w_gradを用いて現在の重み を更新します。
- : 勾配b_gradを用いて現在の重み を更新します。
- : 誤差の平均です。一般的な2乗誤差ではないです。
yieldを使用してジェネレータを実行し、勾配法 1ステップごとの結果を得てアニメーションとして生成しています。