Scikit-Learn チュートリアル: インストール方法と Scikit-Learn の例

Scikit-learnとは何ですか?

シキット学習 オープンソースです Python 機械学習用のライブラリ。KNN、XGBoost、ランダムフォレスト、SVM などの最先端のアルゴリズムをサポートしています。NumPy 上に構築されています。Scikit-learn は、Kaggle コンテストや著名なテクノロジー企業で広く使用されています。前処理、次元削減 (パラメーター選択)、分類、回帰、クラスタリング、モデル選択に役立ちます。

Scikit-learn には、すべてのオープンソース ライブラリに関する最良のドキュメントがあります。 インタラクティブなチャートを提供します。 https://scikit-learn.org/stable/tutorial/machine_learning_map/index.html.

Scikit Learnの仕組み
Scikit Learnの仕組み

Scikit-learn の使用はそれほど難しくなく、優れた結果が得られます。 ただし、scikit learn は並列計算をサポートしていません。 これを使用して深層学習アルゴリズムを実行することは可能ですが、特に TensorFlow の使用方法を知っている場合には、最適なソリューションではありません。

Scikit-learn をダウンロードしてインストールする方法

今これで Python Scikit-learn チュートリアルでは、Scikit-learn をダウンロードしてインストールする方法を学びます。

オプション1: AWS

scikit-learn は AWS 経由で使用できます。 お願いします 参照する scikit-learn がプリインストールされている docker イメージ。

開発者バージョンを使用するには、次のコマンドを使用します。 Jupyter

import sys
!{sys.executable} -m pip install git+git://github.com/scikit-learn/scikit-learn.git

オプション2: マックとか Windows アナコンダを使用する

Anaconda のインストールについて詳しくは、以下を参照してください。 https://www.guru99.com/download-install-tensorflow.html

最近、scikit の開発者は、現在のバージョンが直面する一般的な問題に取り組む開発バージョンをリリースしました。 現在のバージョンではなく開発者バージョンを使用する方が便利であることがわかりました。

Conda 環境で scikit-learn をインストールする方法

conda 環境で scikit-learn をインストールした場合は、手順に従ってバージョン 0.20 に更新してください。

ステップ1) tensorflow 環境をアクティブ化する

source activate hello-tf

ステップ2) conda コマンドを使用して scikit lean を削除する

conda remove scikit-learn

ステップ3) 開発者版をインストールします。
scikit learn 開発者バージョンを必要なライブラリとともにインストールします。

conda install -c anaconda git
pip install Cython
pip install h5py
pip install git+git://github.com/scikit-learn/scikit-learn.git

注: Windows ユーザーがインストールする必要があります Microsoft ビジュアル C++ 14. ここから入手できます こちら

機械学習を使用した Scikit-Learn の例

この Scikit チュートリアルは XNUMX つの部分に分かれています。

  1. scikit-learn による機械学習
  2. LIME でモデルを信頼する方法

最初の部分では、パイプラインの構築方法、モデルの作成方法、ハイパーパラメータの調整方法について詳しく説明し、2 番目の部分では、モデル選択に関する最先端の情報を提供します。

ステップ 1) データをインポートする

この Scikit 学習チュートリアルでは、アダルト データセットを使用します。

このデータセットの背景については、を参照してください。記述統計について詳しく知りたい場合は、Dive ツールとOverview ツールを使用してください。

参照する このチュートリアル ダイブと概要について詳しく知る

Pandas を使用してデータセットをインポートします。 連続変数の型を float 形式に変換する必要があることに注意してください。

このデータセットには XNUMX つのカテゴリ変数が含まれています。

カテゴリカル変数は CATE_FEATURES にリストされています。

  • ワーククラス
  • 教育
  • 夫婦の
  • 職業
  • 関係
  • レース
  • セックス
  • 母国

さらに、XNUMX つの連続変数:

連続変数は CONTI_FEATURES にリストされています。

  • 年齢
  • fnlwgt
  • 教育番号
  • 資本利得
  • 資本損失
  • 時間_週

どの列が使用されているかをよりよく理解できるように、リストを手動で入力していることに注意してください。 カテゴリまたは連続のリストをより迅速に作成する方法は、次を使用することです。

## List Categorical
CATE_FEATURES = df_train.iloc[:,:-1].select_dtypes('object').columns
print(CATE_FEATURES)

## List continuous
CONTI_FEATURES =  df_train._get_numeric_data()
print(CONTI_FEATURES)

データをインポートするコードは次のとおりです。

# Import dataset
import pandas as pd

## Define path data
COLUMNS = ['age','workclass', 'fnlwgt', 'education', 'education_num', 'marital',
           'occupation', 'relationship', 'race', 'sex', 'capital_gain', 'capital_loss',
           'hours_week', 'native_country', 'label']
### Define continuous list
CONTI_FEATURES  = ['age', 'fnlwgt','capital_gain', 'education_num', 'capital_loss', 'hours_week']
### Define categorical list
CATE_FEATURES = ['workclass', 'education', 'marital', 'occupation', 'relationship', 'race', 'sex', 'native_country']

## Prepare the data
features = ['age','workclass', 'fnlwgt', 'education', 'education_num', 'marital',
           'occupation', 'relationship', 'race', 'sex', 'capital_gain', 'capital_loss',
           'hours_week', 'native_country']

PATH = "https://archive.ics.uci.edu/ml/machine-learning-databases/adult/adult.data"

df_train = pd.read_csv(PATH, skipinitialspace=True, names = COLUMNS, index_col=False)
df_train[CONTI_FEATURES] =df_train[CONTI_FEATURES].astype('float64')
df_train.describe()
年齢 fnlwgt 教育番号 資本利得 資本損失 時間_週
カウント 32561.000000 3.256100e + 04 32561.000000 32561.000000 32561.000000 32561.000000
意味する 38.581647 1.897784e + 05 10.080679 1077.648844 87.303830 40.437456
STD 13.640433 1.055500e + 05 2.572720 7385.292085 402.960219 12.347429
17.000000 1.228500e + 04 1.000000 0.000000 0.000000 1.000000
25% 28.000000 1.178270e + 05 9.000000 0.000000 0.000000 40.000000
50% 37.000000 1.783560e + 05 10.000000 0.000000 0.000000 40.000000
75% 48.000000 2.370510e + 05 12.000000 0.000000 0.000000 45.000000
マックス 90.000000 1.484705e + 06 16.000000 99999.000000 4356.000000 99.000000

native_country 機能の一意の値の数を確認できます。 オランダ・オランダから来た世帯は XNUMX 世帯だけであることがわかります。 この世帯は私たちに情報を提供しませんが、トレーニング中のエラーによって提供されます。

df_train.native_country.value_counts()
United-States                 29170
Mexico                          643
?                               583
Philippines                     198
Germany                         137
Canada                          121
Puerto-Rico                     114
El-Salvador                     106
India                           100
Cuba                             95
England                          90
Jamaica                          81
South                            80
China                            75
Italy                            73
Dominican-Republic               70
Vietnam                          67
Guatemala                        64
Japan                            62
Poland                           60
Columbia                         59
Taiwan                           51
Haiti                            44
Iran                             43
Portugal                         37
Nicaragua                        34
Peru                             31
France                           29
Greece                           29
Ecuador                          28
Ireland                          24
Hong                             20
Cambodia                         19
Trinadad&Tobago                  19
Thailand                         18
Laos                             18
Yugoslavia                       16
Outlying-US(Guam-USVI-etc)       14
Honduras                         13
Hungary                          13
Scotland                         12
Holand-Netherlands                1
Name: native_country, dtype: int64

この有益でない行をデータセットから除外できます。

## Drop Netherland, because only one row
df_train = df_train[df_train.native_country != "Holand-Netherlands"]

次に、連続フィーチャの位置をリストに保存します。 これは、パイプラインを構築する次のステップで必要になります。

以下のコードは、CONTI_FEATURES 内のすべての列名をループしてその位置 (つまり、その番号) を取得し、それを conti_features というリストに追加します。

## Get the column index of the categorical features
conti_features = []
for i in CONTI_FEATURES:
    position = df_train.columns.get_loc(i)
    conti_features.append(position)
print(conti_features)  
[0, 2, 10, 4, 11, 12]

以下のコードは、カテゴリ変数を除いて上記と同じジョブを実行します。 以下のコードは、カテゴリ特徴を除いて、以前に実行したことを繰り返します。

## Get the column index of the categorical features
categorical_features = []
for i in CATE_FEATURES:
    position = df_train.columns.get_loc(i)
    categorical_features.append(position)
print(categorical_features)  
[1, 3, 5, 6, 7, 8, 9, 13]

データセットを確認できます。 各カテゴリ特徴は文字列であることに注意してください。 モデルに文字列値を与えることはできません。 ダミー変数を使用してデータセットを変換する必要があります。

df_train.head(5)

実際には、フィーチャ内のグループごとに XNUMX つの列を作成する必要があります。 まず、以下のコードを実行して、必要な列の合計量を計算します。

print(df_train[CATE_FEATURES].nunique(),
      'There are',sum(df_train[CATE_FEATURES].nunique()), 'groups in the whole dataset')
workclass          9
education         16
marital            7
occupation        15
relationship       6
race               5
sex                2
native_country    41
dtype: int64 There are 101 groups in the whole dataset

データセット全体は、上図に示すように 101 のグループで構成されています。たとえば、ワーククラスの特徴には XNUMX つのグループがあります。次のコードを使用して、グループの名前を視覚化できます。

unique() は、カテゴリ特徴の一意の値を返します。

for i in CATE_FEATURES:
    print(df_train[i].unique())
['State-gov' 'Self-emp-not-inc' 'Private' 'Federal-gov' 'Local-gov' '?'
 'Self-emp-inc' 'Without-pay' 'Never-worked']
['Bachelors' 'HS-grad' '11th' 'Masters' '9th' 'Some-college' 'Assoc-acdm'
 'Assoc-voc' '7th-8th' 'Doctorate' 'Prof-school' '5th-6th' '10th'
 '1st-4th' 'Preschool' '12th']
['Never-married' 'Married-civ-spouse' 'Divorced' 'Married-spouse-absent'
 'Separated' 'Married-AF-spouse' 'Widowed']
['Adm-clerical' 'Exec-managerial' 'Handlers-cleaners' 'Prof-specialty'
 'Other-service' 'Sales' 'Craft-repair' 'Transport-moving'
 'Farming-fishing' 'Machine-op-inspct' 'Tech-support' '?'
 'Protective-serv' 'Armed-Forces' 'Priv-house-serv']
['Not-in-family' 'Husband' 'Wife' 'Own-child' 'Unmarried' 'Other-relative']
['White' 'Black' 'Asian-Pac-Islander' 'Amer-Indian-Eskimo' 'Other']
['Male' 'Female']
['United-States' 'Cuba' 'Jamaica' 'India' '?' 'Mexico' 'South'
 'Puerto-Rico' 'Honduras' 'England' 'Canada' 'Germany' 'Iran'
 'Philippines' 'Italy' 'Poland' 'Columbia' 'Cambodia' 'Thailand' 'Ecuador'
 'Laos' 'Taiwan' 'Haiti' 'Portugal' 'Dominican-Republic' 'El-Salvador'
 'France' 'Guatemala' 'China' 'Japan' 'Yugoslavia' 'Peru'
 'Outlying-US(Guam-USVI-etc)' 'Scotland' 'Trinadad&Tobago' 'Greece'
 'Nicaragua' 'Vietnam' 'Hong' 'Ireland' 'Hungary']

したがって、トレーニング データセットには 101 + 7 列が含まれます。 最後の XNUMX 列は連続的な特徴です。

Scikit-learn が変換を処理できます。 これは XNUMX つの手順で行われます。

  • まず、文字列を ID に変換する必要があります。 たとえば、State-gov は ID 1、Self-emp-not-inc は ID 2 などになります。 LabelEncoder 関数がこれを実行します。
  • 各IDを新しい列に転置します。前述のように、データセットには101​​101のグループのIDがあります。したがって、すべてのカテゴリ機能のグループをキャプチャするXNUMXの列があります。Scikit-learnには、この操作を実行するOneHotEncoderという関数があります。

ステップ 2) トレーニング/テスト セットを作成する

データセットの準備ができたので、80/20 に分割できます。

トレーニング セットが 80 パーセント、テスト セットが 20 パーセントです。

train_test_split を使用できます。 最初の引数はデータフレームであり、XNUMX 番目の引数はラベル データフレームです。 test_size でテストセットのサイズを指定できます。

from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(df_train[features],
                                                    df_train.label,
                                                    test_size = 0.2,
                                                    random_state=0)
X_train.head(5)
print(X_train.shape, X_test.shape)
(26048, 14) (6512, 14)

ステップ 3) パイプラインを構築する

パイプラインにより、一貫したデータをモデルに供給することが容易になります。

背後にある考え方は、生のデータを「パイプライン」に入れて操作を実行することです。

たとえば、現在のデータセットでは、連続変数を標準化し、カテゴリデータを変換する必要があります。パイプライン内では任意の操作を実行できることに注意してください。たとえば、データセットに「NA」がある場合は、それを平均または中央値に置き換えることができます。新しい変数を作成することもできます。

あなたには選択の余地があります。 XNUMX つのプロセスをハードコーディングするか、パイプラインを作成します。 最初の選択肢はデータ漏洩につながり、時間の経過とともに不整合が生じる可能性があります。 より良いオプションはパイプラインを使用することです。

from sklearn.preprocessing import StandardScaler, OneHotEncoder, LabelEncoder
from sklearn.compose import ColumnTransformer, make_column_transformer
from sklearn.pipeline import make_pipeline
from sklearn.linear_model import LogisticRegression

パイプラインは、ロジスティック分類器にデータを供給する前に 2 つの操作を実行します。

  1. 変数を標準化します: `StandardScaler()`
  2. カテゴリ特徴量を変換します: OneHotEncoder(sparse=False)

make_column_transformer を使用して 0.19 つの手順を実行できます。 この機能は、scikit-learn の現在のバージョン (XNUMX) では使用できません。 現在のバージョンでは、パイプラインでラベル エンコーダーと XNUMX つのホット エンコーダーを実行することはできません。 これが開発者版を使用することにした理由の XNUMX つです。

make_column_transformer は簡単に使用できます。変換を適用する列と、実行する変換を定義する必要があります。たとえば、連続機能を標準化するには、次のようにします。

  • conti_features、make_column_transformer 内の StandardScaler()。
    • conti_features: 連続変数を含むリスト
    • StandardScaler: 変数を標準化します。

make_column_transformer 内のオブジェクト OneHotEncoder は、ラベルを自動的にエンコードします。

preprocess = make_column_transformer(
    (conti_features, StandardScaler()),
    ### Need to be numeric not string to specify columns name 
    (categorical_features, OneHotEncoder(sparse=False))
)

fit_transformを使用してパイプラインが機能するかどうかをテストできます。データセットは次の形状になります: 26048, 107

preprocess.fit_transform(X_train).shape
(26048, 107)

データ トランスフォーマーを使用する準備ができました。 make_pipeline を使用してパイプラインを作成できます。 データが変換されたら、ロジスティック回帰をフィードできます。

model = make_pipeline(
    preprocess,
    LogisticRegression())

scikit-learn を使用してモデルをトレーニングするのは簡単です。 パイプライン、つまりモデルの前にオブジェクト フィットを使用する必要があります。 scikit-learn ライブラリのスコア オブジェクトを使用して精度を出力できます。

model.fit(X_train, y_train)
print("logistic regression score: %f" % model.score(X_test, y_test))
logistic regression score: 0.850891

最後に、predict_proba を使用してクラスを予測できます。 各クラスの確率を返します。 合計すると XNUMX になることに注意してください。

model.predict_proba(X_test)
array([[0.83576663, 0.16423337],
       [0.94582765, 0.05417235],
       [0.64760587, 0.35239413],
       ...,
       [0.99639252, 0.00360748],
       [0.02072181, 0.97927819],
       [0.56781353, 0.43218647]])

ステップ 4) グリッド検索でパイプラインを使用する

ハイパーパラメータ (隠れユニットなどのネットワーク構造を決定する変数) の調整は、面倒で疲れる可能性があります。

モデルを評価する XNUMX つの方法は、トレーニング セットのサイズを変更してパフォーマンスを評価することです。

この方法を XNUMX 回繰り返すと、スコア メトリックを確認できます。 しかし、それは大変な作業です。

代わりに、scikit-learn はパラメーター調整と相互検証を実行する機能を提供します。

交差検証

相互検証とは、トレーニング中に、トレーニング セットがフォールドで n 回スリップされ、モデルが n 回評価されることを意味します。 たとえば、cv が 10 に設定されている場合、トレーニング セットはトレーニングされ、10 回評価されます。 各ラウンドで、分類器はモデルをトレーニングするためにランダムに XNUMX つの分割を選択し、XNUMX 番目の分割が評価用になります。

グリッド検索

各分類子には調整するハイパーパラメータがあります。 別の値を試したり、パラメーター グリッドを設定したりできます。 scikit-learn 公式 Web サイトにアクセスすると、ロジスティック分類器に調整すべきさまざまなパラメーターがあることがわかります。 トレーニングを高速化するには、C パラメーターを調整することを選択します。 正則化パラメータを制御します。 ポジティブである必要があります。 値が小さいほど、レガライザーの重みが大きくなります。

GridSearchCV オブジェクトを使用できます。 調整するハイパーパラメータを含むディクショナリを作成する必要があります。

ハイパーパラメータの後に、試したい値をリストします。 たとえば、C パラメータを調整するには、次を使用します。

  • 'logisticregression__C': [0.1, 1.0, 1.0]: パラメーターの前に、分類子の名前 (小文字) と XNUMX つのアンダースコアが続きます。

モデルは 0.001 つの異なる値 (0.01、0.1、1、XNUMX) を試します。

10 個のフォールドを使用してモデルをトレーニングします: cv=10

from sklearn.model_selection import GridSearchCV
# Construct the parameter grid
param_grid = {
    'logisticregression__C': [0.001, 0.01,0.1, 1.0],
    }

GridSearchCV をパラメーター gri と cv で使用してモデルをトレーニングできます。

# Train the model
grid_clf = GridSearchCV(model,
                        param_grid,
                        cv=10,
                        iid=False)
grid_clf.fit(X_train, y_train)

出力

GridSearchCV(cv=10, error_score='raise-deprecating',
       estimator=Pipeline(memory=None,
     steps=[('columntransformer', ColumnTransformer(n_jobs=1, remainder='drop', transformer_weights=None,
         transformers=[('standardscaler', StandardScaler(copy=True, with_mean=True, with_std=True), [0, 2, 10, 4, 11, 12]), ('onehotencoder', OneHotEncoder(categorical_features=None, categories=None,...ty='l2', random_state=None, solver='liblinear', tol=0.0001,
          verbose=0, warm_start=False))]),
       fit_params=None, iid=False, n_jobs=1,
       param_grid={'logisticregression__C': [0.001, 0.01, 0.1, 1.0]},
       pre_dispatch='2*n_jobs', refit=True, return_train_score='warn',
       scoring=None, verbose=0)

最適なパラメータにアクセスするには、best_params_ を使用します。

grid_clf.best_params_

出力

{'logisticregression__C': 1.0}

XNUMX つの異なる正則化値を使用してモデルをトレーニングした後の最適なパラメーターは次のとおりです。

print("best logistic regression from grid search: %f" % grid_clf.best_estimator_.score(X_test, y_test))

グリッド検索からの最良のロジスティック回帰: 0.850891

予測確率にアクセスするには:

grid_clf.best_estimator_.predict_proba(X_test)
array([[0.83576677, 0.16423323],
       [0.9458291 , 0.0541709 ],
       [0.64760416, 0.35239584],
       ...,
       [0.99639224, 0.00360776],
       [0.02072033, 0.97927967],
       [0.56782222, 0.43217778]])

scikit-learn を使用した XGBoost モデル

Scikit-learnの例を使って、市場で最も優れた分類器の1つをトレーニングしてみましょう。XGBoostはランダムフォレストの改良版です。分類器の理論的背景はこの記事の範囲外です。 Python Scikit チュートリアル。XGBoost は多くの Kaggle コンテストで優勝していることに留意してください。平均的なデータセット サイズでは、ディープラーニング アルゴリズムと同等かそれ以上のパフォーマンスを発揮します。

分類器は調整するパラメーターが多数あるため、トレーニングが困難です。 もちろん、GridSearchCV を使用してパラメーターを選択することもできます。

代わりに、最適なパラメーターを見つけるためのより良い方法を使用する方法を見てみましょう。 GridSearchCV は、多くの値を渡す場合、退屈でトレーニングに非常に時間がかかる可能性があります。 検索空間はパラメータの数とともに増加します。 推奨される解決策は、RandomizedSearchCV を使用することです。 この方法は、各反復後に各ハイパーパラメータの値をランダムに選択することで構成されます。 たとえば、分類器が 1000 回の反復にわたってトレーニングされた場合、1000 個の組み合わせが評価されます。 多かれ少なかれ同じように機能します。 グリッドサーチCV

xgboostをインポートする必要があります。 ライブラリがインストールされていない場合は、pip3 install xgboost または

use import sys
!{sys.executable} -m pip install xgboost

In Jupyter 環境

次に、

import xgboost
from sklearn.model_selection import RandomizedSearchCV
from sklearn.model_selection import StratifiedKFold

このScikitの次のステップ Python チュートリアルには、調整するパラメータを指定することが含まれています。調整するすべてのパラメータを確認するには、公式ドキュメントを参照してください。 Python Sklearn チュートリアルでは、それぞれ 2 つの値を持つ 2 つのハイパーパラメータのみを選択します。XGBoost のトレーニングには時間がかかり、グリッド内のハイパーパラメータが多いほど、待機時間も長くなります。

params = {
        'xgbclassifier__gamma': [0.5, 1],
        'xgbclassifier__max_depth': [3, 4]
        }

XGBoost 分類器を使用して新しいパイプラインを構築します。600 個の推定器を定義することを選択します。n_estimators は調整可能なパラメータであることに注意してください。値が高いとオーバーフィッティングにつながる可能性があります。さまざまな値を自分で試すこともできますが、数時間かかる可能性があることに注意してください。他のパラメータにはデフォルト値を使用します。

model_xgb = make_pipeline(
    preprocess,
    xgboost.XGBClassifier(
                          n_estimators=600,
                          objective='binary:logistic',
                          silent=True,
                          nthread=1)
)

Stratified K-Folds 相互検証を使用して相互検証を改善できます。 ここでは、計算を高速化するために 5 つの折り目のみを構築しますが、品質は低下します。 結果を改善するには、自宅でこの値を 10 または XNUMX に増やします。

XNUMX 回の反復にわたってモデルをトレーニングすることを選択します。

skf = StratifiedKFold(n_splits=3,
                      shuffle = True,
                      random_state = 1001)

random_search = RandomizedSearchCV(model_xgb,
                                   param_distributions=params,
                                   n_iter=4,
                                   scoring='accuracy',
                                   n_jobs=4,
                                   cv=skf.split(X_train, y_train),
                                   verbose=3,
                                   random_state=1001)

ランダム化検索を使用する準備ができました。モデルをトレーニングできます。

#grid_xgb = GridSearchCV(model_xgb, params, cv=10, iid=False)
random_search.fit(X_train, y_train)
Fitting 3 folds for each of 4 candidates, totalling 12 fits
[CV] xgbclassifier__max_depth=3, xgbclassifier__gamma=0.5 ............
[CV] xgbclassifier__max_depth=3, xgbclassifier__gamma=0.5 ............
[CV] xgbclassifier__max_depth=3, xgbclassifier__gamma=0.5 ............
[CV] xgbclassifier__max_depth=4, xgbclassifier__gamma=0.5 ............
[CV]  xgbclassifier__max_depth=3, xgbclassifier__gamma=0.5, score=0.8759645283888057, total= 1.0min
[CV] xgbclassifier__max_depth=4, xgbclassifier__gamma=0.5 ............
[CV]  xgbclassifier__max_depth=3, xgbclassifier__gamma=0.5, score=0.8729701715996775, total= 1.0min
[CV]  xgbclassifier__max_depth=3, xgbclassifier__gamma=0.5, score=0.8706519235199263, total= 1.0min
[CV] xgbclassifier__max_depth=4, xgbclassifier__gamma=0.5 ............
[CV] xgbclassifier__max_depth=3, xgbclassifier__gamma=1 ..............
[CV]  xgbclassifier__max_depth=4, xgbclassifier__gamma=0.5, score=0.8735460094437406, total= 1.3min
[CV] xgbclassifier__max_depth=3, xgbclassifier__gamma=1 ..............
[CV]  xgbclassifier__max_depth=3, xgbclassifier__gamma=1, score=0.8722791661868018, total=  57.7s
[CV] xgbclassifier__max_depth=3, xgbclassifier__gamma=1 ..............
[CV]  xgbclassifier__max_depth=3, xgbclassifier__gamma=1, score=0.8753886905447426, total= 1.0min
[CV] xgbclassifier__max_depth=4, xgbclassifier__gamma=1 ..............
[CV]  xgbclassifier__max_depth=4, xgbclassifier__gamma=0.5, score=0.8697304768486523, total= 1.3min
[CV] xgbclassifier__max_depth=4, xgbclassifier__gamma=1 ..............
[CV]  xgbclassifier__max_depth=4, xgbclassifier__gamma=0.5, score=0.8740066797189912, total= 1.4min
[CV] xgbclassifier__max_depth=4, xgbclassifier__gamma=1 ..............
[CV]  xgbclassifier__max_depth=3, xgbclassifier__gamma=1, score=0.8707671043538355, total= 1.0min
[CV]  xgbclassifier__max_depth=4, xgbclassifier__gamma=1, score=0.8729701715996775, total= 1.2min
[Parallel(n_jobs=4)]: Done  10 out of  12 | elapsed:  3.6min remaining:   43.5s
[CV]  xgbclassifier__max_depth=4, xgbclassifier__gamma=1, score=0.8736611770125533, total= 1.2min
[CV]  xgbclassifier__max_depth=4, xgbclassifier__gamma=1, score=0.8692697535130154, total= 1.2min
[Parallel(n_jobs=4)]: Done  12 out of  12 | elapsed:  3.6min finished
/Users/Thomas/anaconda3/envs/hello-tf/lib/python3.6/site-packages/sklearn/model_selection/_search.py:737: DeprecationWarning: The default of the `iid` parameter will change from True to False in version 0.22 and will be removed in 0.24. This will change numeric results when test-set sizes are unequal. DeprecationWarning)
RandomizedSearchCV(cv=<generator object _BaseKFold.split at 0x1101eb830>,
          error_score='raise-deprecating',
          estimator=Pipeline(memory=None,
     steps=[('columntransformer', ColumnTransformer(n_jobs=1, remainder='drop', transformer_weights=None,
         transformers=[('standardscaler', StandardScaler(copy=True, with_mean=True, with_std=True), [0, 2, 10, 4, 11, 12]), ('onehotencoder', OneHotEncoder(categorical_features=None, categories=None,...
       reg_alpha=0, reg_lambda=1, scale_pos_weight=1, seed=None,
       silent=True, subsample=1))]),
          fit_params=None, iid='warn', n_iter=4, n_jobs=4,
          param_distributions={'xgbclassifier__gamma': [0.5, 1], 'xgbclassifier__max_depth': [3, 4]},
          pre_dispatch='2*n_jobs', random_state=1001, refit=True,
          return_train_score='warn', scoring='accuracy', verbose=3)

ご覧のとおり、XGBoost のスコアは前のロジスティック回帰よりも優れています。

print("Best parameter", random_search.best_params_)
print("best logistic regression from grid search: %f" % random_search.best_estimator_.score(X_test, y_test))
Best parameter {'xgbclassifier__max_depth': 3, 'xgbclassifier__gamma': 0.5}
best logistic regression from grid search: 0.873157
random_search.best_estimator_.predict(X_test)
array(['<=50K', '<=50K', '<=50K', ..., '<=50K', '>50K', '<=50K'],      dtype=object)

scikit-learn で MLPClassifier を使用して DNN を作成する

最後に、scikit-learn を使用して深層学習アルゴリズムをトレーニングできます。 方法は他の分類器と同じです。 分類子は MLPClassifier で入手できます。

from sklearn.neural_network import MLPClassifier

次のディープラーニングアルゴリズムを定義します。

  • アダムソルバー
  • Relu活性化関数
  • アルファ = 0.0001
  • バッチサイズ 150
  • それぞれ 100 個と 50 個のニューロンを持つ XNUMX つの隠れ層
model_dnn = make_pipeline(
    preprocess,
    MLPClassifier(solver='adam',
                  alpha=0.0001,
                  activation='relu',
                    batch_size=150,
                    hidden_layer_sizes=(200, 100),
                    random_state=1))

レイヤーの数を変更してモデルを改善できます

model_dnn.fit(X_train, y_train)
  print("DNN regression score: %f" % model_dnn.score(X_test, y_test))

DNN 回帰スコア: 0.821253

ライム: 自分のモデルを信頼してください

適切なモデルを作成したら、それを信頼するためのツールが必要です。 機械学習 アルゴリズム、特にランダムフォレストとニューラルネットワークは、ブラックボックスアルゴリズムとして知られています。言い換えれば、動作はしますが、その理由は誰にもわかりません。

XNUMX 人の研究者が、コンピューターがどのように予測を行うかを確認するための優れたツールを考案しました。 この論文は「なぜあなたを信頼すべきですか?」と題されています。

彼らは、という名前のアルゴリズムを開発しました。 ローカルで解釈可能なモデルに依存しない説明 (LIME).

例を見てみましょう:

機械学習による予測が信頼できるかどうかわからない場合があります。

たとえば、医師は、コンピューターが診断したからといって、その診断を信頼することはできません。 また、モデルを実稼働環境に導入する前に、そのモデルが信頼できるかどうかを知る必要もあります。

ニューラル ネットワーク、ランダム フォレスト、カーネルを備えた SVMS などの信じられないほど複雑なモデルであっても、分類器が予測を行う理由を理解できると想像してください。

予測の背後にある理由を理解できれば、予測を信頼しやすくなります。 医師の例から、モデルがどの症状が重要であるかを彼に告げた場合、あなたはそれを信頼するでしょうが、そのモデルを信頼すべきでないのかどうかも判断しやすくなります。

Lime は、どの機能が分類器の決定に影響を与えるかを知ることができます

データの準備

LIME を実行するには、変更する必要があるものがいくつかあります。 パイソン。 まず、ターミナルに lime をインストールする必要があります。 pip install limeを使用できます

Lime は、LimeTabularExplainer オブジェクトを使用してモデルをローカルで近似します。 このオブジェクトには次のものが必要です。

  • numpy 形式のデータセット
  • 機能の名前: feature_names
  • クラスの名前: class_names
  • カテゴリ特徴量の列のインデックス: categorical_features
  • 各カテゴリ特徴量のグループの名前: categorical_names

numpy 列車セットを作成する

df_train を pandas からコピーして変換できます。 numpy とても簡単に

df_train.head(5)
# Create numpy data
df_lime = df_train
df_lime.head(3)

クラス名を取得する ラベルには、オブジェクト unique() を使用してアクセスできます。 君は見るべきだ:

  • '<= 50K'
  • '> 50K'
# Get the class name
class_names = df_lime.label.unique()
class_names
array(['<=50K', '>50K'], dtype=object)

カテゴリ特徴量の列のインデックス

グループの名前を取得するには、前に学んだ方法を使用できます。ラベルは LabelEncoder でエンコードします。すべてのカテゴリ機能に対してこの操作を繰り返します。

## 
import sklearn.preprocessing as preprocessing
categorical_names = {}
for feature in CATE_FEATURES:
    le = preprocessing.LabelEncoder()
    le.fit(df_lime[feature])
    df_lime[feature] = le.transform(df_lime[feature])
    categorical_names[feature] = le.classes_
print(categorical_names)    
{'workclass': array(['?', 'Federal-gov', 'Local-gov', 'Never-worked', 'Private',
       'Self-emp-inc', 'Self-emp-not-inc', 'State-gov', 'Without-pay'],
      dtype=object), 'education': array(['10th', '11th', '12th', '1st-4th', '5th-6th', '7th-8th', '9th',
       'Assoc-acdm', 'Assoc-voc', 'Bachelors', 'Doctorate', 'HS-grad',
       'Masters', 'Preschool', 'Prof-school', 'Some-college'],
      dtype=object), 'marital': array(['Divorced', 'Married-AF-spouse', 'Married-civ-spouse',
       'Married-spouse-absent', 'Never-married', 'Separated', 'Widowed'],
      dtype=object), 'occupation': array(['?', 'Adm-clerical', 'Armed-Forces', 'Craft-repair',
       'Exec-managerial', 'Farming-fishing', 'Handlers-cleaners',
       'Machine-op-inspct', 'Other-service', 'Priv-house-serv',
       'Prof-specialty', 'Protective-serv', 'Sales', 'Tech-support',
       'Transport-moving'], dtype=object), 'relationship': array(['Husband', 'Not-in-family', 'Other-relative', 'Own-child',
       'Unmarried', 'Wife'], dtype=object), 'race': array(['Amer-Indian-Eskimo', 'Asian-Pac-Islander', 'Black', 'Other',
       'White'], dtype=object), 'sex': array(['Female', 'Male'], dtype=object), 'native_country': array(['?', 'Cambodia', 'Canada', 'China', 'Columbia', 'Cuba',
       'Dominican-Republic', 'Ecuador', 'El-Salvador', 'England',
       'France', 'Germany', 'Greece', 'Guatemala', 'Haiti', 'Honduras',
       'Hong', 'Hungary', 'India', 'Iran', 'Ireland', 'Italy', 'Jamaica',
       'Japan', 'Laos', 'Mexico', 'Nicaragua',
       'Outlying-US(Guam-USVI-etc)', 'Peru', 'Philippines', 'Poland',
       'Portugal', 'Puerto-Rico', 'Scotland', 'South', 'Taiwan',
       'Thailand', 'Trinadad&Tobago', 'United-States', 'Vietnam',
       'Yugoslavia'], dtype=object)}

df_lime.dtypes
age               float64
workclass           int64
fnlwgt            float64
education           int64
education_num     float64
marital             int64
occupation          int64
relationship        int64
race                int64
sex                 int64
capital_gain      float64
capital_loss      float64
hours_week        float64
native_country      int64
label              object
dtype: object

データセットの準備ができたので、以下の Scikit 学習の例に示すように、別のデータセットを構築できます。 LIME でのエラーを回避するために、実際にはパイプラインの外側でデータを変換します。 LimeTabularExplainer のトレーニング セットは、文字列のない numpy 配列である必要があります。 上記の方法では、トレーニング データセットはすでに変換されています。

from sklearn.model_selection import train_test_split
X_train_lime, X_test_lime, y_train_lime, y_test_lime = train_test_split(df_lime[features],
                                                    df_lime.label,
                                                    test_size = 0.2,
                                                    random_state=0)
X_train_lime.head(5)

XGBoostから最適なパラメータでパイプラインを作成できます

model_xgb = make_pipeline(
    preprocess,
    xgboost.XGBClassifier(max_depth = 3,
                          gamma = 0.5,
                          n_estimators=600,
                          objective='binary:logistic',
                          silent=True,
                          nthread=1))

model_xgb.fit(X_train_lime, y_train_lime)
/Users/Thomas/anaconda3/envs/hello-tf/lib/python3.6/site-packages/sklearn/preprocessing/_encoders.py:351: FutureWarning: The handling of integer data will change in version 0.22. Currently, the categories are determined based on the range [0, max(values)], while in the future they will be determined based on the unique values.
If you want the future behavior and silence this warning, you can specify "categories='auto'."In case you used a LabelEncoder before this OneHotEncoder to convert the categories to integers, then you can now use the OneHotEncoder directly.
  warnings.warn(msg, FutureWarning)
Pipeline(memory=None,
     steps=[('columntransformer', ColumnTransformer(n_jobs=1, remainder='drop', transformer_weights=None,
         transformers=[('standardscaler', StandardScaler(copy=True, with_mean=True, with_std=True), [0, 2, 10, 4, 11, 12]), ('onehotencoder', OneHotEncoder(categorical_features=None, categories=None,...
       reg_alpha=0, reg_lambda=1, scale_pos_weight=1, seed=None,
       silent=True, subsample=1))])

警告が表示されます。警告では、パイプラインの前にラベル エンコーダーを作成する必要がないことが説明されています。LIME を使用しない場合は、Scikit-learn を使用した機械学習チュートリアルの最初の部分の方法を使用しても問題ありません。それ以外の場合は、この方法を使用し、最初にエンコードされたデータセットを作成し、パイプライン内でホット エンコーダーを設定します。

print("best logistic regression from grid search: %f" % model_xgb.score(X_test_lime, y_test_lime))
best logistic regression from grid search: 0.873157
model_xgb.predict_proba(X_test_lime)
array([[7.9646105e-01, 2.0353897e-01],
       [9.5173013e-01, 4.8269872e-02],
       [7.9344827e-01, 2.0655173e-01],
       ...,
       [9.9031430e-01, 9.6856682e-03],
       [6.4581633e-04, 9.9935418e-01],
       [9.7104281e-01, 2.8957171e-02]], dtype=float32)

LIME を実際に使用する前に、間違った分類の特徴を含む numpy 配列を作成しましょう。後でそのリストを使用して、分類器を誤解させる原因について理解することができます。

temp = pd.concat([X_test_lime, y_test_lime], axis= 1)
temp['predicted'] = model_xgb.predict(X_test_lime)
temp['wrong']=  temp['label'] != temp['predicted']
temp = temp.query('wrong==True').drop('wrong', axis=1)
temp= temp.sort_values(by=['label'])
temp.shape

(826、16)

新しいデータを使用してモデルから予測を取得するラムダ関数を作成します。 すぐに必要になります。

predict_fn = lambda x: model_xgb.predict_proba(x).astype(float)
X_test_lime.dtypes
age               float64
workclass           int64
fnlwgt            float64
education           int64
education_num     float64
marital             int64
occupation          int64
relationship        int64
race                int64
sex                 int64
capital_gain      float64
capital_loss      float64
hours_week        float64
native_country      int64
dtype: object
predict_fn(X_test_lime)
array([[7.96461046e-01, 2.03538969e-01],
       [9.51730132e-01, 4.82698716e-02],
       [7.93448269e-01, 2.06551731e-01],
       ...,
       [9.90314305e-01, 9.68566816e-03],
       [6.45816326e-04, 9.99354184e-01],
       [9.71042812e-01, 2.89571714e-02]])

pandas データフレームを numpy 配列に変換します

X_train_lime = X_train_lime.values
X_test_lime = X_test_lime.values
X_test_lime
array([[4.00000e+01, 5.00000e+00, 1.93524e+05, ..., 0.00000e+00,
        4.00000e+01, 3.80000e+01],
       [2.70000e+01, 4.00000e+00, 2.16481e+05, ..., 0.00000e+00,
        4.00000e+01, 3.80000e+01],
       [2.50000e+01, 4.00000e+00, 2.56263e+05, ..., 0.00000e+00,
        4.00000e+01, 3.80000e+01],
       ...,
       [2.80000e+01, 6.00000e+00, 2.11032e+05, ..., 0.00000e+00,
        4.00000e+01, 2.50000e+01],
       [4.40000e+01, 4.00000e+00, 1.67005e+05, ..., 0.00000e+00,
        6.00000e+01, 3.80000e+01],
       [5.30000e+01, 4.00000e+00, 2.57940e+05, ..., 0.00000e+00,
        4.00000e+01, 3.80000e+01]])
model_xgb.predict_proba(X_test_lime)
array([[7.9646105e-01, 2.0353897e-01],
       [9.5173013e-01, 4.8269872e-02],
       [7.9344827e-01, 2.0655173e-01],
       ...,
       [9.9031430e-01, 9.6856682e-03],
       [6.4581633e-04, 9.9935418e-01],
       [9.7104281e-01, 2.8957171e-02]], dtype=float32)
print(features,
      class_names,
      categorical_features,
      categorical_names)
['age', 'workclass', 'fnlwgt', 'education', 'education_num', 'marital', 'occupation', 'relationship', 'race', 'sex', 'capital_gain', 'capital_loss', 'hours_week', 'native_country'] ['<=50K' '>50K'] [1, 3, 5, 6, 7, 8, 9, 13] {'workclass': array(['?', 'Federal-gov', 'Local-gov', 'Never-worked', 'Private',
       'Self-emp-inc', 'Self-emp-not-inc', 'State-gov', 'Without-pay'],
      dtype=object), 'education': array(['10th', '11th', '12th', '1st-4th', '5th-6th', '7th-8th', '9th',
       'Assoc-acdm', 'Assoc-voc', 'Bachelors', 'Doctorate', 'HS-grad',
       'Masters', 'Preschool', 'Prof-school', 'Some-college'],
      dtype=object), 'marital': array(['Divorced', 'Married-AF-spouse', 'Married-civ-spouse',
       'Married-spouse-absent', 'Never-married', 'Separated', 'Widowed'],
      dtype=object), 'occupation': array(['?', 'Adm-clerical', 'Armed-Forces', 'Craft-repair',
       'Exec-managerial', 'Farming-fishing', 'Handlers-cleaners',
       'Machine-op-inspct', 'Other-service', 'Priv-house-serv',
       'Prof-specialty', 'Protective-serv', 'Sales', 'Tech-support',
       'Transport-moving'], dtype=object), 'relationship': array(['Husband', 'Not-in-family', 'Other-relative', 'Own-child',
       'Unmarried', 'Wife'], dtype=object), 'race': array(['Amer-Indian-Eskimo', 'Asian-Pac-Islander', 'Black', 'Other',
       'White'], dtype=object), 'sex': array(['Female', 'Male'], dtype=object), 'native_country': array(['?', 'Cambodia', 'Canada', 'China', 'Columbia', 'Cuba',
       'Dominican-Republic', 'Ecuador', 'El-Salvador', 'England',
       'France', 'Germany', 'Greece', 'Guatemala', 'Haiti', 'Honduras',
       'Hong', 'Hungary', 'India', 'Iran', 'Ireland', 'Italy', 'Jamaica',
       'Japan', 'Laos', 'Mexico', 'Nicaragua',
       'Outlying-US(Guam-USVI-etc)', 'Peru', 'Philippines', 'Poland',
       'Portugal', 'Puerto-Rico', 'Scotland', 'South', 'Taiwan',
       'Thailand', 'Trinadad&Tobago', 'United-States', 'Vietnam',
       'Yugoslavia'], dtype=object)}
import lime
import lime.lime_tabular
### Train should be label encoded not one hot encoded
explainer = lime.lime_tabular.LimeTabularExplainer(X_train_lime ,
                                                   feature_names = features,
                                                   class_names=class_names,
                                                   categorical_features=categorical_features, 
                                                   categorical_names=categorical_names,
                                                   kernel_width=3)

テスト セットからランダムな世帯を選択し、モデルの予測とコンピューターがどのように選択を行ったかを見てみましょう。

import numpy as np
np.random.seed(1)
i = 100
print(y_test_lime.iloc[i])
>50K
X_test_lime[i]
array([4.20000e+01, 4.00000e+00, 1.76286e+05, 7.00000e+00, 1.20000e+01,
       2.00000e+00, 4.00000e+00, 0.00000e+00, 4.00000e+00, 1.00000e+00,
       0.00000e+00, 0.00000e+00, 4.00000e+01, 3.80000e+01])

Explain_instance を使用して Explainer を使用すると、モデルの背後にある説明を確認できます。

exp = explainer.explain_instance(X_test_lime[i], predict_fn, num_features=6)
exp.show_in_notebook(show_all=False)

データの準備

分類器が世帯を正しく予測したことがわかります。 収入は確かに50万以上です。

まず言えることは、分類器は予測される確率についてそれほど確信を持っていないということです。機械は、世帯の収入が 50 万ドルを超える確率は 64% であると予測しています。この 64% は、キャピタル ゲインと婚姻収入で構成されています。青色は正のクラスにマイナスの影響を与え、オレンジ色の線はプラスの影響を与えています。

この世帯の資本利得はゼロであるため、分類子は混乱しています。資本利得は通常、富の優れた予測子ですが、この世帯の資本利得はゼロです。また、この世帯の労働時間は週 40 時間未満です。年齢、職業、性別は分類子にプラスの影響を与えています。

婚姻状況が独身の場合、分類器は50万ドル未満の収入を予測しただろう(0.64-0.18 = 0.46)。

間違って分類された別の世帯で試してみることができます

temp.head(3)
temp.iloc[1,:-2]
age                  58
workclass             4
fnlwgt            68624
education            11
education_num         9
marital               2
occupation            4
relationship          0
race                  4
sex                   1
capital_gain          0
capital_loss          0
hours_week           45
native_country       38
Name: 20931, dtype: object
i = 1
print('This observation is', temp.iloc[i,-2:])
This observation is label        <=50K
predicted     >50K
Name: 20931, dtype: object
exp = explainer.explain_instance(temp.iloc[1,:-2], predict_fn, num_features=6)
exp.show_in_notebook(show_all=False)

データの準備

分類器は収入が 50 未満であると予測しましたが、これは真実ではありません。 この家庭は奇妙に見える。 キャピタルゲインもキャピタルロスもありません。 彼は離婚していて 60 歳で、教育を受けた人々です。つまり、education_num > 12 です。全体的なパターンによれば、この世帯は、分類子によって説明されるように、収入が 50 未満になるはずです。

LIME をいじってみます。 分類器による重大な間違いに気づくでしょう。

ライブラリの所有者の GitHub を確認できます。 これらは、画像とテキストの分類に関する追加のドキュメントを提供します。

まとめ

以下は、scikit learn バージョン >=0.20 でのいくつかの便利なコマンドのリストです。

トレーニング/テスト データセットを作成する 練習生が分かれる
パイプラインを構築する
列を選択して変換を適用します メイクカラムトランスフォーマー
変換の種類
標準化する StandardScaler
最小最大 MinMaxScaler
ノーマライズ ノーマライザー
欠損値の代入 代入する
カテゴリカルに変換する OneHotEncoder
データを適合させて変換する フィットトランスフォーム
パイプラインを作る パイプラインの作成
ベーシックモデル
ロジスティック回帰 ロジスティック回帰
XGブースト XGB分類子
ニューラルネット MLP分類子
グリッド検索 GridSearchCV
ランダム検索 ランダム化検索CV