
train_test_split 関数
train_test_split 関数は、Scikit-learn(機械学習用のPythonライブラリ)に属しています。
この関数を使うことで、データセットを訓練用データとテスト用データに簡単に分割することができます。
その前に特徴量とターゲットの説明をしておきましょう。
特徴量(Features)とは?
特徴量とは、モデルが学習するために使用するデータのことです。
例:
身長(cm)
体重(kg)
年齢(歳)
これらの特徴量を使用して、ターゲット変数を予測するためのモデルを作成します。
ターゲット(Target)
ターゲットとは、モデルが予測しようとするデータのことです。
例:
上記の特徴量を使って、人があるスポーツに適しているかどうかを予測する場合、ターゲット変数は「スポーツ適性」となります。例えば、「スポーツ適性」は0(適していない)または1(適している)といったラベルで表されることがあります。

train_test_split関数の基本的な使い方
from sklearn.model_selection import train_test_split
# データセットの作成(例として、特徴量とラベルの配列)
X = [[1, 2], [3, 4], [5, 6], [7, 8], [9, 10]]
y = [0, 1, 0, 1, 0]
# データの分割
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
引数の詳細
train_test_split 関数の主な引数は以下の通りです:
X: 特徴量データセット(2次元配列やデータフレーム)
訓練や予測に使用するデータです。
行毎に1つのデータポイントを表し、各列はそれぞれの特徴を表します。
y: ターゲットデータセット(1次元配列やデータフレーム)
予測する目標値やラベルです。各行は対応する特徴量の行に対応します。
test_size: テストデータの割合または個数(省略可能)
データの何割をテストデータとして使用するかを指定します。0より大きく1未満の小数(例えば、0.2)を指定すると、全データの20%をテストデータとして使用します。
または整数を指定することで、テストデータの個数を直接指定することもできます。
train_size: 訓練データの割合または個数(省略可能)
データの何割を訓練データとして使用するかを指定します。test_sizeと同様に小数や整数を使用できます。通常は省略します。
random_state: 乱数シード(省略可能)
乱数シードを指定することで、データの分割が再現可能になります。同じシード値を使えば、何度実行しても同じ結果が得られます。再現性を持たせたい場合に便利です。
shuffle: データをシャッフルするかどうか(省略可能、デフォルトはTrue)
データをシャッフル(ランダムに並び替える)するかどうかを指定します。通常はシャッフルします。
シャッフルすると、データがランダムに並び替えられ、訓練データとテストデータがバランス良く分かれます。
シャッフルしない場合はデータの順序が維持され、特定の順序に基づくバイアスが生じる可能性があります。
stratify: ストラティファイドサンプリング(層化抽出)のための配列(省略可能)
y配列を指定すると、各クラスの割合を保ちながらデータを分割します。クラス不均衡があるデータセットで使用されます。
(クラス不均衡は後で解説します。)
3. 返り値
train_test_split 関数は以下の4つのデータセットを返します:
X_train: 訓練用の特徴量データセット
X_test: テスト用の特徴量データセット
y_train: 訓練用のターゲットデータセット
y_test: テスト用のターゲットデータセット
4. 具体例
train_test_split 関数を使ってデータを分割し、その内容を表示する具体例です。
import numpy as np
from sklearn.model_selection import train_test_split
# 特徴量データの作成
X = np.array([[1, 2], [3, 4], [5, 6], [7, 8], [9, 10]])
# ターゲットデータの作成
y = np.array([0, 1, 0, 1, 0])
# データの分割
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# 分割されたデータの表示
print("X_train:", X_train)
print("X_test:", X_test)
print("y_train:", y_train)
print("y_test:", y_test)
この例では、データセットを80%:20%の割合で訓練用とテスト用に分割し、乱数シードを設定して再現性を持たせています。
結果
X_train: [[7 8]
[3 4]
[1 2]
[5 6]]
X_test: [[ 9 10]]
y_train: [1 1 0 0]
y_test: [0]
X_train: 訓練用の特徴量データセット
X_test: テスト用の特徴量データセット
y_train: 訓練用のターゲットデータセット
y_test: テスト用のターゲットデータセット
これにより、データの一部を訓練用としてモデルの学習に使用し、残りのデータをテスト用としてモデルの性能評価に使用します。
クラス不均衡とは?
例えば、病気の診断データセットがあるとします。
その中で「陽性(病気あり)」のデータポイントが少なく、「陰性(病気なし)」のデータポイントが非常に多い場合、このデータセットはクラス不均衡があるといいます。
具体例:
- 陽性(病気あり): 10例
- 陰性(病気なし): 90例
この場合、陽性のデータは全体の10%、陰性のデータが90%と大きく比率が異なります。
このような状況をクラス不均衡と呼びます。
クラス不均衡の問題
モデルのバイアス:
データの分布が偏っているため、機械学習モデルは多数派のクラス(この例では陰性)に偏った予測をしやすくなります。性能評価の難しさ:
クラス不均衡がある場合、モデルの精度(accuracy)だけでは性能を正しく評価できません。
例えば、単純に陰性と予測するだけであれば90%の精度が得られますが、陽性は正しく予測できていないかもしれません。
例:
from sklearn.model_selection import train_test_split
import numpy as np
# 特徴量データの作成
X = np.arange(100).reshape(-1, 1) # 100個のデータポイント
# ターゲットデータの作成(極端なクラス不均衡の例)
y = np.concatenate([np.ones(5), np.zeros(95)]) # 5個の陽性(1)と95個の陰性(0)
print("全データセットのクラス分布:")
print("陽性(1): {:.2f}".format(sum(y == 1) / len(y)))
print("陰性(0): {:.2f}".format(sum(y == 0) / len(y)))
# データの分割(stratify を使用しない場合)
X_train_ns, X_test_ns, y_train_ns, y_test_ns = train_test_split(X, y, test_size=0.2, random_state=42, stratify=None)
print("\nstratify を使用しない場合のクラス分布:")
print("訓練データ中のクラス分布: {{0: {:.2f}, 1: {:.2f}}}".format(sum(y_train_ns == 0) / len(y_train_ns), sum(y_train_ns == 1) / len(y_train_ns)))
print("テストデータ中のクラス分布: {{0: {:.2f}, 1: {:.2f}}}".format(sum(y_test_ns == 0) / len(y_test_ns), sum(y_test_ns == 1) / len(y_test_ns)))
# データの分割(stratify を使用する場合)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42, stratify=y)
print("\nstratify を使用する場合のクラス分布:")
print("訓練データ中のクラス分布: {{0: {:.2f}, 1: {:.2f}}}".format(sum(y_train == 0) / len(y_train), sum(y_train == 1) / len(y_train)))
print("テストデータ中のクラス分布: {{0: {:.2f}, 1: {:.2f}}}".format(sum(y_test == 0) / len(y_test), sum(y_test == 1) / len(y_test)))
このコードを実行すると、stratifyの有無でどう違うかがわかります。
データセットのクラス分布:
陽性(1): 0.05
陰性(0): 0.95
stratify を使用しない場合のクラス分布:
訓練データ中のクラス分布: {0: 0.96, 1: 0.04}
テストデータ中のクラス分布: {0: 0.90, 1: 0.10}
stratify を使用する場合のクラス分布:
訓練データ中のクラス分布: {0: 0.95, 1: 0.05}
テストデータ中のクラス分布: {0: 0.95, 1: 0.05}