DALEXで実践する説明可能AI超入門
— 第2回 —
DALEXの環境構築と基本操作

DALEXで実践する説明可能AI超入門— 第2回 —DALEXの環境構築と基本操作

前回の記事では、なぜAIの判断根拠を説明することが重要なのか、そしてXAI(説明可能AI)がどのような場面で活用されているかを学びました。

DALEXで実践する説明可能AI超入門— 第1回 —なぜ今XAI(説明可能AI)が必要なのか

今回はいよいよ実践編です。

実際に手を動かしながら、DALEXというライブラリを使って、AIモデルの中身を「見える化」する体験をしていきましょう。

料理のレシピを順番に実行するように、一つずつ進めていけば、必ずゴールにたどり着けます。

「なぜAIがその判断をしたのか」を可視化できるようになっているはずです。

環境構築:DALEXを使う準備を整えよう

Google ColabやJupyter Notebookを開いて、以下のコマンドを実行しましょう。すでにインストールされている場合は必要ありません。

# 必要なライブラリをインストール
!pip install dalex          # DALEX本体:XAIの主役
!pip install xgboost        # XGBoost:高性能な機械学習モデル
!pip install pandas         # pandas:データを扱うための基本ツール
!pip install numpy          # numpy:数値計算の基礎ライブラリ
!pip install scikit-learn   # scikit-learn:機械学習の定番ライブラリ
!pip install matplotlib     # matplotlib:グラフ描画用

 

インストールが完了したら、実際に使えるか確認してみましょう。

# ライブラリをインポート(道具箱から必要な道具を取り出すイメージ)
import dalex as dx
import pandas as pd
import numpy as np
import xgboost as xgb
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error
import warnings
warnings.filterwarnings('ignore')  # 不要な警告を非表示に

# バージョン確認(正しくインストールされたかチェック)
print(f"DALEX version: {dx.__version__}")

 

以下、実行結果です。インストールされたタイミングで、バージョンは異なります。

DALEX version: 1.7.2

 

サンプルデータで学習

 データの準備

今回は、機械学習の入門でよく使われる「タイタニック号の乗客データ」を使います。

このデータセットは、1912年に沈没したタイタニック号の乗客情報と、その人が生存したかどうかを記録したものです。

年齢、性別、チケットクラスなどの情報から、生存確率を予測するモデルを作り、その判断根拠を可視化していきます。

# タイタニックデータセットを読み込む
titanic = dx.datasets.load_titanic()

# データの中身を確認
print("最初の5行を表示:")
print(titanic.head())
print(f"\nデータの形状: {titanic.shape}")
print(f"行数(乗客数): {titanic.shape[0]}, 列数(特徴量): {titanic.shape[1]}")

# 各列の意味を理解しよう
print("\n各列の説明:")
print("- survived: 生存したか(1=生存、0=死亡)")
print("- class: チケットクラス(1st, 2nd, 3rd)")
print("- gender: 性別")
print("- age: 年齢")
print("- sibsp: 同乗した兄弟・配偶者の数")
print("- parch: 同乗した親・子供の数")
print("- fare: 運賃")
print("- embarked: 乗船港")

 

以下、実行結果です。

最初の5行を表示:
   gender   age class     embarked   fare  sibsp  parch  survived
0    male  42.0   3rd  Southampton   7.11      0      0         0
1    male  13.0   3rd  Southampton  20.05      0      2         0
2    male  16.0   3rd  Southampton  20.05      1      1         0
3  female  39.0   3rd  Southampton  20.05      1      1         1
4  female  16.0   3rd  Southampton   7.13      0      0         1

データの形状: (2207, 8)
行数(乗客数): 2207, 列数(特徴量): 8

各列の説明:
- survived: 生存したか(1=生存、0=死亡)
- class: チケットクラス(1st, 2nd, 3rd)
- gender: 性別
- age: 年齢
- sibsp: 同乗した兄弟・配偶者の数
- parch: 同乗した親・子供の数
- fare: 運賃
- embarked: 乗船港

 

 データの前処理

予測する目的変数 ysurvived(1=生存、0=死亡)、説明変数 X は目的変数以外です。説明変数 X には文字列などのカテゴリカルな変数と、数値データにより構成されます。

機械学習モデルは数値しか理解できません。そのため、文字データを数値に変換する必要があります。get_dummies関数は通常、文字列やカテゴリカルな変数をダミー変数に変換します。数値データはそのまま保持され、ダミー化されません。

# データの前処理
# カテゴリ変数(文字データ)を数値に変換
X = pd.get_dummies(titanic.drop('survived', axis=1))  # 説明変数(予測に使う情報)
y = titanic['survived']  # 目的変数(予測したい結果)

 

訓練データとテストデータに分割します。訓練データでモデルを学習し、テストデータで検証します。

# 訓練データとテストデータに分割
X_train, X_test, y_train, y_test = train_test_split(
    X, # 説明変数
    y, # 目的変数
    test_size=0.25, # テストデータの割合
    random_state=42 
)

print(f"訓練データ: {X_train.shape[0]}件")
print(f"テストデータ: {X_test.shape[0]}件")

 

以下、実行結果です。

訓練データ: 1655件
テストデータ: 552件

 

 XGBoostモデルの構築

いよいよ機械学習モデルを構築します。XGBoostは、多くのデータ分析コンペティションで優勝実績のある強力なアルゴリズムです。決定木を何本も組み合わせて、より正確な予測を行います。

# XGBoostモデルを構築
model = xgb.XGBRegressor(
    n_estimators=100,     # 決定木の数(多いほど複雑だが時間もかかる)
    max_depth=3,          # 各決定木の深さ(深いほど複雑)
    learning_rate=0.1,    # 学習の速度(小さいほど慎重に学習)
    random_state=42       # 再現性のための種
)

# モデルを訓練(学習させる)
model.fit(X_train, y_train)

# モデルの性能を評価
y_pred = model.predict(X_test)
rmse = np.sqrt(mean_squared_error(y_test, y_pred))
print(f"予測精度(RMSE): {rmse:.4f}")
print("(値が小さいほど予測精度が高い)")

 

以下、実行結果です。

予測精度(RMSE): 0.3876

 

モデルをDALEXで解釈する

 Explainerオブジェクトの作成

ここからが本題です。構築したXGBoostモデルは高い予測精度を持っていますが、なぜその予測をしたのかは分かりません。

DALEXのExplainerオブジェクトは、このブラックボックスなモデルに「説明能力」を与えます。

# Explainerオブジェクトを作成
explainer = dx.Explainer(
    model,                        # 解釈したいモデル
    X_test,                       # テストデータ(説明変数)
    y_test,                       # テストデータ(目的変数)
    label='XGBoost生存予測モデル'    # モデルの名前(グラフに表示される)
)

# Explainerの基本情報を確認
print("Explainerの準備完了!")
print(f"モデルタイプ: {explainer.model_type}")
print(f"データサイズ: {explainer.data.shape}")

 

以下、実行結果です。

Preparation of a new explainer is initiated

  -> data              : 552 rows 17 cols
  -> target variable   : Parameter 'y' was a pandas.Series. Converted to a numpy.ndarray.
  -> target variable   : 552 values
  -> model_class       : xgboost.sklearn.XGBRegressor (default)
  -> label             : XGBoost生存予測モデル
  -> predict function  : <function yhat_default at 0x7f7c33dedea0> will be used (default)
  -> predict function  : Accepts pandas.DataFrame and numpy.ndarray.
  -> predicted values  : min = -0.0196, mean = 0.33, max = 1.18
  -> model type        : regression will be used (default)
  -> residual function : difference between y and yhat (default)
  -> residuals         : min = -1.15, mean = -0.0115, max = 0.906
  -> model_info        : package xgboost

A new explainer has been created!
Explainerの準備完了!
モデルタイプ: regression
データサイズ: (552, 17)

 

 特徴量重要度の可視化

最初は「特徴量重要度」の可視化です。

これは、モデルが予測する際に「どの情報を重視しているか」を示すもので、まるでテストで「どの科目の配点が高いか」を知るようなものです。

DALEXの特徴量重要度は、Permutation Feature Importance(PFI) の一種です。

PFI(Permutation Feature Importance)とは「ある特徴量の値をランダムに並べ替え、その状態で再度予測し、モデルの損失がどれだけ悪化するかを測定する」ことで、特徴量の重要度を計るアプローチをとったものです。

# 特徴量重要度を計算
vi = explainer.model_parts()

# 結果を表示
print(vi.result)

# より見やすい棒グラフで可視化
vi.plot(show=True)

 

以下、実行結果です。

                  variable  dropout_loss           label
0      embarked_Queenstown      0.386838  XGBoost生存予測モデル
1         embarked_Belfast      0.386892  XGBoost生存予測モデル
2   class_victualling crew      0.387328  XGBoost生存予測モデル
3             _full_model_      0.387577  XGBoost生存予測モデル
4              gender_male      0.387577  XGBoost生存予測モデル
5       embarked_Cherbourg      0.387618  XGBoost生存予測モデル
6                    parch      0.387749  XGBoost生存予測モデル
7                class_2nd      0.387826  XGBoost生存予測モデル
8     embarked_Southampton      0.387962  XGBoost生存予測モデル
9   class_engineering crew      0.388654  XGBoost生存予測モデル
10  class_restaurant staff      0.389331  XGBoost生存予測モデル
11                   sibsp      0.393254  XGBoost生存予測モデル
12               class_1st      0.396066  XGBoost生存予測モデル
13                    fare      0.396840  XGBoost生存予測モデル
14         class_deck crew      0.402720  XGBoost生存予測モデル
15                     age      0.413680  XGBoost生存予測モデル
16               class_3rd      0.421111  XGBoost生存予測モデル
17           gender_female      0.514895  XGBoost生存予測モデル
18              _baseline_      0.545857  XGBoost生存予測モデル

 

この出力した表のdropout_loss は、各条件での「モデルの損失」を表す数値です。

「モデルの損失」とは、モデルの予測値と実際の正解値とのズレを定量的に測った指標です。損失が小さいほど、モデルは正確に予測できていることを意味します。

たとえば、gender_female(性別の女性フラグ)という変数だけを ランダムに並び替え(=無効化) した状態で計算した「モデルの損失」が0.514895ということです。

これは、「ある変数を使えなくしたとき、モデルの性能がどれだけ悪化するか」を測ったものです。

そのため、「モデルの損失」が大きいほど、重要な変数であるということになります。

ここで、この出力の_baseline__full_model_に着目します。これは、Dalexの特徴量重要度分析における2つの基準点で、モデル全体の性能を測るための「両端の比較軸」となっています。

項目 意味
_baseline_ 0.545857 どの変数も使わない(=完全にランダムな予測)ときの損失値
_full_model_ 0.387577 すべての変数を使ったときの損失値(最良の状態)

 

ここで、_baseline__full_model_を計算してみます。

0.545857 − 0.387577 = 0.1583

すべての「説明変数」を使うことによって誤差を0.158改善できたことになります。

逆の見方をすると、最良の状態(_full_model_)から全変数を除くと、モデルの損失が0.158悪化することを意味します。

 

このことを、各変数に対し適用してみます。

つまり、最良の状態(_full_model_)から各変数を除くと、損失どの程度増えるか(悪化するのか)を考えてみましょう。

たとえば、gender_female を除外すると、モデルの損失が0.127悪化します。

項目 意味
full_model(_full_model_ 0.3876 すべての変数を使ったときのモデルの損失(=最も良い状態)
gender_female 除外時 0.5149 性別の情報を壊した(使えなくした)ときの損失
差(悪化量) 0.5149 − 0.3876 = +0.127 性別を除外したことで損失が0.127増加

 

この悪化量(dropout_loss の差分)をグラフ化します。

vi.plot(show=True)

 

以下、実行結果です。

 

棒が長いほど、その特徴量が重要であることを示しています。

このタイタニックのデータでは、通常以下のような傾向が見られます。

  • 性別(gender)が最も重要:当時の「女性と子供を優先」という避難ルールを反映
  • チケットクラス(class)も重要:上級クラスの乗客ほど生存率が高い
  • 年齢(age)の影響:若い人ほど生存率が高い傾向

このように、特徴量重要度を見ることで、モデルが現実の状況を適切に学習できているかを確認できます。

 

 Break Down分析で個別予測を説明

次に、特定の乗客一人について、なぜモデルがその予測をしたのかを詳しく見てみましょう。

これは医師が患者一人一人に合わせた診断をするようなものです。

# テストデータから一人の乗客を選択
passenger_index = 0  # 最初の乗客を選択
single_passenger = X_test.iloc[[passenger_index]]  # 二重括弧でDataFrame形式を維持

# この乗客の情報を確認
print("選択した乗客の情報:")
for col, val in single_passenger.iloc[0].items():
    if val != 0:  # 0でない値だけ表示(ダミー変数のため)
        print(f"  {col}: {val}")

 

以下、実行結果です。

選択した乗客の情報:
  age: 20.0
  gender_female: True
  class_restaurant staff: True
  embarked_Southampton: True

 

選択した乗客の予測(生存確率)を実施します。

# 予測結果
prediction = model.predict(single_passenger)[0]
actual = y_test.iloc[passenger_index]
print(f"\nモデルの予測生存確率: {prediction:.2%}")
print(f"実際の結果: {'生存' if actual == 1 else '死亡'}")

 

以下、実行結果です。予測結果だけでなく、実際の結果も表示しています。

モデルの予測生存確率: 91.34%
実際の結果: 生存

 

この予測結果の要因を分解します。個別予測のBreak Downプロット(寄与分解図) です。

# 予測の要因分解
bd = explainer.predict_parts(
    single_passenger, 
    type='break_down'  # 段階的に要因を分解する手法
)

# 結果を可視化
bd.plot(show=True)

 

以下、実行結果です。

 

このグラフは、 一人の乗客に対して、モデルが「なぜこの人は生存すると予測したのか」を可視化しています。

縦軸には特徴量(性別、年齢、船室など)、横軸にはそれぞれの特徴がどの程度、 生存確率(予測値)を押し上げたか・押し下げたか が示されています。

モデル全体の平均的な生存確率(ベースライン)は 0.33(33%)ですが、この乗客は最終的に 0.913(91.3%)の生存確率 と予測されています。

以下では、どの特徴がどのようにこの予測に影響したのかを見ていきます。

特徴量 寄与値 寄与方向 解釈
intercept(ベースライン) 0.33 平均的な生存確率の出発点。ここから各特徴を加算していく。
gender_female = 1.0 +0.463 ⬆️ プラス 女性であることが大きく生存確率を押し上げた(救助の優先対象)。
class_3rd = 0.0 +0.089 ⬆️ プラス 3等船室ではない(上級船室)ことが生存を助けた。
fare = 0.0 +0.035 ⬆️ プラス 運賃の効果は小さいが、他変数との関係でわずかにプラスに作用。
age = 20.0 +0.024 ⬆️ プラス 若年であることが生存率を少し上げた。
sibsp = 0.0 +0.003 ⬆️ プラス 同行家族がいないことで混乱を避けやすかった可能性。
embarked_Queenstown = 0.0 −0.002 ⬇️ マイナス Queenstown出発でないことがわずかにマイナス(影響はごく小)。
embarked_Belfast = 0.0 +0.004 ⬆️ プラス Belfast出発でないことがわずかにプラス。
gender_male = 0.0 −0.045 ⬇️ マイナス モデル上の符号処理による補正項。実質的には女性効果と同義。
その他の特徴(+ all other factors) ほぼ0 ±微小 補正項。ほとんど影響なし。

 

この乗客は、「若い女性で上級船室にいた」 という特徴を持っており、Titanicの生存者によく見られる典型的なパターンに一致しています。そのため、ベースライン(33%)より大幅に高い 91.3% の生存確率 が予測されました。

寄与の内訳を見ると、性別(女性) が最も強い要因であり、次いで 船室の等級 が生存確率を押し上げています。年齢や運賃といった他の要素は補助的にプラス寄与をしています。

全体として、このモデルは「社会的優先順位(女性・上級船室・若年)」という歴史的な救助傾向を学習しており、この乗客を「ほぼ確実に生存」と判断したことが理解できます。

 

まとめ

今回は、DALEXの環境構築から始めて、実際にモデルを構築し、その判断根拠を可視化するところまで体験しました。

特徴量重要度を見ることで、モデルがどの特徴を重視しているかが分かり、Break Down分析によって個別の予測がなぜそうなったのかを理解できました。

これはXAIの世界への最初の一歩にすぎません。

次回は、より高度な手法であるPDP(Partial Dependence Plot)やICE(Individual Conditional Expectation)を学び、特徴量と予測結果の関係をさらに深く理解していきます。

これらの手法を使えば、「年齢が10歳上がると生存確率はどう変化するか」「運賃と生存率の関係は線形なのか、それとも閾値があるのか」といった、より複雑な問いに答えることができるようになります。