「モデルの解釈性のためのPFIに深く入り込む」

深くPFIに入り込む

ツールボックスにもう一つの解釈ツール

fabioによる写真

データサイエンティストとしての仕事において、モデルの評価方法を知ることは重要です。ステークホルダーに完全に理解し、伝えることができなければ、解決策に署名はされません。そのため、解釈方法を知ることは非常に重要です。

解釈性の欠如は非常に優れたモデルを台無しにすることがあります。私はステークホルダーが予測がどのように行われるのかを理解することに関心を持たなかったモデルを開発したことはありません。したがって、モデルを解釈し、ビジネスに伝える能力は、データサイエンティストにとって必要なスキルです。

この記事では、Permutation Feature Importance (PFI)というモデルに依存しない手法を探求します。この手法は、モデルの最も重要な特徴を特定し、モデルが予測を行う際に考慮している内容をより良く伝えるのに役立ちます。

Permutation Feature Importanceとは何ですか

PFIメソッドは、ターゲット変数に関連する特徴を変更した場合にモデルがどのように変化するかに基づいて、特徴の重要性を推定しようとします。

そのため、重要性を分析したい各特徴について、他のすべての特徴とターゲットを同じままにしたまま、その特徴をランダムにシャッフルします。

これにより、特徴とターゲットの関係を変えることで、特徴はターゲットを予測する際に無意味になります。

その後、シャッフルしたデータセットを使用してモデルに予測させます。モデルの性能低下の程度は、その特徴の重要性を示します。

アルゴリズムは次のようになります:

  • トレーニングデータセットでモデルを訓練し、トレーニングとテストの両方で性能を評価します
  • 各特徴について、その特徴がシャッフルされた新しいデータセットを作成します
  • 次に、訓練済みモデルを使用して新しいデータセットの出力を予測します
  • 新しい性能指標を古い性能指標で割ることで、特徴の重要性を得ます

特徴が重要でない場合、モデルの性能はあまり変化しません。重要である場合は、性能が大幅に低下するはずです。

PFIの解釈

PFIの計算方法を知ったので、どのように解釈するのでしょうか?

それは、PFIを適用するフォールドによって異なります。通常、トレーニングデータセットまたはテストデータセットのどちらかに適用します。

トレーニングの解釈

トレーニング中、モデルはデータのパターンを学習し、それを表現しようとします。もちろん、トレーニング中は未知のデータにモデルが一般化するかどうかはわかりません。

したがって、PFIをトレーニングデータセットに適用することで、モデルによるデータの表現の学習に最も関連性のある特徴を見ることができます。

ビジネス的には、これはモデル構築において最も重要な特徴を示しています。

テストの解釈

一方、テストデータセットにメソッドを適用すると、モデルの一般化における特徴の影響を確認できます。

考えてみましょう。特徴をシャッフルした後にテストセットでモデルの性能が低下する場合、その特徴はそのセットの性能に重要であったことを意味します。テストセットは一般化をテストするために使用するものであるため、重要であると言えます(すべてを正しく行っている場合)。

PFIの問題点

PFIは、特徴のモデルパフォーマンスへの影響を分析するため、生のデータについては何も述べていません。モデルのパフォーマンスが低い場合、PFIで見つける関連性は無意味になります。

これは、モデルが適合不足(トレーニングセットでの予測能力が低い)または過剰適合(テストセットでの予測能力が低い)している場合の両方に当てはまります。その場合、この方法から洞察を得ることはできません。

また、2つの特徴が高度に相関している場合、PFIは解釈を誤る可能性があります。1つの特徴をシャッフルしても必要な情報が別の特徴にエンコードされている場合、性能は全く低下しない可能性があります。これにより、その特徴は無意味であると思われるかもしれませんが、実際にはそうではないかもしれません。

PythonでPFIを実装する

PFIをPythonで実装するには、まず必要なライブラリをインポートする必要があります。これには、主にnumpy、pandas、tqdm、sklearnのライブラリを使用します:

import pandas as pdimport numpy as npimport matplotlib.pyplot as pltfrom tqdm import tqdmfrom sklearn.model_selection import train_test_splitfrom sklearn.datasets import load_diabetes, load_irisfrom sklearn.ensemble import RandomForestRegressor, RandomForestClassifierfrom sklearn.metrics import accuracy_score, r2_score

次に、Irisデータセットをロードし、データにランダムフォレストを適合させます。

X, y = load_iris(return_X_y=True)X_train, X_test, y_train, y_test = train_test_split(  X, y, test_size=0.3, random_state=12, shuffle=True)rf = RandomForestClassifier(  n_estimators=3, random_state=32).fit(X_train, y_train)

モデルを適合させたら、特徴の影響を確認するためにモデルのパフォーマンスを分析しましょう:

print(accuracy_score(rf.predict(X_train), y_train))print(accuracy_score(rf.predict(X_test), y_test))

トレーニングセットでは99%の正答率、テストセットでは95.5%の正答率を達成していることがわかります。これは良さそうです。後で比較するために元のエラースコアを取得しましょう:

original_error_train = 1 - accuracy_score(rf.predict(X_train), y_train)original_error_test = 1 - accuracy_score(rf.predict(X_test), y_test)

次に、置換スコアを計算しましょう。そのために、各特徴のシャッフルを複数回実行して特徴スコアの統計を取得することが一般的です。今回は、各特徴に対して10回の繰り返しを行います:

n_steps = 10feature_values = {}for feature in range(X.shape[1]):  # 各特徴のパフォーマンスポイントを保存する    errors_permuted_train = []    errors_permuted_test = []        for step in range(n_steps):        # np.random.shuffle関数はインプレースでシャッフルするため、データを再度取得する必要があります        X, y = load_iris(return_X_y=True)        X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=12, shuffle=True)        np.random.shuffle(X_train[:, feature])        np.random.shuffle(X_test[:, feature])            # 新しいデータに先ほど適合させたモデルを適用してパフォーマンスを取得する        errors_permuted_train.append(1 - accuracy_score(rf.predict(X_train), y_train))        errors_permuted_test.append(1 - accuracy_score(rf.predict(X_test), y_test))            feature_values[f'{feature}_train'] = errors_permuted_train    feature_values[f'{feature}_test'] = errors_permuted_test

これで、各シャッフルのパフォーマンスを持つ辞書ができました。次に、各特徴の各フォールドごとに、元のモデルのパフォーマンスと比較したときの平均値と標準偏差を持つテーブルを生成しましょう:

PFI = pd.DataFrame()for feature in feature_values:    if 'train' in feature:        aux = feature_values[feature] / original_error_train        fold = 'train'    elif 'test' in feature:        aux = feature_values[feature] / original_error_test        fold = 'test'        PFI = PFI.append({        'feature': feature.replace(f'_{fold}', ''),        'pfold': fold,        'mean':np.mean(aux),        'std':np.std(aux),    }, ignore_index=True)    PFI = PFI.pivot(index='feature', columns='fold', values=['mean', 'std']).reset_index().sort_values(('mean', 'test'), ascending=False)

次のようなものが得られます:

特徴2が両方のフォールドで最も重要な特徴であることがわかります。特徴3が続いています。numpyのシャッフル関数にランダムシードを固定しないため、この数値は変動する可能性があります。

重要度をグラフで表示して、重要度をより視覚的に把握することもできます:

結論

PFIは、最も重要な特徴を素早く特定するのに役立つシンプルな方法論です。開発中のモデルに適用して振る舞いを確認してみてください。

ただし、この方法の限界にも注意してください。方法の不足点を知らないままだと、誤った解釈をすることになってしまいます。

また、PFIは特徴の重要性を示していますが、モデルの出力にどの方向で影響しているのかは明示されていません。

では、次のモデルでこれをどのように活用するつもりですか?

モデルの全体的な理解を向上させることができる解釈性のある手法について、もっと詳しく説明する投稿をお楽しみに。

We will continue to update VoAGI; if you have any questions or suggestions, please contact us!

Share:

Was this article helpful?

93 out of 132 found this helpful

Discover more