Scikit-LearnはPythonで最も広く使われている機械学習ライブラリの1つです。
最適化されており、効率的で、高レベルのAPIはシンプルで使いやすい。
Scikit-Learnには便利なツールやメソッドがたくさんあり、前処理や評価など手間のかかる処理も1つのメソッドを呼び出すだけで簡単に行うことができます。
一般的に、データを分割する際のルールは80/20です。
つまり、データの80%をモデルのトレーニングに使用し、20%をテストに使用します。
これはあなたが扱っているデータセットによりますが、80/20の分割は非常に一般的で、ほとんどのデータセットでうまくいくことでしょう。
このガイドでは、Scikit-Learn の split_train_test()
メソッドの使い方と、分割処理を制御するためのパラメータの設定方法について説明します。
Scikit-Learnのインストール
まだインストールされていなければ、Scikit-Learn は pip
経由で簡単にインストールすることができます。
$ pip install scikit-learn
インストールが完了したら、ライブラリ自体をインポートすることができます。
import sklearn
ほとんどの場合、ライブラリ全体をインポートすることは避け、特定のクラスやモジュールをインポートすることになるでしょう。
注:Scikit-Learnを使用する場合、インポートリストが膨大になる傾向があります。
トレーニングセットとテストセットの重要性
機械学習で(基本的な)モデルを学習する際の最も一般的な手順は、大まかな流れが同じである。
- モデルへ投入するデータの取得と処理。
Scikit-learnにはモデルの学習に使う様々なデータセット(虹彩、糖尿病、数字…)があり、主にベンチマークや学習のためにロードして使用します。
- データセットをトレーニングセットとテストセットに分割する
- モデルの構築とアーキテクチャの定義
- モデルのコンパイル
- モデルのトレーニング
- 結果の検証
トレーニングセットはデータセット全体のサブセットであり、通常、データ全体に対してモデルを学習させることはありません。
非生成モデルでは、トレーニングセットには通常メインデータセットの80%程度のデータが含まれます。
その名の通り、モデルの学習に使用される。
この手順は、モデルの適合とも呼ばれる。
しかし、このルールには例外がある。
例えば、画像を生成するGAN(Generative Adversarial Networks)を学習させる場合、その結果をどのようにテストするのでしょうか。
今まで見たこともないような新しいインスタンスを表すので、場合によっては非常に主観的になってしまいます。
少なくとも現状では、ほとんどの生成モデルでは、人間が出力を判断する必要があり、その場合、テストセットは完全に冗長となります。
また、クロスバリデーションなどの手法を使う場合、学習データからあまり多くを「奪わない」ために、テストデータをほんの少し少なくしたい場合があります。
例えば、あるデータセットに1,000,000個のインスタンスがあったとして、そのうちの5%だけをテストセットとして使用すると、50,000個のインスタンスとなり、どのモデルでも十分すぎるほどテストすることができます。
テストセットはデータセット全体のサブセットであり、モデルを評価し、トレーニングセットからどの程度学習したかを確認するために使用されます。
ということです。
モデルは評価前にテストセットと対話したり、見たりしてはいけません。
そうでなければ、モデルをテストしているとは言えません。
バリデーションセットとは?
バリデーションセットは、プロフェッショナルモデルやアカデミックモデルでよく目にするものです。
バリデーションセットはトレーニングセットから取り出され、モデルの精度をおおよそ検証するためにトレーニング中に使用されます。
テストセットはモデルの学習が終了するまで完全に切り離されています – しかし、検証セットは学習中に検証するために使用されます。
注意:検証セットは学習には使用されず、モデルもそのデータで学習することはありません。
現在のエポックを検証するだけです。
このように、モデルは間接的にデータを使って学習しますが、そのデータは事前信念に影響を与えるので、検証セットをテストに使うことはできません。
自分の知識が間違っていると聞けば、たとえその理由がわからなくても、より深く学ぶことができるのと同じです。
これが、検証セットがモデルの精度を近似する理由であり、検証セットを使用する場合でもテストセットが必要な理由です。
テストセットは、学習中のモデルの実際の性能を近似するのに役立ちます。
そのため、テストセットでテストした後、気づかないうちに幻のオーバーフィットモデルになってしまうことがありません。
また、検証セットを使ってモデルをチューニングし、テストセットにさらさずにその能力をおおよそ評価することができます。
Kerasなどのディープラーニングフレームワークは、オーバーフィットの良い兆候として、通常のトレーニングの「精度」の他に「val_accuracy」を表示することができます。
もし、乖離し始めたら、学習中にモデルがオーバーフィットしていることになるので、十分に乖離したら、さらに学習する時間を無駄にする必要はありません。
さらに、 EarlyStopping
などのコールバックを利用すると、 val_accuracy
が n
エポック以降も改善しない場合、たとえ accuracy
が増加していても、モデルの学習を自動的に停止させることができます。
>
> バリデーションセットの作成は簡単です。
文字通り、既に train_test_split()
メソッドで分割された学習セットに対して train_test_split()
メソッドを実行し、そこから検証セットを抽出するだけです。
あるいは、手動で検証セットを作成することもできます。
検証セットのサイズは、通常テストセットと同じように分割します。
トレーニングセットの 10-20% 程度が一般的です。
巨大なデータセットの場合はこれよりもずっと小さくできますが、小さなデータセットの場合は大きくしすぎてしまい、モデルがトレーニングセットのデータにフィットしにくくなってしまいます。
>
> 以降では、同じ train_test_split()
メソッドを使って、検証セットも取り出すことにします。
Scikit-Learnのデータセット モジュール
Scikit-Learn にはクリーンで人気のあるデータセットがいくつか組み込まれており、学習中や簡単なタスクでモデルのベンチマークを行う際によく利用されています。
Pythonの機械学習に関するリソースを読んだことがある方は、これらの最も人気のあるデータセットを見たことがあるのではないでしょうか。
- Iris – 3クラス(花)のセット、各クラス50サンプル、分類に使用されます。
- Diabetes – 合計442のサンプルを持つセット、回帰に使用。
- Digits – 10クラス(手書きの数字)のセット、1クラスあたり~180サンプル、分類に使用されます。
- Wine – 3クラス(ワイン)のセット、総サンプル数178、分類に使用。
これらのデータセットは datasets
モジュールを通して、それぞれの関数で読み込むことができる。
from sklearn import datasets
X_iris, y_iris = datasets.load_iris(return_X_y=True)
X_diabetes, y_diabetes = datasets.load_diabetes(return_X_y=True)
X_digits, y_digits = datasets.load_digits(return_X_y=True)
X_wine, y_wine = datasets.load_wine(return_X_y=True)
あるいは、特定の関数をロードすることもできます。
from sklearn.datasets import load_iris
from sklearn.datasets import load_diabetes
from sklearn.datasets import load_digits
from sklearn.datasets import load_wine
X_iris, y_iris = load_iris(return_X_y=True)
X_diabetes, y_diabetes = load_diabetes(return_X_y=True)
X_digits, y_digits = load_digits(return_X_y=True)
X_wine, y_wine = load_wine(return_X_y=True)
デフォルトでは、これらのメソッドはデータとターゲット(データとそのクラス)を含む Bunch
オブジェクトを返します。
しかし、 return_X_y
引数を True
に設定すると、 (data, targets)
というタプルが返され、学習対象のデータと分類器/回帰モデルがヒットさせたいターゲットクラスが表現されます。
train_test_split() によるデータセットの分割
train_test_split()メソッドは
sklearn.model_selection` モジュールに含まれる。
from sklearn.model_selection import train_test_split
このメソッドにはいくつかの引数があり、デフォルトでは75/25の割合で分割されます。
実際には、Scikit-Learnのすべてのデフォルト値はかなり合理的で、ほとんどのタスクでうまく機能するように設定されています。
しかし、これらのデフォルトがどのようなものであるかは、それほどうまく機能しない場合に知っておくとよいでしょう。
主な引数は train_size
と test_size
で、値の範囲は 0
と 1
で、それらの合計は 1
でなければならない。
これらの値はデータセットに対する割合を表すので、例えば train_size
のように1つだけ定義しても、 test_size
は 1 - train_size
となり、その逆もまた然りである。
train_sizeとtest_size引数の設定
X_train,
X_test,
y_train,
y_test` の4つのサブセットを残す、最も一般的な方法である。
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
X, y = load_iris(return_X_y=True)
X_train, X_test, y_train, y_test = train_test_split(X, y)
print(X_train.shape)
print(X_test.shape)
print(y_train.shape)
print(y_test.shape)
train_sizeと
test_sizeのどちらも設定しない場合、デフォルト値が適用され、
test_sizeは
0.25に設定され、
train_sizeはその補完値 (
0.75`) となります。
(112, 4)
(38, 4)
(112,)
(38,)
X_trainセットには 112 個のインスタンスがあり、
X_test` セットには 38 個のインスタンスがあるため、訓練セットとテストセットは 75%/25% に分割されていることがわかります。
他の分割比率は以下の通りです。
80%/20% (非常に一般的), 67%/33%, より稀に 50%/50% です。
これらの設定は、train_test_split()
メソッドの引数のどちらか、または両方を定義することで実現できます。
X_train, X_test, y_train, y_test = train_test_split(X, y, train_size=0.8)
X_train, X_test, y_train, y_test = train_test_split(X, y, train_size=0.8, test_size=0.2)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)
これらの3つの分割は、すべて同じ分割になります。
(120, 4)
(30, 4)
(120,)
(30,)
train_test_split()によるValidation Setの作成
Validation Setは学習時にとても便利で、データサイエンティストとしての生活をかなり楽にしてくれます。
>
可能な限り、検証セットを使用するようにしましょう。
トレーニングセットからバリデーションセットを抽出する関数は組み込まれていませんが、結局のところ前と同じように分割するだけなので、同じ train_test_split()
メソッドを使用してはどうでしょうか。
これを再利用して、トレーニングセットから10%のデータを取り出して、検証セットを手に入れましょう。
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
X, y = load_iris(return_X_y=True)
X_train, X_test, y_train, y_test = train_test_split(X, y, train_size=0.8)
X_train, X_valid, y_train, y_valid = train_test_split(X_train, y_train, train_size=0.9)
print(X_train.shape)
print(X_test.shape)
print(X_valid.shape)
すでに分割されている X_train
から10%を分割しているので、70%-20%-10%の分割にはなりませんが、実際には72%-20%-8%の分割になります。
(108, 4)
(30, 4)
(12, 4)
これを考慮し、手動で別の数値を設定するか、あるいは事前に比率を定義しておき、切り捨てたサイズではなく元のサイズを参照するように更新された分割を計算することができます。
データをトレーニングセット、テストセット、バリデーションセットに比例して分割するためには、2回目の関数コールで test_size
引数を設定する必要があります。
tests=validationr/(trainr+testr)tests=validationr/(trainr+testr)
test_s = validation_r/(train_r+test_r)
Diabetesの方がインスタンス数が多いので、そちらをロードしてみましょう(丸めの関係で、小さいデータセットでは同じ比率でも微妙に異なる分割になることがあります)。
from sklearn.datasets import load_diabetes
from sklearn.model_selection import train_test_split
X, y = load_diabetes(return_X_y=True)
print(X.shape)
(442, 10)
80%/10%/10% の分割を目指すとすると、それぞれ 352
, 45
, 45
インスタンスにしたいと思います。
これらの比率を定義し、train_test_split()
関数でデータセットをトレーニング、テスト、検証の各セットに分割しましょう。
from sklearn.datasets import load_diabetes
from sklearn.model_selection import train_test_split
train_ratio = 0.80
test_ratio = 0.10
validation_ratio = 0.10
X, y = load_diabetes(return_X_y=True)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=test_ratio)
X_train, X_valid, y_train, y_valid = train_test_split(X_train, y_train, test_size=validation_ratio/(train_ratio+test_ratio))
print(X_train.shape)
print(X_test.shape)
print(X_valid.shape)
この結果、以下のようになります。
(352, 10)
(45, 10)
(45, 10)
すごい! データセットが3つのセットに分割されました。
これでモデルに投入して、学習中に検証を行い、ハイパーパラメータを調整することができます。
層別分割
データセット内のクラスごとにサンプル数が異なることがある。
例えば、あるクラスには100個のサンプルがあり、2番目のクラスには50個、3番目のクラスには30個、といった具合である。
これを無視して分割すると、分類モデルを学習するときに問題が発生します(ただし、回帰モデルはこの問題が発生しません)。
>
gt; クラスの比率を維持するように、何らかの方法で集合を分割するのがベストです。
これが層別分割です。
幸いなことに、train_test_split
メソッドには stratify
という引数があり、これはクラスごとのサンプル数を定義する配列を取るので、分割する際に比率を保つことができます。
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
X, y = load_iris(return_X_y=True)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, stratify=y)
多くの場合、データセットから y
という NumPy 配列を使用することで、適切な stratify
分割配列にすることができます。
これにより、モデルはクラスのインスタンス間のバランスの欠如と戦うことができ、いくつかのクラスに偏ることが少なくなります。
結論
このガイドでは、Scikit-Learnライブラリとその datasets
モジュールのいくつかに親しんでいただきました。
トレーニングセット、テストセット、検証セットが何であるか、それらがどこに適用されるか、そしてモデルを検証することの利点を学びました。
また、train_test_split()
メソッドを使用してデータをトレーニングセットとテストセットに分割する方法と、検証セットを分割してこれらのセットの比率を動的に保持する方法について見てきました。