前回の記事では、Hadoopクラスタ(というかHDFS)に収納されている、いかにもありがちなユーザー行動テーブルを、機械学習で扱いやすい素性ベクトル+分類ラベルのテーブルに直す、というお話をしました。
ここからがデータマイニングの本番です。
ここではどの機械学習分類器を使うのか?といった細かい議論は後回しにして*1、とにかくRを使ってどのように上記のテーブルに対して機械学習を実行するのか?について書いてみます。
とにかくR formula: 'y~x+y+z'の形に持って行く
既に前回の記事でも触れた通りですが、Rの関数群は大抵の作業仮説となるモデルを"formula"として与えられるように設定されています。
その書式については、とある方のブログ記事が分かりやすいと思いますのでまずはそちらをご参照のこと。要は、回帰分析のモデル式と同じように「被説明変数=説明変数1+説明変数2+...」という書き方さえできればオッケーということです。
予め分析対象としたい説明変数が決まっている場合はともかく、大抵の場合は全ての説明変数について調べてみたいはずです。その場合は'y~.'と書けば済みます。
ともかく、ここではそういう扱い方に適したデータが既にあって、持ってくるだけの状況だと仮定しましょう。そこで、サンプルデータを僕のGitHubリポジトリに置いてありますので、
- tjo_uu_behavior.txt
- r_commands.txt
の2点とも取ってきて、Rのワーキングディレクトリに置いて下さい。そうそう、当たり前ですがまだRをPCにインストールしてない人はインストールして下さい(R本体のインストール方法についてはこの辺とか)。RStudioもあると楽で良いです。
tjo_uu_behavior.txtがサンプルデータ、r_commands.txtがこの後実際に用いるコマンドのリストです。
Rのパッケージをインストールする
前回の記事でも紹介した4手法をそれぞれやってみようと思います。ということで、
- 決定木/回帰木 ⇒ {mvpart}
- ロジスティック回帰 ⇒ (不要)
- サポートベクターマシン(SVM) ⇒ {kernlab}
- ランダムフォレスト ⇒ {randomForest}
の各パッケージをインストールして下さい(パッケージインストール方法についてはこの辺とか)。ちなみにRStudioがあればパッケージインストールはかなり楽にできます。
サンプルデータを確認する
一応、下記のような感じにしてあります(はてなブログでの表示の都合上変数名を日本語で書いてますが、実際にはRに読み込ませることを考えて英文で書いてあります)。
ユーザーID | 記事閲覧 | 記事投稿 | 写メ投稿 | コメント閲覧 | コメント投稿 | 検索 | GPSオン | Result |
---|---|---|---|---|---|---|---|---|
1 | 1 | No | ||||||
2 | 1 | No | ||||||
3 | 1 | No | ||||||
... | ... | ... | ... | ... | ... | ... | ... | ... |
1916 | 1 | Yes | ||||||
1917 | 1 | Yes | ||||||
1918 | 1 | Yes | ||||||
... | ... | ... | ... | ... | ... | ... | ... | ... |
想定としては、何かしらのスマホ向けブログです。記事&コメントの閲覧・投稿に加えて、写メ投稿や検索、そして現在地表示用のGPS強制オンなどが素性として計測されていて、これに分類ラベルとしてResult(定着=Yes / 離脱=No)というラベルが右端に追加されています。
データをインポート&NAを0に直す
> rawData <- read.delim("tjo_uu_behavior.txt") > viewData(rawData)
とすると、変数rawDataにサンプルデータが入ります(RStudioならもっと簡単です)。どんな構造かは、viewData関数でR上からも確認できます。ただし、今後機械学習を行う上でUserIDカラムは不要*2です。また、NAを0に直す必要があります。そこで、
partData<-rawData[,2:8] # UserIDカラムとResultラベルを除外する partData<-as.matrix(partData) # マトリクス形式に直す idx<-which(is.na(partData)==T) # NAが入っているマトリクスのインデックスを求める partData[idx]<-0 # NAが入っているインデックス全てに0を代入する partData<-as.data.frame(partData) # データフレーム形式に直す attach(rawData) # 元データの各カラムを呼び出してメモリに入れる Data<-cbind(partData,Result) # UserIDカラムを除去してNAを0に直したものとResultラベルをくっつける detach(rawData) # 元データをメモリから外す
とすることでUserIDカラムを削除した上で、NAを全て0に直したテーブルDataが得られます。as.matrix()やas.data.frame()といった型変換関数を使いこなせると便利です。
それでは、実際の機械学習に取り掛かってみましょう。
決定木/回帰木
Data.rp<-rpart(Result~.,data=Data)
とやってみましょう。これで決定木の結果が得られます。
plot(Data.rp,uniform=T,margin=0.12) text(Data.rp,use.n=T,all=F)
plot関数でまず決定木のツリーを描き、text関数で個々のデータの数値を書き込みます。all=Tとすると、各分岐時点での定着:離脱比も表示できます。
大雑把ではありますが、どのアクションがユーザー定着に貢献するか?が可視化されているのが分かりますね。
ロジスティック回帰
Data.glm<-glm(Result~.,data=Data,family="binomial")
とすることで、ロジスティック回帰が実行できます。summary関数を使うことで、各アクションの重み付けと、それらの効果が統計学的に有意か否かを見ることができます。
> summary(Data.glm) Call: glm(formula = Result ~ ., family = "binomial", data = Data) Deviance Residuals: Min 1Q Median 3Q Max -2.6771 -0.8263 -0.5952 0.2374 2.2293 Coefficients: Estimate Std. Error z value Pr(>|z|) (Intercept) -0.89916 0.06209 -14.483 < 2e-16 *** post.view -0.74178 0.14061 -5.275 1.33e-07 *** post.submit 4.45451 0.51088 8.719 < 2e-16 *** photo.submit 2.71624 0.30541 8.894 < 2e-16 *** comment.view -1.49874 0.28597 -5.241 1.60e-07 *** comment.submit 16.46523 438.81887 0.038 0.970 search 16.46523 403.65465 0.041 0.967 gps.on -0.09124 0.33068 -0.276 0.783 --- Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1 (Dispersion parameter for binomial family taken to be 1) Null deviance: 2769.5 on 2200 degrees of freedom Residual deviance: 2192.1 on 2193 degrees of freedom AIC: 2208.1 Number of Fisher Scoring iterations: 14
サポートベクターマシン(SVM)
Data.svm<-ksvm(Result~.,data=Data,type="C-bsvc",kernel=rbfdot(sigma=0.1),prob.model=T)
とすると、SVMの分類結果が得られます。ただし、SVMでは「どのアクションが効果的だったか」を定量化することは基本的にはできません*3。分類結果の概要は下記のようにすると見られます。
> print(Data.svm) Support Vector Machine object of class "ksvm" SV type: C-bsvc (classification) parameter : cost C = 1 Gaussian Radial Basis kernel function. Hyperparameter : sigma = 0.1 Number of Support Vectors : 1003 Objective Function Value : -971.6927 Training error : 0.2199 Probability model included.
ランダムフォレスト
> Data.rf<-randomForest(Result~.,data=Data)
とすることで、ランダムフォレストの結果が得られます。これは本来なら分類器として用いる手法ですが、その性質上「どのアクションが分類に効果的だったか」を数値化することができます*4。
> Data.rf$importance MeanDecreaseGini post.view 16.7056543 post.submit 105.4874881 photo.submit 42.8869116 comment.view 12.1641898 comment.submit 6.6509893 search 8.0601498 gps.on 0.3140066
記事投稿と写メ投稿が、ユーザーの定着vs.離脱とを分ける上で効果的だということが分かります。なお、SVMと同じように分類結果の概要は以下のようにすると見られます。
> print(Data.rf) Call: randomForest(formula = Result ~ ., data = Data) Type of random forest: classification Number of trees: 500 No. of variables tried at each split: 2 OOB estimate of error rate: 21.99% Confusion matrix: No Yes class.error No 1473 17 0.0114094 Yes 467 244 0.6568214
まとめ
とりあえずもう見たまんまですね。このスマホブログでは、「記事投稿」「写メ投稿」しているユーザーほど定着しやすいということが分かります。なので大雑把に言えば、ユーザー導線*5を工夫してこの2つのアクションをやってもらうようにすれば、ユーザーがどんどん定着してアプリとしても流行るだろう、ということが言えるわけです*6。
・・・という感じで、素性ベクトル+分類ラベルのテーブルさえ手に入れることができれば、後はRにぶち込むことで簡単に誰でもデータマイニング(=機械学習)できるということが分かりました*7。後は個々の現場のデータ感やビジネスモデルに合わせてチューニングするだけ。
それでは皆さん、良いデータマイニングライフを!
種明かし
ところで、もう気付いた方もいるかもですが、このサンプルデータは実はRにデフォルトで入っている「タイタニック号の全乗客の生存 / 死亡分類データ」を、僕が自分で改変していかにもそれっぽいwebデータマイニング向けの生データサンプルとして仕立てたものですw
ちなみに、R上では
> data(Titanic)
とコマンドを入れることで、元のクロス集計データが得られます。このデータを加工して色々遊びたい方は、isseing333さんのブログ記事に従って
z <- data.frame(Titanic) Titanic1 <- data.frame(Class = rep(z[, 1], z[, 5]), Sex = rep(z[, 2], z[, 5]), Age = rep(z[, 3], z[, 5]), Survived = rep(z[, 4], z[, 5]))
というようにすると、カテゴリカルデータから成る素性ベクトル+分類ラベル式テーブルが得られます。
*1:細かい議論を始めたら1つのエントリでは到底終わらない
*2:Rは列単位で識別してくれるのでIDを振る必要はない
*3:原理的にという面もあり、識別関数だからというポイントもあり
*4:決定木/回帰木を集合学習の手法で精度を向上させたものなので、決定木/回帰木と全く同じように説明変数の効果を数値化できる
*5:チュートリアルなんかでも良いのかも?
*6:もっともこんなベタな結論と施策「だけ」で流行るようになるアプリなんて現実にはないですわな。。。
*7:実際にはどの手法を、どんなデータに、どのようにして適用するか?という、遥かに難しく大きな問題があるんですが、それはデータマイニングの学術的な知識を学んでからでないと無理なので、今後おいおい書いていくつもりです