SEワンタンの独学備忘録

IT関連の独学した内容や資格試験に対する取り組みの備忘録

【機械学習】入門⑨ 分類-ロジスティック回帰- Pythonで学ぶ「教師あり学習」

前回の続き。
それっぽくはなりますが、単純に難しくなってきます。

↓前回
www.wantanblog.com

ロジスティック回帰

ロジスティック回帰

ロジスティック回帰(ロジスティックかいき、英: Logistic regression)は、ベルヌーイ分布に従う変数の統計的回帰モデルの一種である。

引用元:ロジスティック回帰 - Wikipedia

ロジスティック回帰は類似度を確率によって表現することができる手法と言われていますが詳しくはやりながら理解していきましょう。

ロジスティック回帰とシグモイド関数

今回用いるロジスティック回帰モデルはシグモイド関数を使用します。
シグモイド関数は以前の記事でも扱ったもので以下のようなものです。

f:id:wantanBlog:20200321184345p:plain

ここでは上記のモデルをロジスティック関数と呼びます。

なお数学的には厳密ではなく、一般的なロジスティック関数自体はこのモデルを指しているのでなく、ロジスティック関数の特殊な場合が上記の関数になるようです。
上記の関数自体が狭義のシグモイド関数であり正確には標準シグモイド関数と言うそうです。

あくまで機械学習分野ではロジスティック関数と呼んでしまっても問題なさそうです。


過去記事からの転記ですが、標準シグモイド関数は以下のような外形となり、その値は0から1までの間をとります。
0から1までの間をとるという特徴から確率表現に向いていそうということも予想できます。

f:id:wantanBlog:20200321220748p:plain

ロジスティック回帰モデル

まずはPythonでロジスティック関数の外形を再確認します。

・実装例

# ロジスティック回帰モデル
import numpy as npy
import matplotlib.pyplot as plt
%matplotlib inline

# ロジスティック関数
def logistic(x):
    y = 1 / (1 + npy.exp(-x))
    return y

def logistic1(x,func):
    y = 1 / (1 + npy.exp(-func))
    return y

def func1(x):
    y = 2*x - 5
    return y

def func2(x):
    y = -3*x + 2
    return y
             
X = npy.linspace(-5, 5, 400)
Y = logistic(X)

plt.grid(True)
plt.xlim(-5, 5)
plt.ylim(-.5, 3)
plt.plot(X,Y,color='gray',linewidth=4)
plt.plot(X,logistic1(X,func1(X)),color='blue',linewidth=4)
plt.plot(X,func1(X),color='blue',linewidth=2,linestyle='--')
plt.plot(X,logistic1(X,func2(X)),color='red',linewidth=4)
plt.plot(X,func2(X),color='red',linewidth=2,linestyle='--')

・出力結果

f:id:wantanBlog:20200321232804p:plain

グレーのグラフは一番基本的な形の関数です。
青と赤はそれぞれロジスティック関数のxに一次関数を当てはめたものです。点線は元の一次関数のグラフを表していてグラフの外形をそのままに0から1までにおさまるように圧縮されていることがわかります。


次に前回のデータ範囲をロジスティック関数に適用してみます。

・例

# ロジスティック回帰モデル
import numpy as npy
import matplotlib.pyplot as plt
%matplotlib inline

# ロジスティック関数
def logistic(x,w):
    y = 1 / (1 + npy.exp(-(w[0]*x + w[1])))
    return y

# ロジスティック関数の描写
def show_logistic(w):
    xb = npy.linspace(X_min, X_max, 100)
    y = logistic(xb, w)
    plt.plot(xb,y,color='gray',linewidth=4)
    # 決定境界
    i = npy.min(npy.where(y > 0.5))
    B = (xb[i -1] + xb[i])/2
    plt.plot([B, B], [-.5, 1.5],color='k',linestyle='--')
    plt.grid(True)
    return B

# データ生成
# データセットの取り出し
sample_data = npy.load('classdata1.npz')
X_min = sample_data['X_min']
X_max = sample_data['X_max']
X_n = sample_data['X_n']
X = sample_data['X']
X_col = ['cornflowerblue','gray']

#test
W=[0.14,-25]
show_logistic(W)

・出力結果

178.77272727272725

f:id:wantanBlog:20200321234727p:plain

上記のグラフではまずロジスティック関数を一次関数を適用しモデルとしています。

f:id:wantanBlog:20200321235500p:plain

パラメータとなるWを調整します。
※これは今回は出力しながら調整

そしてサンプルデータの範囲(最小値から最大値)で出力を行います。
また、ロジスティック関数において0.5(中間)を初めて超えた点をとり、その点と一個前の点の中間点をとることで決定境界としています。

交差エントロピー誤差

交差エントロピー誤差

情報理論において、交差エントロピー(こうさエントロピー)またはクロスエントロピー(英: cross entropy)は、2つの確率分布の間に定義される尺度である。符号化方式が、真の確率分布 ではなく、ある所定の確率分布 に基づいている場合に、とりうる複数の事象の中からひとつの事象を特定するために必要となるビット数の平均値を表す。

引用元:交差エントロピー - Wikipedia

うーんこの時点ではどんなことを表しているのかいまいちわからない!
※エントロピーというと化学で使うやつぐらいしか知らないけど統計とかでもでてくる言葉なんですね

交差エントロピー誤差の導出

まずはロジスティック回帰モデルを使って、サンプルデータにおいて、xがt=1(休日である)となる確率を以下の式で表します。

f:id:wantanBlog:20200322173407p:plain

ここで、データが一つだけの場合を考えるとアクセス数xに対してt=1(休日である)だったら「」またt=0(平日である)のときは「1-y」で表現することができます。
このような状況を数学的な表現として以下のような式で一般化することができます。

f:id:wantanBlog:20200322010139p:plain

指数が0のときは無条件で1になるので、t=1のとき「」、t=0のときは「1-y」と表現することができます。

今の状態では、データが一つだけの場合を考えているので、データがn個の場合に拡張します。
複数のデータ群をTとすると以下のように表現することができます。

f:id:wantanBlog:20200322011934p:plain

式中に登場している「Π」は積の記号で、和の記号Σと同じような感じで各項を乗算していきます。
前回記事の以下の式と同じようなものになります。

f:id:wantanBlog:20200322012945p:plain

つまりこの式は尤度を表しています。

Π」について知らない場合は以下記事も参照。
www.wantanblog.com


ここで、前回の最尤推定を数学的に考えたときと同様に式の次元を下げるために両辺の対数を取ります。

f:id:wantanBlog:20200322013659p:plain

対数をとることでΠをΣに置き換えることができます。
この式を初めてみるとかなり複雑な式に見えましたが、導出方法がわかると何を表現しているのかはなんとなくわかりますね。

もうすぐゴールです。
上記の式が最大になるパラメータを求めることになりますが、慣用的に「ー1」をかけるようです。
これによって最小値を求めることになり、その式を「交差エントロピー誤差」と呼びます。

また、その式をデータ数に誤差の値が影響されにくくするためにデータ数Nで式を割ることで平均を導出します。

f:id:wantanBlog:20200322015155p:plain

この式を「平均交差エントロピー誤差」と呼びます。

Pythonによる実装

導出した平均交差エントロピー誤差をPythonで実装します。

・例

# 平均交差エントロピー誤差
import numpy as npy
import matplotlib.pyplot as plt
%matplotlib inline

# ロジスティック関数
def logistic(x,w):
    y = 1 / (1 + npy.exp(-(w[0]*x + w[1])))
    return y

# 平均交差エントロピー誤差
def cee_logistic(w,x,t):
    y = logistic(x,w)
    cee = 0
    for n in range(len(y)):
        cee = cee -(t[n]*npy.log(y[n]) + (1 - t[n]) * npy.log(1 - y[n]))
    cee = cee / X_n
    return cee

# データ生成
# データセットの取り出し
sample_data = npy.load('classdata1.npz')
X_min = sample_data['X_min']
X_max = sample_data['X_max']
X_n = sample_data['X_n']
X = sample_data['X']
X_col = ['cornflowerblue','gray']

# 目標データ
T = sample_data['Y']

#test
W=[0.14,-17]
cee_logistic(W,X,T)

・出力結果

8.018694918854194

cee_logistic(w,x,t)の関数に対して、前回使用したパラメータを引数として渡すと平均交差エントロピー誤差が返却されます。

平均交差エントロピー誤差のグラフ

上記の実装ではあるパラメータパターンに対してのみ値を返却していましたが、範囲をとってグラフの概観を表示してみます。

・例

# 平均交差エントロピー誤差
import numpy as npy
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
%matplotlib inline

# ロジスティック関数
def logistic(x,w):
    y = 1 / (1 + npy.exp(-(w[0]*x + w[1])))
    return y

# 平均交差エントロピー誤差
def cee_logistic(w,x,t):
    y = logistic(x,w)
    cee = 0
    for n in range(len(y)):
        cee = cee -(t[n]*npy.log(y[n]) + (1 - t[n]) * npy.log(1 - y[n]))
    cee = cee / X_n
    return cee

# データ生成
# データセットの取り出し
sample_data = npy.load('classdata1.npz')
X_min = sample_data['X_min']
X_max = sample_data['X_max']
X_n = sample_data['X_n']
X = sample_data['X']
X_col = ['cornflowerblue','gray']

# 目標データ
T = sample_data['Y']

wn = 80
w_range = npy.array([[-.3,.1],[-0,40]])
w0 = npy.linspace(w_range[0,0],w_range[0,1],wn)
w1 = npy.linspace(w_range[1,0],w_range[1,1],wn)

ww0, ww1 = npy.meshgrid(w0,w1)
C = npy.zeros((len(w1),len(w0)))
w = npy.zeros(2)

for i0 in range(wn):
    for i1 in range(wn):
        w[0] = w0[i0]
        w[1] = w1[i1]
        C[i1, i0]=cee_logistic(w, X, T)
# グラフ表示
plt.figure(figsize=(12,5))
plt.subplots_adjust(wspace=0.5)
ax = plt.subplot(1,2,1, projection='3d')
ax.plot_surface(ww0, ww1, C, color='blue',edgecolor='black',rstride=10,cstride=10,alpha=0.3)
ax.set_xlabel('$w_0$', fontsize=14)
ax.set_ylabel('$w_1$', fontsize=14)
ax.set_xlim(-.3,.1)
ax.set_ylim(0,40)
ax.set_zlim(0,20)
ax.view_init(30,-95)

plt.subplot(1,2,2)
cont = plt.contour(ww0,ww1,C,20,colors='black',levels=[0,0.2,0.4,0.8,1.6,3.2,6.4])
cont.clabel(fmt='%.1f', fontsize=8)
plt.xlabel('$w_0$', fontsize=14)
plt.ylabel('$w_1$', fontsize=14)
plt.grid(True)
plt.show()

・出力結果

f:id:wantanBlog:20200322030303p:plain

対象としたサンプルデータによって、表示範囲をよく検討する必要があります。
まずは適当に表示してみて徐々に表示範囲を絞っていくとよいでしょう。

グラフの概観は左の3Dから分かります。
最小値は右の等高線からがわかりやすく「w0=-0.1、w1=20」前後に最小となる点がありそうなことが分かります。

ーーーーーーーーーーーーーーーーーーーーー

今回はここまで、未だ答えにはたどり着けていませんが交差エントロピー誤差までがなんとなくわかったということで。

・今回のソース
python_dev/Classification2.ipynb at master · wantanblog/python_dev · GitHub