R で識別器を作ってみるのに必要な散布図の書き方・正規化の方法(iris データセットを例に)

PRML 4章とかを読んで、ちょっと試しに識別器を実装してみたい! というとき、初心者的にはデータセットをどこから持ってくるか、そのデータセットをどう使うか、実行結果をどうやってグラフなどに出力するか、といったあたりが悩み。


R はそのへんとてもよくできていて、すごくラクチン。
まず結構な数の著名なデータセットがあらかじめ入っている。その一つである iris dataset を例に「識別器を試作するための準備」について説明していこう。


iris dataset は、3品種(setosa, versicolor, verginica)のユリの花それぞれ50本について、花の萼(がく)の長さと幅、花弁の長さと幅を測ったもの。
iris はデータがきれいに分かれているので、どんな識別器に食わせてもそこそこ良い結果が出る。初心者が達成感を得るのに最適(笑)。


R での iris データセットの利用は簡単。なにしろ、iris という変数に最初っから iris dataset が入っている。

> iris
    Sepal.Length Sepal.Width Petal.Length Petal.Width    Species
1            5.1         3.5          1.4         0.2     setosa
2            4.9         3.0          1.4         0.2     setosa
        :
149          6.2         3.4          5.4         2.3  virginica
150          5.9         3.0          5.1         1.8  virginica


3クラスあるので多クラスの識別子を作れるし、2クラスのが作りたかったら、後ろの2クラス(versicolor, verginica)を抽出すればいい(setosa は線形分離されているので、さすがにおもしろみが少ない)。


またデータも4次元あるから、そのまま使えば多変量の識別器が作れる。
ただやっぱり図に落としにくいので、2次元に制限してしまうのもいいかも。最初から難しくしてもしょうがないしね。
例えば、2クラス(versicolor, verginica)に関する、萼(がく)の長さ花弁の長さのみ取り出すなら以下のようにすればいい。

# 2クラス、2次元
xlist <- iris[51:150,c(1,3)]
tlist <- ifelse(iris[51:150,5]=="versicolor",1,0)
plot(xlist,col=ifelse(tlist,"red","blue"))



いや、やっぱり多変量でやりたい、という場合はどうやって図に落とすかが悩みどころ。
pairs という関数を使えば、三面図のような、各成分の組み合わせごとに散布図を描いてくれる。

col <- sapply(iris[,5], function(x) switch(x, "setosa"="black", "versicolor"="red", "virginica"="blue"))
pairs(iris[,1:4],col=col)



品種名を色名に変換するところに R の変態な switch 関数を使っているのでちょっと読みにくい。
1行で書くことにこだわりがなければ、names を設定したベクトルを連想配列的に使って色名に変換するのがわかりやすいかもしれない。

colmap <- c("setosa"="black", "versicolor"="red", "virginica"="blue")
pairs(iris[,1:4], col=colmap[iris[,5]])


scatterplot3d というパッケージを使えば3次元の散布図が描けるので、それを使ってみるのもいいかも。
どの3軸を使うかは明示的に指定しないといけないけどね。

library(scatterplot3d)
scatterplot3d(iris[,1], iris[,2], iris[,3], color=colmap[iris[,5]])



ちなみに以下のように angle を変化させれば、3次元の図を回転させて見ることができる。なかなか効果的なので試してみてほしい。

for(angle in 10:80) scatterplot3d(iris[,1], iris[,2], iris[,3], color=colmap[iris[,5]], angle=angle*2)


線形識別器ならデータセットそのままでもなんの問題もないが、RBF 基底関数とか多項式基底とかを使いたい場合は、成分ごとに平均や分散がばらついているのは都合が悪いことも。
そういう場合の1つの手に、データを正規化してしまうというのがある。つまり、成分ごとに平均 0、分散 1 になるよう平行移動&定数倍してあげる。
R にはそのための scale という便利な関数がある。

xlist <- iris[1:4]
xlist <- scale(xlist, mean(xlist), sd(xlist))


あとは品種名を 1-of-K 表現に書き換えることができれば、だいたいの識別器は作り始められるだろう。
例えばこんな感じなんだけど、もっと簡単で直感的な書き方あるかなあ。

tlist <- iris[5]
tlist <- cbind(ifelse(tlist=="setosa",1,0), ifelse(tlist=="versicolor",1,0), ifelse(tlist=="virginica",1,0))