# ランダムフォレスト(random forest)とは？PythonとRで実装してみよう！

> 当サイト【スタビジ】の本記事では、決定木をアンサンブル学習することにより汎化能力を高めた強力な機械学習手法「ランダムフォレスト」について見ていきましょう！アルゴリズムを確認した後にRでもPythonでも実装をおこなっていきますよー！

- URL: https://toukei-lab.com/%e3%83%a9%e3%83%b3%e3%83%80%e3%83%a0%e3%83%95%e3%82%a9%e3%83%ac%e3%82%b9%e3%83%88
- 公開: 2022.11.29 / 更新: 2024.11.29
- サイト: スタビジ（https://toukei-lab.com）

:::talk{who="umatan"}
当サイト【スタビジ】の本記事では、決定木をアンサンブル学習することにより汎化能力を高めた強力な機械学習手法「ランダムフォレスト」について見ていきましょう！アルゴリズムを確認した後にRでもPythonでも実装をおこなっていきますよー！
:::

こんにちは！

データサイエンティストのウマたん([@statistics1012](https://twitter.com/statistics1012?lang=ja))です！

汎化能力の非常に高いかつ簡易的に実装可能なランダムフォレストについてみていきましょう！

今でこそ[ディープラーニング](/deep-learning)や[XGboost](/xgboost)などの台頭により日の目を見なくなりましたが、依然としてパッケージで<mark>簡単に実装でき相当良い精度をたたき出す最強手法であることには変わりありません。</mark>

:::talk{who="umatan"}
ランダムフォレストは機械学習手法の中でも非常に優秀なイメージがあるよ！学生時代は非常にお世話になったなー！
:::

:::talk{who="robotan"}
ランダムフォレストについて詳しく見ていきその特徴を学んでいこう！そしてRとPythonで実装していこう！
:::

さて、ランダムフォレストとは**一体どんな手法**なのでしょうか？

:::box{title="読み飛ばしガイド"}
・[ランダムフォレストのアルゴリズム](#i-2)\
・[ランダムフォレストをRで実装](#R)\
・[ランダムフォレストをPythonで実装](#Python)
:::

以下の動画でも解説をしていますよー！

::youtube{#DXHB0j7cU68}

## ランダムフォレストとは

まず、<mark>ランダムフォレストについてまとめられた海外の論文から引用してみましょう！</mark>

> Random forests are a combination of tree predictors such that each tree depends on the values of a random vector sampled independently and with the same distribution for all trees in the forest.\
> 引用元：[Google-“Random Forests”](https://link.springer.com/content/pdf/10.1023/A\:1010933404324.pdf)

ランダムフォレストとはざっくり言うと、

**「決定木をたくさん集めて合体させた手法」**

です。

決定木に関しては「[決定木](/%E6%B1%BA%E5%AE%9A%E6%9C%A8)」を見てください。詳しくまとめています。

決定木は<mark>単体だとそれほど強い手法ではありません。</mark>

しかし、その決定木をバギングと呼ばれる集団学習法（アンサンブル学習）を用いてたくさん集めてくると<mark>最強のランダムフォレストが出来上がるんです。</mark>

[アンサンブル学習法](/ensemble)は、決して精度が高いとはいえない弱いモデルをたくさん構築し、これらの予測結果を統合することで高い精度を出す方法論です。

<https://toukei-lab.com/ensemble>

ランダムフォレストのイメージはこんな感じ。

![](/wp-content/uploads/2018/01/66c19942ab4ba346fdb64ccc04cde373-5.png)

<mark>なんでもかんでもバギングすれば良くなるわけじゃないことは注意しておきましょう！</mark>

それでは、次に具体的なアルゴリズムを見ていきます

## ランダムフォレストのアルゴリズム

アルゴリズムについては、機械学習の教本として有名な「はじめてのパターン認識」から引用していきます！

非常に有名で<mark>他の機械学習手法についてもしっかり学べるので興味があれば見てみてください！</mark>

::product[はじめてのパターン認識]{url="https://www.amazon.co.jp/dp/4627849710?tag=statistic08-22&linkCode=ogi&th=1&psc=1" image="https://m.media-amazon.com/images/I/51YFB3Y0EYL._SL160_.jpg" amazon="https://www.amazon.co.jp/dp/4627849710?tag=statistic08-22&linkCode=ogi&th=1&psc=1" rakuten="https://af.moshimo.com/af/c/click?a_id=858168&p_id=54&pc_id=54&pl_id=616&url=https%3A%2F%2Fsearch.rakuten.co.jp%2Fsearch%2Fmall%2F%25E3%2581%25AF%25E3%2581%2598%25E3%2582%2581%25E3%2581%25A6%25E3%2581%25AE%25E3%2583%2591%25E3%2582%25BF%25E3%2583%25BC%25E3%2583%25B3%2F%3Ff%3D1%26grp%3Dproduct"}

> 1. データセットからN個のブートストラップサンプルを作成する。
> 2. ブートストラップサンプルの中から分岐に用いる変数をM個ランダムサンプリングし、決定木を作成する。
> 3. 1\~2を繰り返す。
> 4. 量産された決定木に対して、予測したいデータを入力する。
> 5. 全ての結果を統合(回帰の問題では平均、分類の問題では多数決)して、1つの予測結果として出力する。
>
> 引用元：「[はじめてのパターン認識](https://af.moshimo.com/af/c/click?a_id=860485\&p_id=170\&pc_id=185\&pl_id=4062\&s_v=b5Rz2P0601xu\&url=http%3A%2F%2Fwww.amazon.co.jp%2Fexec%2Fobidos%2FASIN%2F4627849710%2Fref%3Dnosim)」

ランダムサンプリングする変数の数Mはユーザが自由に設定することができますが、<mark>一般的には変数の数の正の平方根を取ることが多いです。</mark>

## ランダムフォレストはどんな時に使える？

ランダムフォレストのアルゴリズム自体はそれほど複雑ではありませんが、決定木を[弱学習器](/weak-learning)として用いており<mark>結果を解釈するのにはそれほど向いていません</mark>。

精度と解釈容易性は比較的トレードオフの関係にあるため、<mark>解釈容易性を求めるなら決定木を用いたほうが良いです。</mark>

:::talk{who="umatan"}
実務の現場では意外と単純な決定木の方が好んで使用されるよね！
:::

:::talk{who="robotan"}
そうそう！解釈と予測の違いを明確にして手法を使い分けていったほうがいいね！以下の記事も参考にしてみてね！
:::

<https://toukei-lab.com/machinelearning-statistics>

精度を徹底的に求めるならランダムフォレストなどの機械学習手法を使いましょう！

無論、[XGboost](/xgboost)や[Light gbm](/light-gbm)などの[勾配ブースティング木](/gradient-boosting)が強いことは言うまでもありませんが笑

ちなみにランダムフォレストでも、<mark>変数重要度を算出することができどの変数が寄与しているのかを可視化することは可能です。</mark>

## ランダムフォレストをPythonで実装してみよう！

![](/wp-content/uploads/2018/09/goriIMGL9703_TP_V1-e1555747456954.jpg)

それでは早速、Pythonでランダムフォレストを実装していきます。

データ分析コンペ [Nishika](https://competition.nishika.com/competitions/mansion_pra/summary)の「**中古マンション価格予測**」というトレーニングコンペのデータに対してランダムフォレストを実装してみたいと思います。

まず [Nishika](https://competition.nishika.com/competitions/mansion_pra/summary)に会員登録をして**中古マンション価格予測**のデータから学習データとテストデータをダウンロードしてローカルのフォルダに入れておきましょうー！（※会員登録をしないとデータをダウンロードできません）。

学習データには複数CSVが入っているので、それらをglobで結合させてデータフレーム化していきます。

globは、ディレクトリに格納されたファイル名を抽出するのに便利なライブラリです。

<https://toukei-lab.com/glob>

コードは以下！

```python title="nishika_randomforest.py"
import glob
import pandas as pd
import numpy as np
import category_encoders as ce
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestRegressor
from sklearn.metrics import r2_score

files = glob.glob("train/*.csv")
data_list = []
for file in files:
    data_list.append(pd.read_csv(file, index_col=0))
df = pd.concat(data_list)

def data_pre(df):
    nonnull_list = []
    for col in df.columns:
        nonnull = df[col].count()
        if nonnull == 0:
            nonnull_list.append(col)
    df = df.drop(nonnull_list, axis=1)

    df = df.drop("市区町村名", axis=1)

    df = df.drop("種類", axis=1)

    dis = {
        "30分?60分":45,
        "1H?1H30":75,
        "2H?":120,
        "1H30?2H":105
    }
    df["最寄駅：距離（分）"] = df["最寄駅：距離（分）"].replace(dis).astype(float)

    df["面積（㎡）"] = df["面積（㎡）"].replace("2000㎡以上", 2000).astype(float)


    y_list = {}
    for i in df["建築年"].value_counts().keys():
        if "平成" in i:
            num = float(i.split("平成")[1].split("年")[0])
            year = 33 - num
        if "令和" in i:
            num = float(i.split("令和")[1].split("年")[0])
            year = 3 - num
        if "昭和" in i:
            num = float(i.split("昭和")[1].split("年")[0])
            year = 96 - num
        y_list[i] = year
    y_list["戦前"] = 76
    df["建築年"] = df["建築年"].replace(y_list)

    year = {
        "年第１四半期": ".25",
        "年第２四半期": ".50",
        "年第３四半期": ".75",
        "年第４四半期": ".99"
    }
    year_list = {}
    for i in df["取引時点"].value_counts().keys():
        for k, j in year.items():
            if k in i:
                year_rep = i.replace(k, j)
        year_list[i] = year_rep
    df["取引時点"] = df["取引時点"].replace(year_list).astype(float)
    
    cols = ["都道府県名", "地区名", "最寄駅：名称", "間取り", "建物の構造", "用途", "今後の利用目的", "都市計画", "改装", "取引の事情等"]
    ce_df = ce.OrdinalEncoder(cols=cols, handle_unknown='impute')
    df = ce_df.fit_transform(df)
    
    return df
    
df = data_pre(df)
df = df.dropna()

df_train, df_val =train_test_split(df, test_size=0.2)

col = "取引価格（総額）_log"
train_y = df_train[col]
train_x = df_train.drop(col, axis=1)

val_y = df_val[col]
val_x = df_val.drop(col, axis=1)

model = RandomForestRegressor(n_estimators=100)
model.fit(train_x, train_y.values)

pre = model.predict(val_x)
r2_score(val_y, pre)
```

そのあと一部カラムに関しては分析しやすいように加工していきます。

そしてランダムフォレストを以下の部分で実装しています。

```python
model = RandomForestRegressor(n_estimators=100)
model.fit(train_x, train_y.values)

pre = model.predict(val_x)
r2_score(val_y, pre)
```

ここでは評価指標に決定係数を使っています。

結果は0.853となりました！

それなりの精度。

## ランダムフォレストをRで実装してみよう！

![](/wp-content/uploads/2018/09/S_8608589564326.jpg)

それでは続いてランダムフォレストを使ってRで解析を行ってみましょう！

ここで行う分析は2つ。

有名な**花びらのirisデータ**を用いた簡単な解析とデータ解析コンペ[Kaggle](/kaggle)でも代表的なデータとして取り上げられている**タイタニックのデータ**を使ったシミュレーションを何回か行う解析を行っていきます。

### irisデータをランダムフォレストで分類

irisデータとはあやめの種類を分類したデータで目的変数は3カテゴリーの質的変数、説明変数は花びらの幅とか4つです。

サンプルは150個で、<mark>分類しやすいデータなのでどんな手法でも割と簡単に分類できるんですが、どうなるでしょう！</mark>

今回はランダムフォレスト以外に[決定木](/%E6%B1%BA%E5%AE%9A%E6%9C%A8)と[サポートベクターマシン](/svm)と[ニューラルネットワーク](/nn)で比較しました。

```r title="iris.R"
library(randomForest)
library(rpart)
library(kernlab)
library(nnet)

data<-iris
ndata<-nrow(data)
ridx<-sample(ndata,ndata*0.5)
data.learn<-data[ridx,]
data.test<-data[-ridx,]

#randomforest
forest<-randomForest(Species~.,data=data.learn)
pred.forest<-predict(forest,newdata=data.test,type="class")
table(pred.forest,data.test[,5])

#決定木
cart<-rpart(Species~.,data=data.learn)
pred.cart<-predict(cart,newdata=data.test,type="class")
table(pred.cart,data.test[,5])

#svm
svm<-ksvm(Species~.,data=data.learn)
pred.svm<-predict(svm,newdata=data.test)
table(pred.svm,data.test[,5])

#nn
nn<-nnet(Species~.,data=data.learn,size=10)
pred.nn<-predict(nn,newdata=data.test,type="class")
table(pred.nn,data.test[,5])

```

```text
ランダムフォレスト
 pred.forest  setosa versicolor virginica
##   setosa         29          0         0
##   versicolor      0         25         1
##   virginica       0          2        18
決定木
pred.cart    setosa versicolor virginica
##   setosa         29          0         0
##   versicolor      0         26         3
##   virginica       0          1        16
SVM
pred.svm     setosa versicolor virginica
##   setosa         29          0         0
##   versicolor      0         25         1
##   virginica       0          2        18
ニューラルネット
 pred.nn      setosa versicolor virginica
##   setosa         29          0         0
##   versicolor      0         25         2
##   virginica       0          2        17
```

結果はこんな感じになりました！

予想通りどれもいい感じに分類できてますね～！まあirisデータは分類しやすいので！

### タイタニックでの生死予測をランダムフォレストと他の手法で比較

続いて！

**タイタニックのデータ**を用いて解析を行っていきます。[Kaggleの公式サイト](https://www.kaggle.com/c/titanic/data)からデータをダウンロードできます。

まず、データのクレンジングを行い、欠損値は削除、不要と考えられる項目は削除しました。

**項目8つ**

:::box
Survived：生死　pclass：客室のクラス　sex：性別　age：年齢　sibsp：兄弟・配偶者の数　parch：親・子供の数　fare：乗船料金　embarked：乗船した港
:::

**サンプル数714**

この時、生死を目的変数としそれ以外を説明変数とします。

サンプル数714のうちランダムに400個のデータを取り出し学習データとし、残りの314を予測データとします。

学習データで予測モデルを作り、予測データにあてはめ真値と予測値の判別率を精度として比較します。

シミュレーション回数は10回とし、上記の手順を10回繰り返し、結果を平均したものを最終アウトプットとします。シミュレーション回数をもっと増やせば精度の信頼性は上がります。

ランダムフォレストを[サポートベクターマシン](/svm)、[ナイーブベイズ](/naivebaise)、[ニューラルネットワーク](/nn)、[XGboost](/xgboost)、[k近傍法](/%ef%bd%8b%e8%bf%91%e5%82%8d%e6%b3%95)の5手法と比較します！

基本的に引数は全部デフォルトで！果たして結果はどうなるでしょうか！

```r title="knn-comparison.R"
##様々な手法で予測精度の比較を行う##
##randomForest,SVM,ナイーブベイズ，ニューラルネット
library(xgboost)
library(caret)
library(Matrix)
library(kernlab)
library(randomForest)
library(e1071)
library(nnet)
library(ggplot2)
library(class)
titanic<-read.csv("タイタニック乗船者リスト.csv")##データの読み込み
titanic<-na.omit(titanic)##欠損値削除
titanic$Survived<-as.factor(titanic$Survived)##質的変数に変換
sim<-10##シミュレーション回数
result<-matrix(0,sim,6)##結果の箱
titanic[,4]<-as.numeric(titanic[,4])
titanic[,9]<-as.numeric(titanic[,9])

for(i in 1:sim){
  ####学習データとテストデータに分ける####
  train.id<-sample(nrow(titanic),400)
  train.data<-titanic[train.id,-1]
  test.data<-titanic[-train.id,-1]
  
  
  ##randomForest##
  rf<-randomForest(Survived~.,train.data)
  test.data.rf<-cbind(test.data,"predict"=predict(rf,test.data))
  result[i,1]<-sum(test.data.rf$predict==test.data.rf$Survived)/nrow(test.data.rf)
  
  
  ##SVM##
  svm<-ksvm(Survived~.,train.data)
  test.data.svm<-cbind(test.data,"predict"=predict(svm,test.data))
  result[i,2]<-sum(test.data.svm$predict==test.data.svm$Survived)/nrow(test.data.svm)
  
  ##ナイーブベイズ##
  nb<-naiveBayes(Survived~.,train.data)
  test.data.nb<-cbind(test.data,"predict"=predict(nb,test.data))
  result[i,3]<-sum(test.data.nb$predict==test.data.nb$Survived)/nrow(test.data.nb)
  
  ##ニューラルネット##
  nn<-nnet(Survived~.,train.data,size=5)
  test.data.nn<-cbind(test.data,"predict"=round(predict(nn,test.data),0))
  result[i,4]<-sum(test.data.nn$predict==test.data.nn$Survived)/nrow(test.data.nn)
  
  ##XGboost##
  label.data.train<-as.integer(train.data$Survived)-1##目的変数を01に
  label.data.predict<-as.integer(test.data$Survived)-1
  
  train.data.xg<-train.data[,-1]##目的変数取り除く
  test.data.xg<-test.data[,-1]
  
  xgb.data<-xgb.DMatrix(data.matrix(train.data.xg),label=label.data.train)##Xgb型へ成型
  xgb.data.predict<-xgb.DMatrix(data.matrix(test.data.xg))
  
  param <- list(
    "objective" = "binary:logistic"
    ,"eta" = 0.01
    # ,"max_depth" = 10
    ,"min_child_weight" = 5
    # ,"subsample" = 1
    # ,"colsample_bytree" = 1
  )##パラメータ適当
  
  model <- xgboost(
    param=param
    ,data=xgb.data
    ,nrounds=1000
  )##実際の学習
  
  predict_xgb<-predict(model,xgb.data.predict)
  
  a<-c()
  m<-length(predict_xgb)#行数
  
  for(l in 1:m){
    if(predict_xgb[l]>0.5){a[l]<-1}
    else{a[l]<-0}
  }
  result[i,5]<-sum(a==label.data.predict)/length(a)
  
  #k-nn
  knn<-knn(train.data,test.data,train.data[,"Survived"] ,prob=T,k=3)
  test.data.knn<-cbind(test.data,"predict"=knn)
  result[i,6]<-sum(test.data.knn$predict==test.data.knn$Survived)/nrow(test.data.knn)
  
}

result.c<-apply(result,2,mean)##simの平均値
result.c<-t(as.matrix(result.c))
result.c<-rbind(result.c,result.c)
result.c<-round(result.c,3)
##グラフ描画
colnames(result.c) <- c("RF","SVM","Bayes","NN","xgb","knn")
df<-data.frame(method=colnames(result.c),result=colMeans(result.c))
gp<-ggplot(df,aes(x=method,y=result,fill=method))
gp<-gp+geom_bar(width=0.8,stat="identity")
gp<-gp+ylab("result")
gp<-gp+geom_text(label=colMeans(result.c),vjust=1)
plot(gp)
```

randomForestというパッケージに入っているrandomForestという関数を用いて解析を行っていきます。

```python
 ##randomForest##
  rf<-randomForest(Survived~.,train.data)
  test.data.rf<-cbind(test.data,"predict"=predict(rf,test.data))
  result[i,1]<-sum(test.data.rf$predict==test.data.rf$Survived)/nrow(test.data.rf)
```

データ加工は描画でいくつかコードを書いていますが、実際にランダムフォレストを行っているのはこの数行！

Survived\~.はSurvivedを目的変数としてそれ以外を説明変数にするよーということです。

結果は以下のようになりました！

![](/wp-content/uploads/2018/02/ml-comparison.png)

それぞれBayes（ナイーブベイズ）、knn（k近傍法）、NN（ニューラルネットワーク）、RF（ランダムフォレスト）、SVM（サポートベクターマシン）、xgb（XGboost）と表示しています。

やはり<mark>ランダムフォレスト・SVM・XGboostあたりが強いですね！</mark>

:::talk{who="umatan"}
こんなに簡単に実装できて精度の高いランダムフォレストって素晴らしい！
:::

:::talk{who="robotan"}
SVMと比較して計算負荷もそれほど高くないから時間もかからないよ！
:::

他の手法についても是非見てみてください！

## ランダムフォレスト　まとめ

本記事では、**ランダムフォレスト**について見てきました！

<mark>簡単に実装できる割には精度が高いことが特徴です。</mark>

最後にランダムフォレストについてまとめておきましょう！

:::memo
**・ランダムフォレストは決定木とバギングを組み合わせた手法**\
**・ランダムフォレストは簡単に高精度をたたき出すコスパの良い手法**\
**・RでもPythonでもライブラリ使えば簡単に使用できる**
:::

:::talk{who="umatan"}
機械学習手法の中でもかなりメジャーな手法だから、一度は自分の手で実装してみよう！
:::

ランダムフォレストに関してもっと詳しく知りたいかたは以下の書籍を参考にしてみてください！

::product[はじめてのパターン認識]{url="https://www.amazon.co.jp/dp/4627849710?tag=statistic08-22&linkCode=ogi&th=1&psc=1" image="https://m.media-amazon.com/images/I/51YFB3Y0EYL._SL160_.jpg" amazon="https://www.amazon.co.jp/dp/4627849710?tag=statistic08-22&linkCode=ogi&th=1&psc=1" rakuten="https://af.moshimo.com/af/c/click?a_id=858168&p_id=54&pc_id=54&pl_id=616&url=https%3A%2F%2Fsearch.rakuten.co.jp%2Fsearch%2Fmall%2F%25E3%2581%25AF%25E3%2581%2598%25E3%2582%2581%25E3%2581%25A6%25E3%2581%25AE%25E3%2583%2591%25E3%2582%25BF%25E3%2583%25BC%25E3%2583%25B3%2F%3Ff%3D1%26grp%3Dproduct"}

::product[入門パターン認識と機械学習]{url="https://www.amazon.co.jp/dp/4339024791?tag=statistic08-22&linkCode=ogi&th=1&psc=1" image="https://m.media-amazon.com/images/I/514HpqIAd2L._SL160_.jpg" amazon="https://www.amazon.co.jp/dp/4339024791?tag=statistic08-22&linkCode=ogi&th=1&psc=1" rakuten="https://af.moshimo.com/af/c/click?a_id=858168&p_id=54&pc_id=54&pl_id=616&url=https%3A%2F%2Fsearch.rakuten.co.jp%2Fsearch%2Fmall%2F%25E5%2585%25A5%25E9%2596%2580%25E3%2583%2591%25E3%2582%25BF%25E3%2583%25BC%25E3%2583%25B3%25E8%25AA%258D%25E8%25AD%2598%2F%3Ff%3D1%26grp%3Dproduct"}

ちなみにランダムフォレストをはじめとした機械学習手法に関して深く勉強したい方は以下の記事を見てみてください！

<https://toukei-lab.com/machine-learning-fresh>

<https://toukei-lab.com/ai-term>

またデータサイエンスや統計学について勉強したい方は以下の記事でまとめていますのでチェックしてみてください！

<https://toukei-lab.com/data-scientist>

<https://toukei-lab.com/statistics-freshman>

RとPythonで実装をおこなってきましたが、機械学習手法の実装にはPythonの方がオススメ！

Pythonの学習法は以下の記事でまとめていますのでぜひチェックしてみてください！

<https://toukei-lab.com/python-learn>

データサイエンスを幅広く学びたい方には当メディア運営のスクール「**[スタアカ](https://toukei-lab.com/achademy/)**」がオススメです！

:::box
[**業界最安級AIデータサイエンス特化スクール「スタアカ（スタビジアカデミー）」**](https://toukei-lab.com/achademy/)
:::

ご受講お待ちしております。