StratifiedKFold(sklearn)サンプルコードでイメージを掴む

 人工知能(AI)開発で使用するデータを扱うライブラリとして「sklearn」をよく見かけます。ここでは、k-分割交差検証(データセットを学習に適した形に分割)で利用する「StratifiedKFold」のサンプルコードから「sklearn」がどのようなライブラリなのかを肌で感じられるようにしてあります。ちなみに、k-分割交差検証はG検定でも出題される有名な手法です。また、「StratifiedKFold」は「KFold」のデータ分割に加えてデータの偏りを考慮したデータ分割が可能です。

機械学習ライブラリ(初級編)

『機械学習ライブラリ(初級編)』に戻る>>

pandasで様々な角度からデータ分析します

はじめに

k-分割交差検証の必要性

 人工知能(AI)の学習には『すべてのデータを使いません』。未知のもの予測するAIを作ることが目的なので、今あるデータを(1)学習用データ/(2)検証用データに分割して、(2)検証用データをAIの学習が進んでいるかを確認するための(仮想)未知のデータとして利用します。つまり、学習には使わないデータと言うことになります。また、より精度の高いAIを開発するためには、効率よく学習させるなど、手元にあるデータの特性を見極めて上手に利用する必要があります。その一つの手法がk-分割交差検証で、以降で紹介するStratifiedKFoldはデータ分割をサポートしてくれます。

k-分割交差検証の特徴
  • k個の学習用データ(1つをFoldと言う)を作る
  • 1Foldには学習データと検証データがある
  • データの選び方がk通りなのでKFold
    ※このときそれぞれのFoldで検証データは重複しない
  • AIをk回学習させて、それぞれの精度を比較

kは5個ぐらいが標準です。大きくするといくらでもAIの精度が上がるわけではありませんし、k個の異なるAIを作ることになるので、数が増えるごとに計算量が多くなって学習に時間がかかります。

データ準備

◆Kaggleにログインして『Titanicデータ(CSV)』をダウンロード。
 対象ファイルは「train.csv」です。

KaggleからTaitanicデータをダウンロード

Excelなどで内容を参照してみてください。

サンプルコード

IPython起動

 用意した『Titanicデータ(CSV)』のあるディレクトリでIPythonを起動します。起動方法が不明な場合、下記のリンクを参照してください。

データ分析とデータ分割

 「pandas」で読み込んで、データを観察します。注目するデータ列を見つけたら、データに印をつけていきます(次の例では0~4の番号を振る)。追加されたFold列を目印にして「k-分割交差検証」に利用するデータに分割ができることになります。

1.「pandas」で読み込み

1.「pandas」で読み込み

◆読み込み、表示します。

import pandas as pd
import numpy as np

df = pd.read_csv('./train.csv')
df
     PassengerId  Survived  Pclass  ...     Fare Cabin  Embarked
0              1         0       3  ...   7.2500   NaN         S
1              2         1       1  ...  71.2833   C85         C
2              3         1       3  ...   7.9250   NaN         S
3              4         1       1  ...  53.1000  C123         S
4              5         0       3  ...   8.0500   NaN         S
..           ...       ...     ...  ...      ...   ...       ...
886          887         0       2  ...  13.0000   NaN         S
887          888         1       1  ...  30.0000   B42         S
888          889         0       3  ...  23.4500   NaN         S
889          890         1       1  ...  30.0000  C148         C
890          891         0       3  ...   7.7500   NaN         Q

[891 rows x 12 columns]
--
データ数は891件、列数は12となっています。

◆行や列を指定して見やすくします。

  • 5行目まで(インデックスが0~4)
  • 列を「SibSp」~「Embarked」と指定
df.loc[:4,'SibSp':'Embarked']
   SibSp  Parch            Ticket     Fare Cabin Embarked
0      1      0         A/5 21171   7.2500   NaN        S
1      1      0          PC 17599  71.2833   C85        C
2      0      0  STON/O2. 3101282   7.9250   NaN        S
3      1      0            113803  53.1000  C123        S
4      0      0            373450   8.0500   NaN        S
--
少し見やすくなりました。
「NaN」はブランクを意味します。

◆注目するカラム(列名)を確認しておきます。

df.columns
Index(['PassengerId', 'Survived', 'Pclass', 'Name', 'Sex', 'Age', 'SibSp',
       'Parch', 'Ticket', 'Fare', 'Cabin', 'Embarked'],
      dtype='object')
--
12個あります。
「Embarked」列を後で利用します。

2.分割ターゲットの確認

2.分割ターゲットの確認

◆分割ターゲットは「Embarked」列にします。

  • データに偏りのある列を選びました
  • 確認ポイント
    • いくつ種類があるのか
    • それぞれ件数は何件か
df['Embarked'].value_counts()
S    644
C    168
Q     77
Name: Embarked, dtype: int64
--
3種類あります。
件数にばらつきがあり、「S」が多く「Q」は少ないです。

KaggleのTitanicデータの例では、「whether or not they survived(生き残ったかどうか=Survived列)」を判定するAIを作りたい(タスク)、としています。注目する列は、その判定に影響を与えそうな列を選ぶことが重要なのですが、ここではKFoldの特徴理解を優先して、列を選定しました。

3.前処理

3.前処理

◆「Embarked」列にブランクのデータがあります。

df[df['Embarked'].isnull()]
     PassengerId  Survived  Pclass  ...  Fare Cabin  Embarked
61            62         1       1  ...  80.0   B28       NaN
829          830         1       1  ...  80.0   B28       NaN

[2 rows x 12 columns]
--
2件ブランクデータがありました。

◆ブランクデータを削除します。
 KFoldに利用できないため、前処理として2行削除します。

print('before:'+str(len(df)))
df.drop(df[df['Embarked'].isnull()].index, inplace=True)
print('after:'+str(len(df)))
before:891
after:889
--
2件削除されました。

「Embarked」列を使ってStratifiedKFoldを利用するために必要な前処理について説明しました。 『AI学習用データセットのいろいろな入手方法』 で紹介したMNISTデータセットは、前処理済み(サイズ調整、文字が中心、傾き…など)です。AIの動きを理解する時によく使われ、きれいに整えられています。一般的には、AIの学習できるようにデータを整える前処理は必須です。

4.KFoldデータ分割

4.KFoldデータ分割

◆「sklearn」の「StratifiedKFold」をimportします。
 「KFold」と比較し、分割時にデータの偏りを考慮します。

  • オプション
    • k=5分割
    • ランダムシード=0
    • シャッフルする
from sklearn.model_selection import StratifiedKFold

skf = StratifiedKFold(n_splits=5, random_state=0, shuffle=True) #インスタンス作成
splits = skf.split(np.arange(len(df)), y=df["Embarked"].values)
type(splits)
generator
--
Pythonの「generator」型が作成されます。

◆「fold」列を追加して、印(0~4の番号)を付けます。

df["fold"] = -1 #「fold」列を追加(-1は初期値、なんでもOK)
for fold, (train_set, val_set) in enumerate(splits):
    df.loc[df.index[val_set], "fold"] = fold
    print(f'fold:{fold}')
print('')
print(f'train_set:{len(train_set)}')
print(f'val_set:{len(val_set)}')
fold:0
fold:1
fold:2
fold:3
fold:4

train_set:712
val_set:177
--
5分割なので、5回ループします。
ループが終わった後(fold:4)に見ていますが、学習データ(train_set)、検証データ(val_set)に分かれています。
※fold:0の場合は「Fold」列が「0」のデータが検証データ、それ以外が学習データになる

5.どのように分割されたか確認

5.どのように分割されたか確認

◆k=5つに分割されたデータを観察します。
 「C、Q、S」の件数(「value_counts」を利用)を見ると、偏りがないこともわかります。

df[['fold','Embarked']].value_counts(sort=False)
fold  Embarked
0     C            34
      Q            15
      S           129

1     C            34
      Q            15
      S           129

2     C            33
      Q            16
      S           129

3     C            33
      Q            16
      S           129

4     C            34
      Q            15
      S           128
dtype: int64
--
それぞれの件数を見てみます(改行を入れて見やすくしました)。
きれいに印(「fold」列)が付きました。

「fold」列で集計しています。0~4のどのFoldでも「Embarked」列が均等にばらつきのあるデータに分割されています。「k-分割交差検証」での「sklearn-StratifiedKFold」の利用を例に、機械学習ライブラリの役割を見てみました。イメージできたのではないでしょうか。他にも様々な種類がありますが、ここまでにします。

『機械学習ライブラリ(初級編)』に戻る>>

以上