[連載] フリーソフトによるデータ解析・マイニング 第33回

Rと集団学習

1.集団学習とは

  集団学習 (ensemble learning) は、決して精度が高くない複数の結果を統合・組み合わせることで精度を向上させる機械学習方法である。複数の結果の統合・組み合わせの方法としては、分類問題では多数決、数値の予測(回帰)問題では平均が多く用いられている。
  集団学習では、異なる重み、あるいは異なるサンプルから単純なモデルを複数作成し、これらを何らかの方法で組み合わせることで、精度と汎化力を両立するモデルを構築する。
  本稿では、集団学習方法による、回帰・分類のアルゴリズムバギング (bagging)、ブースティング (boosting)、ランダム森 (random forest) の基本概念およびこれらのRのパッケージと関数を紹介する。
  機械学習の問題では、学習によって回帰・分類を行うシステムを学習機械と呼ぶ。文献によっては学習機械を仮説 (hypothesis)、分類器・識別器 (classifier)、学習器 (learner) とも呼ぶ。分類器は分類問題だけではなく、回帰分析、生存分析にも対応する学習機械の別名である。

2.バギング

  バギング (bagging) の bagging は、bootstrap aggregating の頭の文字列を組み合わせた造語である。バギングは1996年 Breiman によって提案された[1]。

2.1 バギングのアルゴリズム

  バギングは、与えられたデータセットから、ブートストラップ (bootstrap) と呼ばれているリサンプリング法による複数の学習データセットを作成し、そのデータを用いて作成した回帰・分類結果を統合・組み合わせる方法で精度を向上させる。ブートストラップサンプルはそれぞれ独立で、学習は並列に行うことができる。ブートストラップサンプルは、与えられたデータの経験分布とその推定量に基づいたリサンプリングにより得られたサンプルである。
  バギングの大まかなアルゴリズムを次に示す。いま、 個の個体により構成された回帰・判別を目的とした教師付きデータがあるとする。

  (1) 教師付きデータから復元抽出方法でm 回抽出を行い、新たな訓練用サブデータセットを作成し、回帰・判別のモデルh を構築する。
  (2) ステップ(1)をB 回繰り返し、回帰・判別のモデルをB 個 {hi | i = 1 , 2 , … , B }構築する。
  (3) 回帰の問題では、B 個の平均値を結果とする。B個の平均値の式
  (4) 判別の問題では、多数決をとる。H (x ) = argmax | { i |h i = y } |

2.2 パッケージと関数

  パッケージ ipred にバギング関数が実装されている。パッケージ ipred はCRANミラーサイトからダウンロードできる。パッケージ ipred の中のバギングの関数は bagging である。バギング関数 bagging は、回帰分析、分類分析、生存分析を前提とし、Breiman の方法に従っている[1]。

bagging(formula,data,nbagg=25…)

  メインの引数の指定は、基本的には関数 rpart と同じである。引数 formula には目的変数と説明変数を指定し、data には用いるデータを指定する。引数 nbagg では、繰り返しの回数 を指定する。デフォルトは25になっている。関数 rpart のパラーメタの調整は引数 control と rpart.control を用いデフォルト値を替えることができる。
  関数 print は、指定したモデルや nbagg などを返す。関数 summary は bagging の結果の要約を返すが、繰り返した毎回の結果を返すので、夥しい結果が返される。出力結果は引数 nbagg に依存する。
  構築したモデルによるテストデータへの適応は、関数 predict を用いる。

2.3 データと解析

  パッケージ kernlab の中に用意されているデータ spam を用いて、関数 bagging の使用例を示す。パッケージ kernlab はCRANミラーサイトからダウンロードできる。

> library(kernlab);data(spam)

  まず、学習用データとテスト用のデータセットを作成する。ここでは、データ spam から2500個体をランダムに選び学習用とし、その残りをテスト用とする。

> set.seed(50)
> tr.num<-sample(4601,2500)
> spam.train<-spam[tr.num,]
> spam.test<-spam[-tr.num,]

  次に、作成した学習データを用いて学習を行い、その結果を用いてテストデータを用いて予測・判別を行う。

> spam.bag<- bagging(type~.,data=spam.train, nbagg=40)
> spam.bagp<- predict(spam.bag,spam.test[,-58],type= "class")

  テストデータにおける判別・誤判別のクロス表を次に示す。

> (spam.bagt<- table(spam.test[,58], spam.bagp))

Spam.bagp
nonspam spam
nonspam
1226 56
spam
90 729

  正しく判別できた比率(正解率)は次のコマンドで求めることができる。

> sum(diag(spam.bagt)) / sum(spam.bagt)
[1] 0.9305093

3.ブースティング

  ブースティング (boosting) は、与えた教師付きデータを用いて学習を行い、その学習結果を踏まえて逐次に重みの調整を繰り返すことで複数の学習結果を求め、その結果を統合・組み合わせ、精度を向上させる。

3.1 ブースティングのアルゴリズム

  ブースティングの中で最も広く知られているのは AdaBoost というアルゴリズムである。AdaBoost は、FreundとSchapier によって1996年に提案された[3]。その後、幾つかの変種 (Discrite AdaBoost、Gentle AdaBoost、Real AdaBoost、Logit AdaBoost、Modest AdaBoost など)が提案されている。そのアルゴリズムの詳細を説明する余裕がないので、大まかな共通点のみを次に示す。

  (1) 重みの初期値w 1iを生成する。
  (2) 学習を行い重みの更新を繰り返す(t = 1 , 2 , ・・・ , T )。
    (a) 重みw ti を用いて弱学習器 を構築する。
    (b) 誤り率 α t = errt を計算する。
    (c) 結果の信頼度 β t を計算する。
    (d) 重みを更新する。w (t+1) i = g (w ti )

  (3) 重み付き多数決で結果を出力する。2値判別の場合は 

  2値判別の場合は提案された幾つかの方法の大きい違いは、重みの初期値の与え方、信頼度の計算と重みの更新の方法である。

3.2 パッケージと関数

  ブースティングを行うパッケージは boost、ada がある。パッケージ boost はCRANダウンロードミラーサイトからダウンロードでき、パッケージ ada は次のサイトからダウンロードできる。

  https://cran.r-project.org/web/packages/ada/index.html

  ローカルディスクにダウンロードした zip 形式で圧縮されたパッケージは、RGui のメニューの「パッケージ」⇒「ローカルにある zip ファイルからパッケージをインストール」をクリックし、zip ファイルを読み込むことでインストールできる。
  パッケージ ada は、ブースティングの専用パッケージである。ブースティングを行う関数は ada である。関数 ada の書き式を次に示す。

ada(formula,iter=50,type = c("discrete","real","logit","gentle"),…

  引数 formula は前節の関数 bagging と同じである。引数 iter は繰り返しの回数で、デフォルトは50になっている。引数 type では、4種類のブースティングの方法からひとつを指定する。デフォルトでは discrete が指定されている。

3.3 データと解析

  本節でも spam データを用いることにする。 次に繰り返しの回数を20にした使用例を示す。

> library(ada)
> spam.ada<-ada(type~.,data=spam.train, iter=20)

  学習結果を用いて、テストデータを当てはめるのには、関数 predict を用いる。関数 predict の書き式は基本的にはその他の回帰・判別関数と同じである。引数 type には、"vector" (予測値)、"prob" (確率)、 "both" (予測値と確率)などが指定できる。

> spam.adap<-predict(spam.ada, spam.test[,-58], type="vector")
> (spam.adat<-table(spam.test[,58], spam.adap))

Spam.adap
nonspam spam
nonspam
1226 56
spam
84 735

> sum(diag(spam.adat))/sum(spam.adat)

[1] 0.9333651

  パッケージ ada には、作図関数 plot、varplot、pairs がある。関数 plot は学習の繰り返しの回数と誤り率との対応図を作成する。関数 plot にテストデータを引数として用いるとテストデータにおける繰り返しの回数と誤り率の対応図を作成することができる。また kappa 引数を真 (TRUE) とした場合は k 統計量 (kappa statistic; 多数決における一致性の指標) と学習の回数との対応関係図が作成される。

> plot(spam.ada,kappa=TRUE, spam.test[,-58],spam.test[,58])

学習回数と誤り率およびk系統計量との関係

図1 学習回数と誤り率および k 系統計量との関係

  関数 varplot は、回帰・分類における変数の重要度を計算して図示する。現時点では若干不安定であり、更なるバージョンアップを期待する。
  現時点のブースティング関数 boost、ada は、いずれも分類は2つクラスに限定されている。k 個のクラスに対応するためには若干のテクニックが必要である。関数 ada を用いた k クラスに対応する例が[4]に紹介されている。

4.ランダム森

  ランダム森 (RF; random forest) は、バギングの提案者 Breiman により今世紀に提案された新しいデータ解析の方法である[4]。

4.1 ランダム森のアルゴリズム

  RFのアルゴリズムを次に示す。

  (1) 与えられたデータセットから 組のブートストラップサンプルを作成する。
  (2) 各々のブートストラップサンプルデータを用いて未剪定の最大の決定・回帰木を作成する。ただし、分岐のノードはランダムサンプリングされた変数の中の最善のものを用いる。
  (3) 全ての結果を統合・組み合わせ(回帰の問題では平均、分類の問題では多数決)、新しい予測・分類器を構築する。

  バギングとRFの大きい違いは、バギングは全ての変数用いるが、RFでは変数をランダムサンプリングしたサブセットを用いることができるので、高次元データ解析に向いている。
  ランダムサンプリングする変数の数Mはユーザが自由に設定することができる。Breiman は、Mは変数の数の正の平方根をと取ることを勧めている。
  RFは、様々な工夫が施され、多くの長所を持っている。幾つかの長所は[5]で紹介している。

4.2 パッケージと関数

  ランダム森の専用パッケージ randomForest は、CRANダウンロードミラーサイトからダウンロードできる。ランダム森のメイン関数は randomForest である。関数 randomForest の書き式を次に示す。

randomForest(formula, data=NULL,…, subset, na.action=na.fail)

  関数 randomForest の引数やそれに関連する関数は、関数 bagging、ada より多い。表1に主な引数、表2には主な関連関数をまとめて示す。

表1 関数 randomForest の主な引数
引数
formula
 モデルの形式、y~x1+x3のように記する
x、y
 説明変数と目的変数、formula の替わりに用いる
data、subset
 用いるデータ
na.action
 欠損値の表記型の指定
ntree
 木の数、デフォルト値は500である
mtry
 木の生成に用いる変数の数、デフォルトでは、分類の場合は
 ルートn、回帰の場合はn /3、n は変数の総数
importance
 変数の重要度
・・・
・・・

表2 関数 randomForest と関連する主な関数
関数 機能
print、summary
 結果の出力と要約の出力
plot
 木の数と誤り率との対応図
predict
 予測と判別
importance
 重要度の計算
varImpPlot
 分類に置ける変数の重要度のグラフ作成
classCenter
 分類におけるクラスの中心を求める
MDSPlot
 MDSの散布図を作成する
treesize
 用いた木のサイズ
varUsed
 RFに用いられた変数の頻度
・・・
・・・

4.3 データと解析

  前節で用意したデータ spam の学習データ spam.train を用いた randomForest の使用例を示す。

> library(randomForest)
> spam.rf<-randomForest(type~., data=spam.train,na.action="na.omit")
> print(spam.rf)

Call: randomForest(formula = type ~ ., data = spam.train, na.action = "na.omit") Type of random forest: classification Number of trees: 500 No. of variables tried at each split: 7 OOB estimate of error rate: 5.16% Confusion matrix:

nonspam spam class.error
nonspam
1459 47 0.031208500
spam
82 912 0.08249497

  結果のオブジェクトに、記録されている項目は関数 summary で確認できる。

> summary(spam.rf)

Length Class Mode
call
4 -none- call
type
1 -none- character
predicted
2500 factor numeric
err.rate
1500 -none- numeric
confusion
6 -none- numeric
votes
5000 matrix numeric
oob.times
2500 -none- numeric
classes
2 -none- character
importance
57 -none- numeric
importanceSD
0 -none- NULL
localImportance
0 -none- NULL
proximity
0 -none- NULL
ntree
1 -none- numeric
mtry
1 -none- numeric
forest
14 -none- list
y
2500 factor numeric
test
0 -none- NULL
inbag
0 -none- NULL
terms
3 terms call

  それぞれの項目は$***で返すことができる。例えば、上記で行ったRFは回帰問題であるか、それとも分類問題であるかに関する情報は次のコマンドで返す。

> spam.rf$type
[1] "classification"

  木の数と誤判別率との対応関係は、$err.rate に記録される。関数plotを用いると $err.rate の折れ線グラフが作成される。次のコマンドの結果を図3に示す。

> plot(spam.rf)

木の数と誤判別率との対応関係

図3 木の数と誤判別率との対応関係

  図3には3つの折れ線があるが、凡例がないので、どの線が何を示しているかを読み取るためには、spam.rf$err.rate と対応付ける必要がある。

> spam.rf$err.rate

OBB nonspam spam
[1,] 0.10444444 0.09523810 0.11864407
[2,] 0.10367893 0.08960177 0.12521151
<後略>

  返された結果には、 OOB(out-of-bag) がある。OOBは交差確認・検証法 (cross-validation) における誤り率に対応するモデル評価の結果である。ランダム森では、作成したモデルの評価は、交差確認法および専用のテストデータセットを用いていない。ランダム森では、ブートストラップサンプルの3分の1をテスト用として外してモデルを作成し、外した3分の1を用いてテストを行う。OOBはこのテスト結果における誤判別率である。
  ランダム森では、データセットにおける変数の重要度を計算する。変数の重要度は次のように返すことができる。関数 varImpPlot を用いてグラフで示すことができる。

> spam.rf$importance

MeanDecreaseGini
make
5.2107228
address
6.1575212
all
11.5373055
<中略>
capitalLong
68.3998362
capitalTotal
52.9686201

> varImpPlot(spam.rf)

変数の重要度(Gini係数)

図4 変数の重要度 (Gini 係数)

  構築したRFのモデルを用いて新しいデータについて回帰・分類を行うのには関数 predict を次のように用いる。

> spam.rfp<-predict(spam.rf, spam.test[,-58])
> (spam.rft<-table(spam.test[,58], spam.rfp))

Spam.rfp
nonspam spam
nonspam
1242 40
spam
73 746

> sum(diag(spam.rft))/sum(spam.rft)
[1] 0.946216

  同じデータセットについてサポートベクトルマシンを用いて判別分析を行うとその正解率は0.9277で、ランダム森の結果より若干低い。

 5.その他

  本稿で紹介した集団学習のバギング、ブースティング、ランダム森が実用化された歴史は長くない。特にランダム森はその方法が提案されたのも約5年の年月しか経っていないので、その内容を扱っている和書は見当たらない。ランダム森の回帰・分類の精度は高く、その適応性もよいので、幅広い応用が期待できる。バギングとブースティングに関しては[5]が詳しい。

参考文献:
[1] Breiman, L. (1996); Bagging Predictors, Machine Learning, 24, 123-140.
[2] Breiman, L. (2001); Random Forests, Machine Learning, 45, 5-23.
[3] Freund, Y. and Schapier, R. E.(1996); Experiments with a new boosting algorithm, In Proceedings of ICML.
[4] Mark, C., Kjell, J. and George, M. (2005); ada: an R Package for Boosting, https://www.jstatsoft.org/article/view/v017i02/v17i02.pdf
[5] 金 明哲(2005):決定木と集団学習, ESTRELA, No.133,62-67.
[6] 麻生英樹・津田宏治・村田昇(2003):パターン認識と学習の統計学、岩波書店