データセットの標準化については、このブログでも何回か扱っている。 しかし、実際にデータセットを標準化したときの例については試していなかった。
そこで、今回は UCI の提供する小麦 (seeds) データセットを最近傍法で分類したときに、スコアが上がる様を見てみたいと思う。 あらかじめ、いくつかの説明変数が教師信号として与えられるので、そこから小麦の品種を分類 (Classification) する。
UCI Machine Learning Repository: seeds Data Set
今回使った環境は次の通り。
$ sw_vers ProductName: Mac OS X ProductVersion: 10.12.3 BuildVersion: 16D32 $ python --version Python 3.5.3
下準備
まずは今回使う Python のパッケージを pip でインストールする。 どれも科学計算系で定番のやつ。
$ pip install numpy pandas scipy scikit-learn
データセットを標準化しない場合
今回使う最近傍法というアルゴリズムでは、分類したい点から最も近くにあるデータの種別を使って分類する。 近さにはユークリッド距離を使うため、データセットの説明変数の大きさや単位に影響を受けやすい。 例えば説明変数の中に 1,000m ~ 10,000m を取る次元と 0.1cm ~ 1cm を取る次元があるとしよう。 当然ながら、ユークリッド距離を計算するとそのままでは前者の次元の影響が大きくなってしまう。 今回使うデータセットでも原理的には同じ問題が発生する。
次のサンプルコードでは、データセットを標準化しない状態で最近傍法を使った分類を実施している。 そして Leave-One-Out 法を使って、計測した汎化性能を出力する。
#!/usr/bin/env python # -*- coding: utf-8 -*- import pandas as pd from sklearn.model_selection import LeaveOneOut from sklearn.metrics import accuracy_score from sklearn.neighbors import KNeighborsClassifier def main(): # 小麦データセットをダウンロードする dataset_url = 'https://archive.ics.uci.edu/ml/machine-learning-databases/00236/seeds_dataset.txt' # noqa df = pd.read_csv(dataset_url, delim_whitespace=True, header=None) # データフレームから説明変数と目的変数を取り出す features = df.loc[:, :6].get_values() targets = df.loc[:, 7].get_values() # 予測した結果の入れ物 predicted_labels = [] # LOO で交差検証する loo = LeaveOneOut() for train, test in loo.split(features): train_data = features[train] target_data = targets[train] # k-NN 法を使う model = KNeighborsClassifier(n_neighbors=1) # 訓練データを学習させる model.fit(train_data, target_data) # テストデータを予測させる predicted_label = model.predict(features[test]) # 予測した結果を追加する predicted_labels.append(predicted_label) # 正解率を出力する score = accuracy_score(targets, predicted_labels) print(score) if __name__ == '__main__': main()
上記のサンプルコードの実行結果は次の通り。 データセットを標準化しない状態では約 90.5% の汎化性能が得られた。
$ python withoutnorm.py
0.904761904762
データセットを標準化する場合
それでは、次はデータセットを標準化する場合を試してみよう。
標準化されたデータセットでは、説明変数の各次元の値が平均 0
で標準偏差が 1
になる。
つまり、元々の単位や大きさは無くなってそれぞれの値の間隔の比率だけが残されることになる。
次のサンプルコードでは、先ほどとデータセットを標準化するところだけ変更している。
データセットの標準化には scikit-learn に用意されている StandardScaler
を用いた。
#!/usr/bin/env python # -*- coding: utf-8 -*- import pandas as pd from sklearn.model_selection import LeaveOneOut from sklearn.metrics import accuracy_score from sklearn.neighbors import KNeighborsClassifier from sklearn.preprocessing import StandardScaler def main(): dataset_url = 'https://archive.ics.uci.edu/ml/machine-learning-databases/00236/seeds_dataset.txt' # noqa df = pd.read_csv(dataset_url, delim_whitespace=True, header=None) features = df.loc[:, :6].get_values() targets = df.loc[:, 7].get_values() # データセットを Z-Score に標準化する sc = StandardScaler() sc.fit(features) normalized_features = sc.transform(features) predicted_labels = [] loo = LeaveOneOut() for train, test in loo.split(normalized_features): train_data = normalized_features[train] target_data = targets[train] model = KNeighborsClassifier(n_neighbors=1) model.fit(train_data, target_data) predicted_label = model.predict(normalized_features[test]) predicted_labels.append(predicted_label) score = accuracy_score(targets, predicted_labels) print(score) if __name__ == '__main__': main()
上記の実行結果は次の通り。 今度は汎化性能が約 93.8% に上昇している。 データセットを標準化するだけで分類精度が 3.3% 上がった。
$ python withnorm.py
0.938095238095
まとめ
データセットを標準化することで、使う機械学習アルゴリズムによっては汎化性能を上げることができる。
スマートPythonプログラミング: Pythonのより良い書き方を学ぶ
- 作者: もみじあめ
- 発売日: 2016/03/12
- メディア: Kindle版
- この商品を含むブログ (1件) を見る