Python, scikit-learn

【scikit-learn】ホールドアウト検証の実装


機械学習モデルの性能を評価する手法の1つにホールドアウト検証があります。

本記事では、Pythonの機械学習ライブラリであるscikit-learnを活用してホールドアウト検証を行う処理の実装方法について解説します。

そもそも「ホールドアウト検証」とは?

まずはホールドアウト検証とは何かについて簡単に説明します。

機械学習モデルの性能評価手法

機械学習モデルのテストでは、任意のデータに対して予測を行い、その予測がどの程度正しいかを調べます。

テストを行う際、モデルの学習に使用したデータは使用することができません。

何故ならモデルは学習データに対して正しい予測ができるように学習を進めるため、学習の完了したモデルに学習データを入力すればほぼ必ず正しい予測が得られるからです。

既に答えを知っている問題でテストをしても適切な性能評価は行えません。

そのため、機械学習モデルを構築する際は、元のデータセットを学習用とテスト用に分割して使用するのが一般的です。

このやり方のことをホールドアウト検証と言います。

「ホールドアウト検証」の実装例を紹介

それでは、ホールドアウト検証の実装例をいくつか紹介しましょう。

なお、紹介するサンプルプログラムの言語にはPythonを使用しており、機械学習ライブラリのscikit-learnを活用してモデルを構築しています。

実装例01. train_test_splitメソッドを利用する

最も簡単な方法はscikit-learnのtrain_test_splitメソッドを利用する方法です。

train_test_splitメソッドは与えられたリストを複数個に分割するメソッドであり、このメソッドを利用すれば簡単にホールドアウト検証を実装できます。

train_test_splitメソッドに設定できる引数と戻り値は以下の通りです。

  • 【引数】分割対象のリスト(複数指定可)
  • 【引数】test_size(int or float):テストデータの件数or比率
  • 【引数】train_size(int or float):学習データの件数or比率
  • 【引数】random_state(int or None):分割に使用する乱数のシード
  • 【引数】shuffle(bool):データをシャッフルするか否か
  • 【戻り値】分割後のリスト(複数個)

この方法でのホールドアウト検証の実装例を以下に示します。

from sklearn.model_selection import train_test_split
from sklearn.neural_network import MLPClassifier
from sklearn.datasets import load_iris

if __name__ == '__main__':

    test_size = 50 # テストに用いるデータの件数を指定
    random_state = 0 # データ分割に用いる乱数のシードを指定

    # 実験用データセットの読み込み
    iris = load_iris()
    x = iris.data
    y = iris.target

    # データセットの分割(学習データとテストデータ)
    train_x, test_x, train_y, test_y = train_test_split(
    x, y, test_size=test_size, random_state=random_state)

    # モデルの学習
    mlp = MLPClassifier(random_state=0)
    mlp.fit(train_x, train_y)

    # 学習データの件数、テストデータの件数、テストの結果を表示
    print('Train data size: {0}'.format(len(train_x)))
    print('Test data size: {0}'.format(len(test_x)))
    print('Test Score: {0}'.format(mlp.score(test_x, test_y)))

このプログラムを実行すると以下の出力結果が得られます。

Train data size: 100
Test data size: 50
Test Score: 0.98

個人的にはこの方法が最も簡単なので、特に理由がなければこの通りに実装することをおすすめします。

実装例02. スライスによるリスト分割を利用する

もう一つはスライスによるリスト分割を利用する方法です。

スライス操作によってリストを任意のインデックス以前と以後に分割することでホールドアウト検証を実装します。

from sklearn.neural_network import MLPClassifier
from sklearn.datasets import load_iris
import random

if __name__ == '__main__':

    # テストに用いるデータの件数を指定
    test_size = 50

    # 実験用データセットの読み込み
    iris = load_iris()
    x = iris.data
    y = iris.target

    # データセットのシャッフル
    pair = list(zip(x,y))
    random.shuffle(pair)
    tuple_x, tuple_y = zip(*pair)
    x = list(tuple_x)
    y = list(tuple_y)

    # データセットの分割(学習データテストデータ)
    train_x = x[test_size:]
    test_x = x[:test_size]
    train_y = y[test_size:]
    test_y = y[:test_size]

    # モデルの学習
    mlp = MLPClassifier(random_state=0)
    mlp.fit(train_x, train_y)

    # 学習データの件数、テストデータの件数、テストの結果を表示
    print('Train data size: {0}'.format(len(train_x)))
    print('Test data size: {0}'.format(len(test_x)))
    print('Test Score: {0}'.format(mlp.score(test_x, test_y)))

このプログラムを実行すると以下の出力結果が得られます。

Train data size: 100
Test data size: 50
Test Score: 0.98

なお、スライス操作だけでは元のデータセットを格納したリストの中身が[0,0…1,1…2,2]のように特定のグループごとの塊になっている場合は上手く分割できません。

分割後のリストの中身に偏りが出てしまうからです。

これは元のデータセットの中身をシャッフルする処理を同時に実装すれば解決できますが、少し面倒なのでこのやり方はあまりおすすめしません。

※train_test_splitメソッドでは、元のデータセットのシャッフルは自動で行われます。