「もし私たちが複雑過ぎるモデルを簡単に説明できるとしたらどうだろう?」

「複雑なモデルを簡単に説明できる方法はあるのか?」

CFNOWを使った対事実的説明の生成が大幅に容易になりましたが、対事実的説明とは何であり、どのように使用できるのでしょうか?

Image generated with Illusion Diffusion model with CFNOW text as illusion (try to squint your eyes and look from a certain distance) | Image by the author using Stable Diffusion model (license)

この記事は以下の記事を基にしています:https://www.sciencedirect.com/science/article/abs/pii/S0377221723006598

そして、CFNOWリポジトリのアドレスはこちらです:https://github.com/rmazzine/CFNOW

もしこの記事を読んでいるなら、人工知能(AI)が今日の世界でいかに重要なものになりつつあるかをおそらくご存知でしょう。しかし、効果的で革新的な機械学習手法とその広範な人気が、予期せぬ/望ましくない結果をもたらす可能性があることを意識しておくことは重要です。

これが、説明可能な人工知能(XAI)がAIの倫理的かつ責任ある開発を保証する上で重要な要素である理由です。この分野では、数百万から数十億のパラメータから成るモデルを説明することは容易な問題ではありません。LIME [1] やSHAP [2] が一般的な例ですが、それぞれ異なる側面を明らかにする多くの方法があります。

ただし、これらの方法によって生成される説明の複雑さは、複雑なグラフや分析に結びつく可能性があり、それによってよく知らない専門家以外の人々が誤解する可能性があります。この複雑さを回避するための一つの方法が、カウンターファクト説明と呼ばれるものです [3]。

カウンターファクト説明は、ものごとを説明するための自然な人間の行動を利用し、いくつかのパラメータを変更することで結果を変えることができる「代替の世界」を作り出します。これは一般的な方法であり、おそらくあなた自身も「少し早く起きていれば、バスに乗り遅れることはなかったのに」といったことを既に経験しているでしょう。このタイプの説明は、結果の主な要因をわかりやすく強調します。

さらに詳しく掘り下げると、カウンターファクトは、単なる説明以上のものです。それらは変更のガイドとして役立ち、異常な動作のデバッグを支援し、いくつかの特徴が予測を変える可能性があるかどうかを検証することができます(ただし、スコアリングには影響を与えません)。この多機能性は、予測を説明することの重要性を強調しています。これは責任あるAIの問題だけでなく、モデルの改善と予測の範囲を超えた使用につながる道でもあります。カウンターファクト説明の注目すべき特徴は、その意思決定に基づく性質です。これはLIMEとSHAPとは異なり、スコアを説明するためにより適しています [6]。

明らかな利点があるにもかかわらず、カウンターファクトがもっと人気がないのはなぜでしょうか。これは妥当な疑問です!カウンターファクト説明の広範な採用を妨げる主な障壁は、以下の3つです [4, 5]:(1)ユーザーフレンドリーで互換性のあるカウンターファクト生成アルゴリズムの不在、(2)効率的なカウンターファクトの生成におけるアルゴリズムの非効率性、(3)包括的な視覚的表現の欠如です。

しかし、良いニュースがあります!新しいパッケージであるCFNOW(CounterFactuals NOWまたはCounterFactual Nearest Optimal Wololo)がこれらの課題に対処するために活躍しています。CFNOWは、表形式、画像、テキスト(埋め込み)入力など、さまざまなデータタイプに対して複数のカウンターファクトを生成することができる汎用的なPythonパッケージです。モデルに依存せず、(1)事実点(説明されるべき点)と(2)予測関数の最小限のデータだけが必要です。

さらに、CFNOWは、カスタムロジックに基づいてカウンターファクトを検索して調整するための新しい戦略の開発と統合を可能とする構造になっています。また、カウンタープロットというカウンターファクト説明を視覚的に表現するための革新的な戦略も備えています。

CFNOWの中心には、データをCFジェネレーターで管理可能な単一の構造に変換するフレームワークがあります。これに続いて、2つのステップのプロセスで見つかった反事実を位置付けて最適化します。局所最小値を防ぐために、このパッケージは数理ヒュリスティックスメソッドであるタブーサーチを実装しており、目的関数をより最適化できる可能性のある新しい領域を探索することが可能です。

このテキストの後続のセクションでは、CFNOWを効果的に利用して表、画像、テキスト(埋め込み)分類器の説明を生成する方法に焦点を当てます。

表分類器

ここでは、一般的なものを示します。複数のタイプのデータを持つ表形式のデータです。以下の例では、数値の連続データ、カテゴリのバイナリデータ、カテゴリのワンホットエンコードデータを使用して、CFNOWのフルパワーを紹介します。

まず最初に、CFNOWパッケージをインストールする必要があります。要件はPythonバージョン3.8以上です:

    pip install cfnow

(この例の完全なコードはこちら:https://colab.research.google.com/drive/1GUsVfcM3I6SpYCmsBAsKMsjVdm-a6iY6?usp=sharing)

この最初のパートでは、Adult Datasetを使用して分類器を作成します。ここでは特に新しい情報はありません。

import warningsimport pandas as pdfrom sklearn.model_selection import train_test_splitfrom sklearn.ensemble import RandomForestClassifierfrom sklearn.metrics import accuracy_scorewarnings.filterwarnings("ignore", message="X does not have valid feature names, but RandomForestClassifier was fitted with feature names")

分類モデルを作成するための基本的なパッケージをインポートし、また、列名を指定せずに予測を行うことに関連する警告を無効にします。

次に、所得が50k以下(≤50K)を表すクラス1と、高所得を表すクラス0の分類器を作成します。

# 分類器を作成するimport warningsimport pandas as pdfrom sklearn.model_selection import train_test_splitfrom sklearn.ensemble import RandomForestClassifierfrom sklearn.metrics import accuracy_scorewarnings.filterwarnings("ignore", message="X does not have valid feature names, but RandomForestClassifier was fitted with feature names")# Adultデータセットを読み込むdataset_url = "https://archive.ics.uci.edu/ml/machine-learning-databases/adult/adult.data"column_names = ['age', 'workclass', 'fnlwgt', 'education', 'education-num', 'marital-status',                'occupation', 'relationship', 'race', 'sex', 'capital-gain', 'capital-loss',                'hours-per-week', 'native-country', 'income']data = pd.read_csv(dataset_url, names=column_names, na_values=" ?", skipinitialspace=True)# 欠損値を含む行を削除するdata = data.dropna()# 2つ以上のユニークなカテゴリを持つ非バイナリのカテゴリ特徴量を識別するnon_binary_categoricals = [column for column in data.select_dtypes(include=['object']).columns                            if len(data[column].unique()) > 2]binary_categoricals = [column for column in data.select_dtypes(include=['object']).columns                        if len(data[column].unique()) == 2]cols_numericals = [column for column in data.select_dtypes(include=['int64']).columns]# ワンホットエンコーディングを非バイナリのカテゴリ特徴量に適用するdata = pd.get_dummies(data, columns=non_binary_categoricals)# バイナリのカテゴリ特徴量を数字に変換する# これにより、ターゲット変数(所得)もバイナリ化されますfor bc in binary_categoricals:    data[bc] = data[bc].apply(lambda x: 1 if x == data[bc].unique()[0] else 0)# データセットを特徴量とターゲット変数に分割するX = data.drop('income', axis=1)y = data['income']# データセットをトレーニングセットとテストセットに分割するX_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)# RandomForestClassifierを初期化するclf = RandomForestClassifier(random_state=42)# 分類器を訓練するclf.fit(X_train, y_train)# テストセットで予測を行うy_pred = clf.predict(X_test)# 分類器の評価accuracy = accuracy_score(y_test, y_pred)print("Accuracy:", accuracy)

上記のコードでは、データセットを作成し、前処理し、分類モデルを作成し、テストセット上での予測と評価を行っています。

さて、テストセットから1つのポイントを取り上げて予測を検証しましょう:

clf.predict([X_test.iloc[0]])# 結果: 0 -> 高収入

そして、CFNOWを使用して、特徴量を最小限に変更することでこの予測をどのように変更できるかを計算します:

from cfnow import find_tabular# その後、CFNOWを使用して分類を変更するための最小変更を生成しますcf_res = find_tabular(    factual=X_test.iloc[0],    feat_types={c: 'num' if c in cols_numericals else 'cat' for c in X.columns},    has_ohe=True,    model_predict_proba=clf.predict_proba,    limit_seconds=60)

上記のコードでは:

  • factualはpd.Seriesとして実際のインスタンスを追加します
  • feat_typesは特徴量のタイプを指定します(数値の連続型は “num”、カテゴリカルは “cat”)
  • has_oheはOHE(One-Hot-Encoding)の特徴量があることを示します(同じ接頭辞の後にアンダースコアが続く特徴量を集計して自動的に検出します。例: country_brazil, country_usa, country_ireland)。
  • model_predict_probaは予測関数を含みます
  • limit_secondsは実行のための合計時間の閾値を定義します。これは、微調整ステップが無限に続く可能性があるため重要です(デフォルトは120秒)

その後、しばらく時間が経った後、最良の反事実クラス(cf_res.cfsの最初のインデックス)のクラスを最初に評価できます

clf.predict([cf_obj.cfs[0]])# 結果: 1-> 低収入

そして、ここでCFNOWとの違いがいくつか現れます。CFNOWはCounterPlotsも統合しているため、チャートをプロットして以下のようなより深い情報を得ることができます:

CounterShapley Chart for our CF | Image by the author

下のCounterShapleyプロットは、カウンターファクトの予測の各特徴の相対的な重要性を示しています。ここでは、傾向のある状態(マリアルステータス)がCFクラスの寄与の50%以上を占めるという興味深い洞察が得られます。

Greedy Chart for our CF | Image by the author

グリーディーチャートはCounterShapleyと非常に似ており、ここでの主な違いは変更の順序です。CounterShapleyは特定の順序を考慮しない(Shapleyの値を使用して寄与を計算する)一方、グリーディーチャートは最もCFクラスに寄与する特徴を変更する最も欲張りな戦略を使用します。これは目的を達成するために最も良いアプローチを選択することが求められる場合に役立つかもしれません。

Constellation Chart for our CF | Image by the author

最後に、最も複雑な分析である星座チャートがあります。見た目は少し圧倒的ですが、実際には非常に直感的に解釈できます。大きな赤い点は1つの特徴の変更(ラベルに関連)を表し、小さな点は2つ以上の特徴の組み合わせを表します。最後に、大きな青い点はCFスコアを表します。ここでは、これらの特徴をすべてそれぞれの値に変更することでCFを得る唯一の方法があることがわかります(つまり、CFを生成するサブセットは存在しません)。さらに、特徴間の関係を調査し、興味深いパターンを見つけることもできます。

この特定の場合では、高収入の予測が、女性で離婚し、自分の子供を持っている場合に変化することが興味深い観察でした。このカウンターファクトは、異なる社会集団への経済的な影響についてさらなる議論を引き起こす可能性があります。

画像分類器

CFNOWは既に述べたように、さまざまなタイプのデータで動作することができます。そのため、イメージデータに対して対照的なデータを生成することも可能です。しかし、イメージデータセットに対する対照事例とは具体的に何を意味するのでしょうか?

その回答は様々であり、対照的なデータを生成する方法には複数の方法があります。例えば、単一のピクセルをランダムノイズで置き換える(敵対的攻撃で使用される方法)や、より複雑なセグメンテーション手法を用いることができます。

CFNOWは、信頼性と高速性に優れたセグメンテーション手法である「quickshift」という手法を使用しています。ただし、他のセグメンテーション手法も統合することが可能です(お勧めします)。

セグメントの検出だけでは対照事例の説明を生成するには不十分です。セグメントを変更し、変更されたバージョンに置き換える必要があります。この変更に関して、CFNOWはパラメータreplace_modeで定義されている4つのオプションを持っています。デフォルトのオプションはblurです。これは置き換えられたセグメントにぼかしフィルターを追加します。また、meanオプションでは、セグメントを平均色で置き換えます。randomオプションでは、ランダムノイズで置き換えます。そしてinpaintオプションでは、周囲のピクセルに基づいて画像を再構築します。

コード全体が必要な場合は、こちらで見つけることができます:https://colab.research.google.com/drive/1M6bEP4x7ilSdh01Gs8xzgMMX7Uuum5jZ?usp=sharing

次に、このタイプのデータに対するCFNOWのコード実装を示します:

まず、もしまだCFNOWパッケージをインストールしていない場合は、以下のコマンドを実行してください。

pip install cfnow

次に、いくつかの追加パッケージを読み込んで事前学習済みのモデルをロードします:

pip install torch torchvision Pillow requests

次に、データをロードし、事前学習済みモデルをロードし、CFNOWが受け取るデータ形式と互換性のある予測関数を作成します:

import requestsimport numpy as npfrom PIL import Imagefrom torchvision import models, transformsimport torch# 事前学習済みのResNetモデルをロードmodel = models.resnet50(pretrained=True)model.eval()# イメージ変換を定義transform = transforms.Compose([    transforms.Resize(256),    transforms.CenterCrop(224),    transforms.ToTensor(),    transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),])# ウェブ上から画像を取得image_url = "https://upload.wikimedia.org/wikipedia/commons/thumb/4/41/Sunflower_from_Silesia2.jpg/320px-Sunflower_from_Silesia2.jpg"response = requests.get(image_url, stream=True)image = np.array(Image.open(response.raw))def predict(images):    if len(np.shape(images)) == 4:        # リスト内のnumpy配列をテンソルのバッチに変換        input_images = torch.stack([transform(Image.fromarray(image.astype('uint8'))) for image in images])    elif len(np.shape(images)) == 3:        input_images = transform(Image.fromarray(images.astype('uint8')))    else:        raise ValueError("入力は画像のリストまたは単一の画像である必要があります。")        # 使用可能なGPUがある場合はGPUを使用し、そうでない場合はCPUを使用    device = torch.device("cuda" if torch.cuda.is_available() else "cpu")    input_images = input_images.to(device)    model.to(device)        # 推論を実行    with torch.no_grad():        outputs = model(input_images)        # 各画像の推論スコアの配列を返す    return torch.asarray(outputs).cpu().numpy()LABELS_URL = "https://raw.githubusercontent.com/anishathalye/imagenet-simple-labels/master/imagenet-simple-labels.json"def predict_label(outputs):    # 事前学習済みモデルで使用されるラベルをロード    labels = requests.get(LABELS_URL).json()        # 予測ラベルを取得    predicted_idxs = [np.argmax(od) for od in outputs]    predicted_labels = [labels[idx.item()] for idx in predicted_idxs]        return predicted_labels# 画像の予測結果をチェックpredicted_label = predict([np.array(image)])print("予測されたラベル:", predict_label(predicted_label))

このコードの大部分はモデルの構築、データの取得、調整に関連しています。CFNOWを使用して対照事例を生成するには、次の手順を行うだけです:

from cfnow import find_image
cf_img = find_image(img=image, model_predict=predict)
cf_img_hl = cf_img.cfs[0]
print("予測されたラベル:", predict_label(predict([cf_img_hl])))
#CFイメージを表示
Image.fromarray(cf_img_hl.astype('uint8'))

上記の例では、すべてのデフォルトオプションパラメーターを使用したため、画像をセグメント化し、セグメントをぼかし画像で置換するためにquickshiftを使用しました。その結果、以下の事実の予測結果が得られました:

事実のイメージが「デイジー」と分類されました | イメージのタイトル: Sunflower (Helianthus L). Słonecznik by Pudelek (Edit by Yzmo and Vassil) from Wikimedia under GNU Free Documentation License, Version 1.2

以下のようにします:

CFイメージが「蜂」と分類されました | イメージのタイトル: Sunflower (Helianthus L). Słonecznik by Pudelek (Edit by Yzmo and Vassil) from Wikimedia under GNU Free Documentation License, Version 1.2

この分析から得られる結果は何でしょうか?実際には、画像の対立仮想を使用することで、モデルがどのように分類を行っているかを検出するための非常に有用なツールとなります。以下の場合に適用することができます:(1) モデルが正しい分類を行った理由を検証したい場合、正しい画像特徴を使用していることを確認します。この場合、モデルはひまわりをデイジーと誤分類しましたが、花をぼかすと予測が変わることが見えます。また、(2) 誤分類された画像の診断に役立ち、画像処理やデータ取得の洞察を向上させることができます。

テキスト分類器

最後に、埋め込みに基づいたテキスト分類器があります。単純なテキスト分類器(タブularデータのようなデータ構造を使用するもの)は、タブラーカウンターファクトジェネレータを使用できますが、埋め込みに基づいたテキスト分類器はそれほど明確ではありません。

その理由は、埋め込みには入力となる単語の数や単語が予測スコアと分類にかなり影響する可能性があるためです。

CFNOWは、次の2つの戦略でこれを解決します:(1) 証拠を削除すること、または(2) 反意語を追加することです。最初の戦略はシンプルで、テキストの各単語がテキストに与える影響を測定するため、単にそれを削除し、分類を反転させるために削除する必要がある単語を見るだけです。一方、反意語を追加することで、意味構造を保持することが可能です(単語を削除するとそれが重大な損害を与える可能性があるため)。

次に、以下のコードはこの文脈でCFNOWを使用する方法を示しています。

コード全体を確認したい場合は、こちらを参照してください:https://colab.research.google.com/drive/1ZMbqJmJoBukqRJGqhUaPjFFRpWlujpsi?usp=sharing

まず、CFNOWパッケージをインストールします:

pip install cfnow

次に、テキスト分類に必要なパッケージをインストールします:

pip install transformers

次に、前のセクションと同様に分類器を構築します:

from transformers import DistilBertTokenizer, DistilBertForSequenceClassificationfrom transformers import pipelineimport numpy as np# センチメント分析のための事前学習済みモデルとトークナイザのロードmodel_name = "distilbert-base-uncased-finetuned-sst-2-english"tokenizer = DistilBertTokenizer.from_pretrained(model_name)model = DistilBertForSequenceClassification.from_pretrained(model_name)# センチメント分析パイプラインを定義しますentiment_analysis = pipeline("sentiment-analysis", model=model, tokenizer=tokenizer)# シンプルなデータセットを定義しますtext_factual = "I liked this movie because it was funny but my friends did not like it because it was too long and boring."result = sentiment_analysis(text_factual)print(f"{text_factual}: {result[0]['label']} (confidence: {result[0]['score']:.2f})")        def pred_score_text(list_text):    if type(list_text) == str:        sa_pred = sentiment_analysis(list_text)[0]        sa_score = sa_pred['score']        sa_label = sa_pred['label']        return sa_score if sa_label == "POSITIVE" else 1.0 - sa_score    return np.array([sa["score"] if sa["label"] == "POSITIVE" else 1.0 - sa["score"] for sa in sentiment_analysis(list_text)])

このコードでは、実際のテキストが高い確信度(≥0.9)でNEGATIVEのセンチメントを持つことが分かるため、カウンターファクトを生成してみましょう:

from cfnow import find_textcf_text = find_text(text_input=text_factual, textual_classifier=pred_score_text)result_cf = sentiment_analysis(cf_text.cfs[0])print(f"CF: {cf_text.cfs[0]}: {result_cf[0]['label']} (confidence: {result_cf[0]['score']:.2f})")

上記のコードでは、わずかな修正(しかし)で分類がNEGATIVEからPOSITIVEに変わり、高い確信度が示されました。これは、モデルが文を予測する方法を理解したり、望ましくない動作をデバッグするためにカウンターファクトがどれだけ役立つかを示すものです。

結論

これはCFNOWとカウンターファクト説明の(相対的に)簡潔な紹介でした。カウンターファクトに関する広範で増加している文献は、深く掘り下げる場合は必ず参照するべきです。Ph.D.アドバイザーであるDavid Martens教授によって書かれたこの先駆的な記事[3]は、カウンターファクト説明へのより良い導入になります。さらに、Verma et al [7]によるような良いレビューもあります。要約すると、カウンターファクト説明は、複雑な機械学習アルゴリズムの意思決定を説明するための容易で便利な方法であり、正しく適用すれば説明に留まらず、データとモデルの潜在能力を最大限に活用することができます。

参考文献:

[1] — https://github.com/marcotcr/lime[2] — https://github.com/shap/shap[3] — https://www.jstor.org/stable/26554869[4] — https://www.mdpi.com/2076-3417/11/16/7274[5] — https://arxiv.org/pdf/2306.06506.pdf[6] — https://arxiv.org/abs/2001.07417[7] — https://arxiv.org/abs/2010.10596

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