文書の過去の版を表示しています。
目次
縛り。
- 学習機械にはlm()かrpart()しか使わない。
- kmeans()を層別に使う。
準備。
install.packages(c("mvtnorm"), dependencies=TRUE) library(mvtnorm) library(MASS)
k-means法によるクラスタリング
まず楕円状に分布するデータ(n=1000)を擬似乱数を用いて生成する。
test.data <- rmvnorm(1000,mean=c(1,1),sigma=matrix(c(1,0.5,0.5,1),ncol=2)) plot(test.data)
これにk-means法をかけてみる。k-means法では、クラスタ数kは分析者が指定するものであり、ここでは2としてみる。さらに最小値を求めるために、ランダムに1000の初期値から出発(nstart=1000)して、最も適したクラスタ分けを採用するように指定してある。
test.kmeans <- kmeans(test.data, centers=2, nstart=1000) print(test.kmeans) plot(test.data, col=test.kmeans$cluster)
最初のグラフに色分けがついたはずで、これがクラスタである。 クラスタはデータのみから生成する層であり、これによる層別に技術的な背景はない。 クラスタ数を3, 4, 5と変えてみると、2の場合とは異なった分割が得られることが見て取れるはずである。
4枚のグラフをまとめて描くようにしてみた。
par(mfrow=c(2,2)) test.kmeans <- kmeans(test.data, centers=2, nstart=1000) print(test.kmeans) plot(test.data, col=test.kmeans$cluster, sub="k=2") test.kmeans <- kmeans(test.data, centers=3, nstart=1000) print(test.kmeans) plot(test.data, col=test.kmeans$cluster, sub="k=3") test.kmeans <- kmeans(test.data, centers=4, nstart=1000) print(test.kmeans) plot(test.data, col=test.kmeans$cluster, sub="k=4") test.kmeans <- kmeans(test.data, centers=5, nstart=1000) print(test.kmeans) plot(test.data, col=test.kmeans$cluster, sub="k=5") par(mfrow=c(1,1))
同じことを、目視でもほぼ2群に分けられそうなデータ(n=2000)でも行ってみる。
test.data <- rbind(rmvnorm(1000,mean=c(2.5,2.5),sigma=matrix(c(1,0.5,0.5,1),ncol=2)), rmvnorm(1000,mean=c(-2.5,-2.5),sigma=matrix(c(1,0.5,0.5,1),ncol=2))) plot(test.data)
そもそも別々に生成した2群のデータを結合しているので、k=2が一番自然なクラスタ分けを得る。 グラフを描かせるとやはりk=2の場合が最もらしいことが確認できる。 それでも3以上でもk-means法がクラスタを生成する。
par(mfrow=c(2,2)) test.kmeans <- kmeans(test.data, centers=2, nstart=1000) print(test.kmeans) plot(test.data, col=test.kmeans$cluster, sub="k=2") test.kmeans <- kmeans(test.data, centers=3, nstart=1000) print(test.kmeans) plot(test.data, col=test.kmeans$cluster, sub="k=3") test.kmeans <- kmeans(test.data, centers=4, nstart=1000) print(test.kmeans) plot(test.data, col=test.kmeans$cluster, sub="k=4") test.kmeans <- kmeans(test.data, centers=5, nstart=1000) print(test.kmeans) plot(test.data, col=test.kmeans$cluster, sub="k=5") par(mfrow=c(1,1))
戯れにk=20としてみると、2つの群それぞれを、細かく群分けする様子が見て取れる。
test.kmeans <- kmeans(test.data, centers=20, nstart=1000) print(test.kmeans) plot(test.data, col=test.kmeans$cluster, sub="k=20")
これはベクトル量子化と呼ばれる、多次元信号(画像など)の量子化法(デジタル信号への変換)と数理的には同等である。 また個々のクラスタごとに予測モデルを構築することは、学習理論における分割統治の考え方に合致している。
分割統治
この考え方をより進めると、下記のような手法に辿り着く。
- 委員会機械 (Committee Machine)
- アンサンブル学習 (Ensemble Learning)
- ブースティング (Boosting)
- バギング (Bagging)
k-means法による層別
k-means法は、数値変数でしか用いられないため、データは「tic.learn[, c(1:86)]」と数値変数のフィールドを指定してkmeans()関数を実行する。
tic.learn.kmeans <- kmeans(tic.learn[, c(1:86)], centers=2, nstart=1000)
k-means法の結果を適用するには、レコードごとに「centers」の座標とのユークリッド距離を算出し、一番小さくなる層にその変数を割り当てる。
date() tic.learn$cluster <- rep(0, dim(tic.learn)[1] ) for( i in c(1:dim(tic.learn)[1]) ) { tic.kmeans.dist <- rep(0, max(tic.learn.kmeans$cluster) ) for( j in c(1:max(tic.learn.kmeans$cluster)) ) { tic.kmeans.dist[j] <- sum( (tic.learn[i,c(1:86)]-tic.learn.kmeans$center[j,])^2 ) } tic.learn$cluster[i] <- sort.list(tic.kmeans.dist)[1] } date()
念のため、keansの結果と比較して、計算に誤りがないことを確認するためにクロス集計を行う。この結果が対角であれば、計算はあっている。
table(tic.learn$cluster,tic.learn.kmeans$cluster)
centers=に指定する値は、いろいろ変えてみるといい。
変数の値を直接指定した層別(V1の例)
例えばV1で層別する場合、まずその変数を集計して、何種類の値があるかを確認する。
table(tic.learn$V1)
V1は1から41の値を取ることが確認できたら、次のように、clusterという変数をtic.learn内に作り、まず0で埋める。 そして、V1の値ごとに層の値を指定するコードを書き、Rに実行させる。
tic.learn$cluster <- rep(0, dim(tic.learn)[1]) tic.learn$cluster[tic.learn$V1==1] <- 1 tic.learn$cluster[tic.learn$V1==2] <- 2 tic.learn$cluster[tic.learn$V1==3] <- 1 tic.learn$cluster[tic.learn$V1==4] <- 2 tic.learn$cluster[tic.learn$V1==5] <- 2 tic.learn$cluster[tic.learn$V1==6] <- 1 tic.learn$cluster[tic.learn$V1==7] <- 2 tic.learn$cluster[tic.learn$V1==8] <- 2 tic.learn$cluster[tic.learn$V1==9] <- 2 tic.learn$cluster[tic.learn$V1==10] <- 2 tic.learn$cluster[tic.learn$V1==11] <- 2 tic.learn$cluster[tic.learn$V1==12] <- 1 tic.learn$cluster[tic.learn$V1==13] <- 2 tic.learn$cluster[tic.learn$V1==14] <- 2 tic.learn$cluster[tic.learn$V1==15] <- 2 tic.learn$cluster[tic.learn$V1==16] <- 2 tic.learn$cluster[tic.learn$V1==17] <- 2 tic.learn$cluster[tic.learn$V1==18] <- 2 tic.learn$cluster[tic.learn$V1==19] <- 2 tic.learn$cluster[tic.learn$V1==20] <- 2 tic.learn$cluster[tic.learn$V1==21] <- 2 tic.learn$cluster[tic.learn$V1==22] <- 2 tic.learn$cluster[tic.learn$V1==23] <- 2 tic.learn$cluster[tic.learn$V1==24] <- 2 tic.learn$cluster[tic.learn$V1==25] <- 2 tic.learn$cluster[tic.learn$V1==26] <- 2 tic.learn$cluster[tic.learn$V1==27] <- 2 tic.learn$cluster[tic.learn$V1==28] <- 2 tic.learn$cluster[tic.learn$V1==29] <- 2 tic.learn$cluster[tic.learn$V1==30] <- 2 tic.learn$cluster[tic.learn$V1==31] <- 2 tic.learn$cluster[tic.learn$V1==32] <- 2 tic.learn$cluster[tic.learn$V1==33] <- 2 tic.learn$cluster[tic.learn$V1==34] <- 2 tic.learn$cluster[tic.learn$V1==35] <- 2 tic.learn$cluster[tic.learn$V1==36] <- 2 tic.learn$cluster[tic.learn$V1==37] <- 2 tic.learn$cluster[tic.learn$V1==38] <- 2 tic.learn$cluster[tic.learn$V1==39] <- 2 tic.learn$cluster[tic.learn$V1==40] <- 2 tic.learn$cluster[tic.learn$V1==41] <- 2
最後に、0の値が残っていないかを確認するために、集計する。
table(tic.learn$cluster)
変数の値を直接指定した層別(V2とV4の組み合わせの例)
例えばV2とV4の組み合わせで、下記のように層別することを考える。
V2/V4 | 1 | 2 | 3 | 4 | 5 | 6 |
1 | 1 | 1 | 1 | 1 | 3 | 3 |
2 | 2 | 2 | 2 | 2 | 3 | 3 |
3 | 3 | 3 | 3 | 3 | 3 | 3 |
4 | 3 | 3 | 3 | 3 | 3 | 3 |
5 | 3 | 3 | 3 | 3 | 3 | 3 |
6 | 3 | 3 | 3 | 3 | 3 | 3 |
7 | 3 | 3 | 3 | 3 | 3 | 3 |
8 | 3 | 3 | 3 | 3 | 3 | 3 |
9 | 3 | 3 | 3 | 3 | 3 | 3 |
10 | 3 | 3 | 3 | 3 | 3 | 3 |
この場合も、まずその変数を集計して、何種類の値があるかを確認する。
table(tic.learn$V2, tic.learn$V4)
すべての組み合わせを書くのは煩雑なので、最初にcluster変数は3で埋めてしまう。
tic.learn$cluster <- rep(3, dim(tic.learn)[1])
clusterが1になる箇所と2になる箇所だけ、書く。
tic.learn$cluster[tic.learn$V2==1 & tic.learn$V4==1] <- 1 tic.learn$cluster[tic.learn$V2==1 & tic.learn$V4==2] <- 1 tic.learn$cluster[tic.learn$V2==1 & tic.learn$V4==3] <- 1 tic.learn$cluster[tic.learn$V2==1 & tic.learn$V4==4] <- 1 tic.learn$cluster[tic.learn$V2==2 & tic.learn$V4==1] <- 2 tic.learn$cluster[tic.learn$V2==2 & tic.learn$V4==2] <- 2 tic.learn$cluster[tic.learn$V2==2 & tic.learn$V4==3] <- 2 tic.learn$cluster[tic.learn$V2==2 & tic.learn$V4==4] <- 2
最後に、確認のために、集計する。
table(tic.learn$cluster)
層別後の解析
層ごとに回帰分析を適用したり、決定木を適用したりする。
for( i in sort(unique(tic.learn$cluster)) ) { tic.learn.cluster <- tic.learn[tic.learn$cluster == i,] tic.working.lm <- lm(V86~V2+V3+V4, data=tic.learn.cluster) print(summary(tic.working.lm)) }
全体に回帰分析を行うのに較べて、残差の標準偏差 (Residual standard error) が小さくなっている層と、大きくなっている層があることを確認できるだろうか。
print(summary(lm(V86~V2+V3+V4, data=tic.learn)))
考えたルールに基づく対象限定 (層別なし)
各変数に閾値を設けてルールを生成したとする。 たとえば、「V47が5.5以上かつV44が1未満」または「V47が5.5以上かつV1が{1,3,6,8,12,20}のどれか」、というルールは 次のように記す。
(tic.learn$V47>5.5 & tic.learn$V44<1) | (tic.learn$V47>5.5 & (tic.learn$V1==1 |tic.learn$V1==3 | tic.learn$V1==6 | tic.learn$V1==8 | tic.learn$V1==12 | tic.learn$V1==20) )
「&」が「かつ(AND)」、「|」が「または(OR)」である。
このルールを検証用データに適用するには、
tic.learn$visit <- (tic.learn$V47>5.5 & tic.learn$V44<1) | (tic.learn$V47>5.5 & (tic.learn$V1==1 |tic.learn$V1==3 | tic.learn$V1==6 | tic.learn$V1==8 | tic.learn$V1==12 | tic.learn$V1==20) )
と、訪問するか否かを二値(TRUE, FALSE)で表すオブジェクトを生成する。
このモデルに予測に基づいた訪問の成果を検証するには、訪問対象のリストtic.learnvisit)
table(tic.learn$visit, tic.learn$V86)
</code>
参考までに、上の訪問ルールを評価用のデータtic.evalに適用した場合、次のようになる。
table(tic.eval$visit) FALSE TRUE 3029 971 table(tic.eval$visit, tic.eval$V86) tic.eval.visit 0 1 FALSE 2878 151 TRUE 884 87
ここでは、訪問対象に884+87=971人を選定し、そのうちの87人が実際に契約してくれる人だったことになる。 契約率は87/971=8.96%。また誤判別率は
(884+151)/4000
で25.9%となる。
決定木に基づく対象限定
学習したモデルに基づいて、訪問対象を狭めるには、predict()という関数を用いて、訪問対象か否かというリストを作成する。 まず、設定まで調整したモデルを、学習用データ(tic.learn)から得る。
tic.rpart <- rpart(V86~., data=tic.learn, control=c(cp=0.005))
次に、このモデル(ここではtic.rpart)を、そのまま学習用データ(tic.learn)に適用して、契約してくれるか否かの予測を行う。 この際、下に記した0.05という閾値は調整(増やしたり減らしたり)が必要かもしれない。
tic.learn$visit <- predict(tic.rpart, newdata=tic.learn)[,2]>0.05
このモデルに予測に基づいた訪問の成果を検証するには、訪問対象のリストtic.visitと検証用データの正解V86のクロス集計を行えばよい。
table(tic.learn$visit) table(tic.learn$visit, tic.learn$V86
参考までに、評価用データに適用した場合は、次のようになる。
tic.eval$visit <- predict(tic.rpart, newdata=tic.eval)[,2]>0.05
table(tic.eval$visit) tic.eval$visit FALSE TRUE 2389 1611 table(tic.eval$visit, tic.eval$V86) tic.eval$visit 0 1 FALSE 2310 79 TRUE 1452 159
ここでは、訪問対象に1452+159=1611人を選定し、そのうちの159人が実際に契約してくれる人だったことになる。契約率は159/1452=11.0%。 また誤判別率は
(79+1452)/4000
で38.275%となる。