root3のメモ帳

データサイエンスな知識を残していくブログ。

カーネル法を勉強し直す その1 〜導入〜

世間ではディープラーニングが流行っていますが、僕自身がカーネル法を勉強し直す事が必要になったので、連載という形でカーネル法の解説をしたいと思います。
まあ、ディープラーニングも完全に万能という事ではなく、ちょこっと分析したい時はこれまでの機械学習の方が楽なことも多いので、知っていて損はないかと思います。

連載大体5〜6回くらいになる予定です。
簡単な計算や実装もやる予定ですが、今回はR言語を利用しています*1Pythonでも同じことは容易にできるはずです。

next:カーネル法を勉強し直す その2 〜カーネル法を導くための準備〜 - root3のメモ帳

線形分類の限界

カーネル法ってなに?」という疑問に答える前に、天下り式ではありますが、線形分類の限界について解説したいと思います。

例えば、2つの特徴量を持つデータが、下の図のように分布しているとします。

f:id:skroot3:20171018220853p:plain

2つの特徴量は、それぞれ横軸(x1)と縦軸(x2)で表されています。 丸くて赤い点がクラス0、三角で青い点がクラス1です。

さて、このデータに対して、クラス0とクラス1を分類できるモデルを構築したいとします。どのようなモデリングを行えば良いでしょうか?

ロジスティック回帰を試す

上記のような分類問題(2クラス分類問題)を解く際によく使われる手法が、ロジスティック回帰と呼ばれる手法です。これは表題にも書いてある線形分類器に属するもので、具体的に言うと以下のようなモデル式にしたがって分類を行います。

  1. 各サンプルは、確率pでクラス1に分類され、確率1-pでクラス0に分類される。
     y \sim B(1, p)
  2. クラス1に分類される確率は、特徴量の線形結合で表現される。
     \displaystyle \ln\frac{p}{1-p} = \beta_0 + \beta_1 x_1 + \beta_2 x_2

難しいことを書いているような気がしなくもないですが、

  • 線形回帰っぽく2クラス分類をやりたい
  • ただし、線形回帰では確率が表現できない(0を下回るし、1を超えてしまうこともある)
  • だったら適当な変換(ロジット変換)をして確率っぽくしよう

と言うだけのことです。線形回帰をちょこっと発展させただけです*2

Rでロジスティック回帰を行うのは簡単です。最初からglm関数が利用できますので、それを使いましょう。
※data_trainは、上記サンプルデータを学習データと検証データに分けたうちの学習データです

mod_glm <- glm(formula = class ~ x1 + x2, data = data_train, family = "binomial")

この程度のデータなら即計算が終わったと思います。

次にこのモデルが、どう言う範囲のデータに対してクラス0、もしくはクラス1を割り当てるかを可視化するために、細かいデータ点集合(mesh、と呼びます)に対しての予測値を計算します。
x1、x2共に-2.5から2.5の範囲を0.01刻みで動かし、各データ点に対して予測値を算出しています。最後に、predictの値が閾値を超えているかどうかでクラス分類をしています*3

今回はdplyr(とmagrittr)を使って書いています。データ加工が非常にやりやすくなるパッケージですので、知らない方はググって調べてみると良いかと思います*4

library(dplyr)
library(magrittr)

mesh_grid <- expand.grid(x1 = seq(-2.5, 2.5, by = 0.01), 
                         x2 = seq(-2.5, 2.5, by = 0.01))
mesh_grid %<>% 
    mutate(pred = predict(mod_glm, newdata = mesh_grid)) %>%  # 予測値を算出
    mutate(pred = if_else(pred >= 0, 1, 0)) # クラスに変換

mesh_gridが作成できたら、クラスによって領域を色付けして可視化してみます。ggplot2を使って作図をします。知らない方は(ry

ggplot(data = data_test) +
    geom_point(aes(x = x1, y = x2, shape = class, color = class)) + # テストデータ
    geom_raster(data = mesh_grid, aes(x = x1, y = x2, fill = pred), alpha = 0.3) + # 領域
    scale_fill_gradient(low= "red", high = "blue") + # 色の使い方
    ggtitle("Decision Area(Logistic Regression)") # タイトル
    theme_bw(base_size = 20) # プロット領域のテーマ

これを実行することで、下記のような図が出力されました。(このブログでは、この領域のことを決定領域と呼ぶことにします)

f:id:skroot3:20171018224358p:plain

見てわかる通りですが、全くうまく分類できておりません。赤い領域に赤い点、青い領域に青い点が入っていれば成功ですが、外れているものも数多くあります。

なぜロジスティック回帰でうまくいかないのか?

上記の通り、今回のデータに対してロジスティック回帰はダメダメでした。では今回のデータの分類は難しい問題なのでしょうか?いいえ、そんなことはありません。人の目からすれば、「中心からの距離」で完全に分類が可能であることは明らかなはずです。

ではなぜロジスティック回帰で分類がうまくできないのでしょうか。理由は、中心からの距離 {x_1}^2 + {x_2}^2は、 x_1  x_2の線形結合では表現できないからです。中心からの距離は、数式でもわかるように特徴量の二乗の項を含みます。ロジスティック回帰のモデル式ではこの「二乗」が表現できないので、いくらパラメータ推定を頑張っても分類ができないのです。

これは別の言い方をすると、ロジスティック回帰(線形分類器)の決定領域の境界は、必ず直線になるという事です。確率が線形結合の関数でしか表せないので、当然ですね。

線形分類器では、どう頑張っても非線形の関係を捉える事はできません*5。これが、線形分類の限界です。

カーネル法の概要

ロジスティック回帰は、線形分類器であるため上記のような非線形の関係を捉える事ができませんでした。そこで、ようやくカーネル法の登場です。

カーネル法についてここで詳しく話すのは非常に長くなってしまいますし、連載の意味もなくなりますので、簡単にだけ説明すると、

カーネル関数」と言うものを利用する事で、分析者が明示的に非線形な関係を与える事なく、非線形な関係を見つける方法

です。これだけ書くと夢のような話ですよね。しかし、実際にそういう分析ができちゃったりするわけです。

カーネル法を試してみる

今回の最後に、カーネル法を試して見ましょう。カーネル法のためにパッケージはすでにあるので、それさえインストールしてしまえば簡単にカーネル法による分析ができます。

今回は、カーネル法を利用した分類器であるサポートベクターマシン(SVM)を利用したいと思います。SVMの詳細は、この連載のどこかで解説する予定です。

RでSVMを行う場合は、kernlabパッケージを利用します。これ以外も、e1071と言うパッケージを利用することでも分析可能です。

library(kernlab)

mod_svm <- ksvm(class ~ x1 + x2,
                data = data_train,
                kernel = "rbfdot" # RBFカーネル
                ) 
print(mod_svm)

# Support Vector Machine object of class "ksvm" 
# 
# SV type: C-svc  (classification) 
# parameter : cost C = 1 
# 
# Gaussian Radial Basis kernel function. 
# Hyperparameter : sigma =  1.05806341433202 
# 
# Number of Support Vectors : 29 
# 
# Objective Function Value : -12.6542 
# Training error : 0 

3行目から6行目が、実際にSVMで分類モデルを構築するように指示している部分になります。class ~ x1 + x2の部分からわかるように、非線形な関係は明示的には指定しておりません。また、kernel = "rbfdot"と言う部分がありますが、ここでカーネル関数を指定しています。詳細は今後の連載に譲りますが、RBFカーネルというもので大抵うまくいく事が(経験的に)知られています。

printの出力をコメントアウトの部分に書いています。意味不明な事が色々書いてありますが、とりあえずTraining error : 0という事で、少なくとも学習データはエラーなく分類できている事がわかります。もちろん過学習の可能性はありますが、ロジスティック回帰ではそもそも訓練データでも完全な分類は不可能なので、かなりいいモデルである事が期待できます。

最後に、ロジスティック回帰の時と同じように決定領域を描いて見ましょう。

f:id:skroot3:20171018231136p:plain

いかがでしょうか。綺麗な円形の境界ができており、検証データに対しても100%の精度で分類できている事がわかります。

繰り返しになりますが、SVMでは非線形な関係は明示的に与えておりません。カーネル関数を利用する事で、明示的に与えなくても円形の境界であることを学習しています。

まとめと次回予告

今回は導入ということで、ロジスティック回帰と比較を行い、

  • 線形分類だけでは限界があること
  • カーネル法は、非線形な関係も自動的に考慮してくれること

を説明しました。

とはいうものの、今回の説明では(ちょっとだけパッケージを紹介しただけで)カーネル法について何も紹介していません。次回から、カーネル法の仕組みを2〜3回に分けて解説していきます。

この辺りを説明できればいいな、と思います。

*1:諸事情によりR言語で書く必要性があるのです

*2:一般化線形回帰モデルの一種です

*3:0を閾値にしているのは、predictの出力が確率そのものではないためです

*4:偉そうなこと書いていますが、僕も最近ようやく慣れてきました

*5:ここで「できません」と言っているのは、あくまでも特徴量をそのまま線形結合した場合です。明示的に非線形な関係を与えれば線形分類器でも非線形問題は解けます。詳細は次回。