ブートストラップ法 2018/05/14

MNISTデータセットを使って, ブートストラップ法を試す.


 ブートストラップといえば, webフレームワークの(Twitter-)bootstrapを想像する方もいらっしゃるかもしれませんが, 今回紹介するのはサンプリング手法の一種のブートストラップ法です. ブートストラップ法は, 再代入誤り率のバイアスを補正するために利用されます.
最大入誤り率なに? って声が聞こえてきそうですね... 再代入誤り率とは学習したデータと同じデータを使い精度評価した時の誤り率のことです. サンプルデータが真の分布に従ってないN個のサンプルから真の誤り率を求めることができる.

以下は手書き文字のデータセットであるMNISTデータセット用いて, サンプル数の バイアスはブートストラップサンプルを50回復元抽出して取得したものであり, 50通りの学習を行う必要があるので処理にものすごい時間がかかる. 簡単に試したい場合は復元抽出の回数(コードの中では50となっている繰り返しの数)かコードの中の変数sample_numを小さくするとよい.

#! /usr/bin/python3
import numpy as np
from sklearn.datasets import *
from sklearn.ensemble import RandomForestClassifier

sample_num = 10000

mnist = fetch_mldata('MNIST original', data_home=".")
mnist_data = np.array(mnist["data"])
mnist_label = np.array(mnist["target"])

### How to check a image using PIL library.###
# import PIL
# from PIL import Image
# test = Image.fromarray(np.uint8(mnist_data[0].reshape(28, 28)))
# rgb_img = Image.merge("RGB", (test,test,test)) 
# rgb_img.save("test.JPG", 'JPEG')

sample_num_ori = []
for i in range(1, 70001):
  sample_num_ori.append(i)
sample_num_ori_np = np.array(sample_num_ori)
sample_random_num_np = np.random.choice(sample_num_ori_np, sample_num, replace=False)

# getting training data for bootstrap method.
train = mnist_data[sample_random_num_np]
label = mnist_label[sample_random_num_np]

# sampling with replacement
bias_sum = []
for i in range(1, 101):
  sample_num_resample = []
  for j in range(1, sample_num+1):
    sample_num_resample.append(j)
  resample = np.random.choice(np.array(sample_num_resample), sample_num, replace=True)
  train_resample = train[resample-1]
  label_resample = label[resample-1]

  clf = RandomForestClassifier(n_estimators=10,n_jobs=2)
  clf.fit(train_resample, label_resample)

  Na_predict = clf.predict(train_resample)
  Na_difference = Na_predict - label_resample
  Na_defference_num = Na_difference-1
  Na_seikai = 0
  for j in range(0, Na_difference.shape[0]):
    if Na_difference[j] == 0:
      Na_seikai = Na_seikai + 1
  e_Na_Na = 1 - (Na_seikai / Na_difference.shape[0])
  
  N_predict = clf.predict(train)
  N_difference = N_predict - label
  N_defference_num = N_difference-1
  N_seikai = 0
  for j in range(0, N_difference.shape[0]):
    if N_difference[j] == 0:
      N_seikai = N_seikai + 1
  e_Na_N = 1 - (N_seikai / N_difference.shape[0])
  
  bias = e_Na_Na - e_Na_N
  bias_sum.append(bias) 

bias_average = np.sum(np.array(bias_sum))/50

clf = RandomForestClassifier(n_estimators=10,n_jobs=2)
clf.fit(train, label)

N_predict = clf.predict(train)
N_difference = N_predict - label
N_defference_num = N_difference-1
N_seikai = 0
for i in range(0, N_difference.shape[0]):
  if N_difference[i] == 0:
    N_seikai = N_seikai + 1
e_N_N = 1 - (N_seikai / N_difference.shape[0])

e = e_N_N - bias_average 
print("真の誤識別率は " + str(e))

Nを10000にして試したところ真の誤り率はε = 3.8304となった.

 母集団の分布(例えば, 正規分布とか)が分かっているかつ, 標本から推定された, パラメータ(正規分布の場合は期待値や標準偏差)を利用できる場合には, より高精度に標本の分布と真の分布との誤差(真の誤識別率)を算出することができるパラメトリック・ブートストラップという方法もある. パラメトリック・ブートストラップ法では母集団の分布型ならびに推定されたパラメータを利用して 乱数を発生させて, それをブートストラップ標本とする.