u = w1 * x1 + w2 * x2 + w3 * x3 + w4 * x4 + w5
u = W・X
上記のように仮定します。前回までの説明のように重みベクトルWとデータXの内積で表せます。
f(u) = 1 / (1 + e ** -u)
確率関数化するのにシグモイド関数を使います。eはネイピア数で高校の数学の教科書の後ろのほうに表が載っていた自然対数の底で2.7くらいの定数で円周率みたいなものと思ってもらっていいです。シグモイド関数の特徴は以下の通りでこれは境界関数をシグモイド関数にいれて確率値化しても矛盾が起きないことが分かると思います。
uが増えればf(u)が増える。
uが増えれば増えるほどf(u)は1に近づく。
uが減れば減るほどf(u)は0に近づく。
u=0のときf(u)=0.5。
u=0の点について点対称。
u = W・X
f(u) = 1 / (1 + e ** -u)
yp = 1 / (1 + e ** -W・X)
P(yt, yp) = yp #yt = 1のとき
1 - yp #yt = 0のとき
L = P0 * P1 * P2...
データの確率はすべてのデータの確率の積なので上記の式になる。log(L) = log(P0 * P1 * P2...)
= log(P0) + log(P1) + log(P2)...
Lの対数関数log(L)を対数尤度関数といい、損失関数に使います。対数をとることでPの足し算になりパラメータの次数が増えないことと数値が大きくなり精密な計算機でなくても計算できるようになります。P(yt, yp) = yt * yp + (1 - yt) * (1 - yp)
yt = 1のときyt = 0のときでは計算できないので上記のようにまとめます。log(P0) = yt0 * log(yp0) + (1 - yt0) * log(1 - yp0)
例えばlog(P0)は上記のようになりデータ数分足したものが対数尤度関数になります。
L = -1 / M (log(P0) + log(P1) + log(P2)... + log(P(M-1)))
log(P) = yt * log(yp) + (1 - yt) * log(1 - yp)
z = -1 * (yt * log(yp) + (1 - yt) * log(1 - yp))
上記のようにzとして定義します。
dz / dyp = -1 * (yt / yp + (1 - yt) / (1 - yp) * -1)
= -1 * ((yt * (1 - yp) + (yt - 1) * yp) / yp * (1 - yp))
= -1 * ((yt - yt * yp + yt * yp - yp) / yp * (1 - yp))
= -1 * ((yt - yp) / yp * (1 - yp))
= (yp - yt) / yp * (1 - yp)
dL/dw1 = dL/dyp * dyp/du * du/dw1
= (yp - yt) / yp(1 - yp) * yp * (1 - py) * x1
= (yp - yt) * x1
from sklearn import datasets
from sklearn.model_selection import train_test_split
iris = datasets.load_iris()
x = iris.data
y = iris.target
idx = np.where((y == 0) | (y == 1))
x = x[idx]
yt = y[idx]
train_x, test_x, train_y, test_y = train_test_split(x, yt, test_size=0.3)
scikit-learnのパッケージをインストールして同梱されているiris datesetを読み込んでいます。
idx = np.where((y == 0) | (y == 1))
はiris datasetに3種類のアヤメのデータが入っているので2種類のアヤメんのデータのインデックスをとってデータを抽出しています。
train_test_splitはデータをシャッフルして指定した割合で学習データとテストデータに分けてくれる関数です。
def pred(x, w):
u = x @ w
return sigmoid(u)
予測式です。境界式をシグモイド関数にいれてます。
def sigmoid(u):
return 1 / (1 + np.exp(-u))
シグモイド関数です。
def accuracy_rate(w, x, y):
yp = pred(x, w)
result = np.where(yp > 0.5, 1, 0)
diff = (test_y == result)
correct = diff[np.where(diff == True)]
return (correct.size / diff.size) * 100
重みwとテストデータxでどちらのアヤメが推定してテストの正解yと答え合わせをして正解率を出してます。
残りは前回のコードと全く同じなので説明を割愛。import numpy as np
from sklearn import datasets
from sklearn.model_selection import train_test_split
def sigmoid(u):
return 1 / (1 + np.exp(-u))
def pred(x, w):
u = x @ w
return sigmoid(u)
def accuracy_rate(w, x, y):
yp = pred(x, w)
result = np.where(yp > 0.5, 1, 0)
diff = (test_y == result)
correct = diff[np.where(diff == True)]
return (correct.size / diff.size) * 100
iris = datasets.load_iris()
x = iris.data
y = iris.target
idx = np.where((y == 0) | (y == 1))
x = x[idx]
yt = y[idx]
train_x, test_x, train_y, test_y = train_test_split(x, yt, test_size=0.3)
# データ数
M = train_x.shape[0]
# データ次元数
D = train_x.shape[1]
w = np.ones(D)
l_rate = 0.01
max_run = 100
loss = 0
rate = accuracy_rate(w, test_x, test_y)
print(rate)
for _ in range(max_run):
yp = pred(train_x, w)
yd = yp - train_y
w -= l_rate * train_x.T @ yd / M
loss = np.mean(yd**2) / 2
rate = accuracy_rate(w, test_x, test_y)
print(rate)
> python test.py
> 56.666666666666664
> 100.0
学習前と後で正解率が上がっているのが分かりますね。
(2022/3/25 学習データがテストデータを含めたものを入れていたので修正)