3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Pythonと機械学習で東京23区の地価(住宅地)を予測する③:SHAP値で地価予測のブラックボックスを解き明かす!

Last updated at Posted at 2025-06-13

1. はじめに

以前の記事では、
CatBoostを使った東京23区の住宅地地価予測モデルを構築し、
データの前処理から特徴量エンジニアリングを行い、

そしてグリッドサーチによるハイパーパラメータチューニングまでを行いました。
その結果、RMSE 37,068円という非常に高い予測精度を達成し、
モデルの性能を最大限に引き出すことに成功しました。

モデルが「何を予測するか」 は、RMSEやR^2スコアといった評価指標で確認できます。
しかし、データ分析の現場では、「なぜその予測値が出たのか?」 という疑問が頻繁に生じます。
例えば、以下のような場面です。

ビジネスへの説明
単に「この物件の価格は〇〇円と予測されます」と伝えるだけでなく、
「駅に近いことが価格を押し上げています」といった具体的な根拠を示すことで、
予測への信頼性が大きく高まります。

モデルの改善
予測が外れた場合、どの特徴量が原因で間違ったのかを特定できれば、
モデルの弱点を見つけ、改善のヒントを得ることができます。

公平性の確保
モデルが特定のデータ属性(例えば、地域や構造など)に対して
不公平な判断をしていないかを確認するためにも、
予測の内部メカニズムを理解することが重要です。

このように、高精度なモデルを構築するだけでなく、
その予測の根拠を明確に説明できる「解釈性」は、
モデルを実社会で活用する上で不可欠な要素となっています。

今回は、この「なぜ?」という疑問に答える強力なツール、SHAP値を導入します。

SHAP値を活用することで、私たちの地価予測モデルがどのように機能しているのか、
そして個々の物件の予測がなぜその値になったのかを、具体的に、そして視覚的に深掘りしていきます。

2. SHAP値とは?

「なぜ、この物件の地価は高く予測されたのだろう?」
「どの要因が予測に一番貢献しているんだろう?」

こうした疑問に答えるために登場するのが、SHAP値です。

SHAP値は、
ゲーム理論の「シャープレイ値」 という、全員で協力して得た成果(報酬)を、
各協力者(プレイヤー)がどれだけ貢献したかに応じて公平に分配する考え方を基にしています。
これは、機械学習モデルの予測結果を解釈するための、強力な手法の一つとされています。

これを機械学習の予測に当てはめると、次のようになります。

モデルが算出した「予測値」を、プレイヤー全員で協力して得た「報酬」と見立てます。
予測に使う「各入力特徴量」を、報酬を得るための「プレイヤー」と見立てます。
そしてSHAP値は、それぞれの特徴量(プレイヤー)が予測値(報酬)にどれだけ「貢献」したかを公平に分配して計算することで、「なぜその予測値になったのか」を説明します。

2-1. SHAP値の重要なポイント

SHAP値が単なる特徴量重要度とは異なり、特に強力である理由は以下の通りです。

1. 個別の予測を説明できる(局所的解釈性)

SHAP値は、モデル全体での重要度だけでなく、個々の物件の地価予測が「なぜその値になったのか」を具体的に説明できます
例えば、「この物件の地価がX円と予測されたのは、路線価が高い(プラス要因)ことと、駅距離が遠い(マイナス要因)ことがそれぞれY円分影響したからだ」といった詳細な理由付けが可能です。

2. 加法性

全てのSHAP値を合計すると、モデルの基準値から実際の予測値への変化量に一致します
これにより、予測値が各特徴量にどのように分解され、割り当てられたかが論理的に説明できます。

3. モデル非依存性(Model-Agnostic)

SHAP値は、CatBoostのようなツリーベースモデルだけでなく、あらゆる種類の機械学習モデルの予測を解釈できます
そのため、異なるモデル間でも統一された基準で比較・評価が可能です。

4. 方向性を示す

SHAP値は正負の符号を持ち、予測値を「増加させる方向」に作用したか、「減少させる方向」に作用したかを明確に示します。
これにより、各特徴量が予測に与えた影響の方向性が一目でわかります。

このように、SHAP値はモデルの予測が「どうしてそうなるのか」というブラックボックスを解き明かし、より透明性が高く、信頼できるモデルを構築するために不可欠なツールなのです。

3. CatBoostモデルへのSHAP値導入ステップ

これまでの分析で構築したCatBoostモデルに対し、
実際にSHAP値を導入し、その予測を解釈するための準備を進めます。
SHAPライブラリはPythonで簡単に利用できます。

3-1. shap ライブラリのインストールとインポート

shapライブラリをインストールしていない場合は、以下のコマンドでインストールします。

pip install shap

次に、Pythonスクリプトの冒頭で必要なライブラリと一緒にshapをインポートします。

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib.ticker as ticker
import japanize_matplotlib
import seaborn as sns
from sklearn.model_selection import train_test_split, GridSearchCV
from sklearn.metrics import mean_absolute_error, mean_squared_error, r2_score
from catboost import CatBoostRegressor
import folium
import joblib
import shap # この行を追加

3-2. SHAP Explainerの作成とSHAP値の計算

CatBoostはSHAPライブラリと非常に親和性が高く、
shap.TreeExplainerを使うことで効率的にSHAP値を計算できます。

TreeExplainerを初期化する際には、学習済みのbest_catboost_modelを渡します。
CatBoostモデル自体がカテゴリカル特徴量の情報を保持しているため、
cat_features引数を明示的に渡す必要はありません。

今回は、モデルがまだ見たことのないデータ(X_test_filtered)に対してSHAP値を計算し、
未知のデータに対する予測の根拠を分析します。

# 33. SHAP値の計算と可視化

print("33. SHAP値の計算と可視化:")

# CatBoostモデル用のSHAP explainerを作成
# CatBoostモデルは内部にカテゴリカル特徴量の情報を持っているため、
# cat_features引数はTreeExplainerに明示的に渡す必要はありません。
explainer = shap.TreeExplainer(
    best_catboost_model,
    feature_names=X_train_filtered.columns.tolist() # 特徴量名を渡す
)

# テストデータ (X_test_filtered) のSHAP値を計算
# この計算はデータ量やモデルの複雑さによっては時間がかかる場合があります。
shap_values = explainer.shap_values(X_test_filtered)

print("SHAP値の計算が完了しました。")
print("-" * 50)
33. SHAP値の計算と可視化:
SHAP値の計算が完了しました。

これでSHAP値の計算が完了しました。
shap_valuesオブジェクトには、X_test_filteredの各サンプルにおける、
各特徴量のSHAP値が格納されています。

次のセクションでは、これらのSHAP値を様々なグラフで可視化し、
モデルの予測根拠を深掘りします。

4. SHAP値によるモデルの解釈

SHAP値の計算が完了したので、
これらの値を可視化して、モデルの予測根拠を深掘りします。

SHAPライブラリは、
モデル全体の特徴量重要度から個別の予測の内訳まで、
多角的に分析するための便利な可視化機能を提供しています。

4-1. モデル全体の特徴量重要度 (Summary Plot - Bar Plot)

まずは、モデルがデータセット全体を通して、
どの特徴量を最も重要視しているかを確認します。

これは、各特徴量のSHAP値の絶対値の平均に基づいており、
一般的な特徴量重要度よりも理論的な根拠を持つとされています。

# 全体的な特徴量重要度 (SHAP値の絶対値の平均)
# y軸に特徴量名が表示されるように、X_test_filteredをDataFrameとして渡す
shap.summary_plot(shap_values, X_test_filtered, plot_type="bar", show=False)
plt.title("全体的な特徴量重要度 (SHAP)")
plt.tight_layout()
plt.show()
print("-" * 50)

#34.png

この棒グラフは、上にある特徴量ほどモデルの予測に大きな影響を与えていることを示しています。
地価予測モデルでは、以下の特徴量が特に重要であることが明確に読み取れます。

kakaku_r06(令和6年価格):
最も長い棒であり、予測に圧倒的に大きな影響を与えていることが分かります。
昨年度の価格が、今年の地価予測において最も重要な手がかりとなっていることを示唆しています。

kakaku_r05(令和5年価格):
kakaku_r06に次いで影響が大きく、数年前の地価も予測に重要な要素であることが分かります。

kotei (固定資産税路線価):
予測への影響度が非常に高く、地価と密接な関係にあることが示されています。

rosenka (相続税路線価):
koteiに続いて重要度が高く、公的な価格指標が予測に貢献していることが分かります。

syakutiken (借地権割合):
こちらも比較的上位に位置しており、土地の権利形態が地価に影響を与えていることがうかがえます。

全体として、地価の変動を捉える上で、過去の価格情報や公的な評価額が非常に強力な特徴量であることがSHAP値によって裏付けられました

4-2. 個々の特徴量の影響度と分布 (Summary Plot - Bee Swarm Plot)

次に、各特徴量が個々の予測に与える影響の分布と、その特徴量の値の大小がSHAP値にどう関係しているかを見てみましょう。
このグラフは、単なる重要度だけでなく、特徴量の値の増減が予測にどのような「方向」で影響するかを示します。

# 個々のSHAP値の分布と特徴量の値の関係
# デフォルトでBee Swarm Plotが生成される
shap.summary_plot(shap_values, X_test_filtered, show=False)
plt.title("特徴量ごとの影響度と分布 (SHAP)")
plt.tight_layout()
plt.show()

#35.png

グラフから読み取れること (SHAP Bee Swarm Plot)

このグラフは、地価予測モデルが「なぜ、それぞれの物件の地価をそう予測したのか?」という理由を、それぞれの特徴量について具体的に示してくれるものです。

グラフの見方

横軸
予測への「影響度」
真ん中の「0.0」が基準(ベースラインとなる予測値)です。

右側(プラス方向)にある点
その特徴量が、予測した地価を「高くする方向」に貢献したことを意味します。

左側(マイナス方向)にある点
その特徴量が、予測した地価を「低くする方向」に貢献したことを意味します。

縦軸
それぞれの「特徴量」上にある特徴量ほど、データ全体で見て、
地価予測への平均的な影響が大きかったことを示しています。


その特徴量の「値の大きさ」

赤い点
その特徴量の値が大きい物件を表します。(例:駅距離が「長い」、価格が「高い」)

青い点
その特徴量の値が小さい物件を表します。(例:駅距離が「短い」、価格が「低い」)

各点(ドット)
一つ一つの点が、データの中の「ある一つの物件」を表しています。
同じ特徴量でも、物件によって地価への影響の仕方が違うことが分かります。
このグラフ全体からは、以下のような具体的な洞察が得られます。

プラス・マイナスの影響方向の傾向

もし「赤い点」が右側に、「青い点」が左側に集まっていれば:
その特徴量は「値が大きいほど地価を高くし、値が小さいほど地価を低くする」関係にあるとモデルが学んだことを示します。

もし「青い点」が右側に、「赤い点」が左側に集まっていれば:
その特徴量は「値が小さいほど地価を高くし、値が大きいほど地価を低くする」という逆の関係にあるとモデルが学んだことを示します。

主要な特徴量から得られる洞察

kakaku_r06 (令和6年価格) および kakaku_r05 (令和5年価格):
赤い点(過去の価格が高い物件)が大きく右側(正のSHAP値)に、青い点(過去の価格が低い物件)が左側(負のSHAP値)に分布しています。
これは、過去の価格が高い物件ほど、今年の地価予測値を強く押し上げていることを明確に示しており、これらの特徴量が予測に非常に大きな正の影響を与えていることが裏付けられます。

kotei (固定資産税路線価) および rosenka (相続税路線価):
これらも同様に、赤い点(評価額や路線価が高い物件)が右側に、青い点(低い物件)が左側に分布しています。
過去の価格情報ほど顕著ではありませんが、路線価が高いほど地価予測値を押し上げる要因として機能していることが分かります。

syakutiken (借地権割合):
青い点(借地権割合が低い、所有権に近い物件)がSHAP値の正の領域(右側)に、赤い点(借地権割合が高い物件)がSHAP値の負の領域(左側)に分布しています。
これは、借地権割合が高い物件ほど地価予測値を押し下げる要因となり、借地権割合が低い(所有権に近い)物件ほど地価予測値を押し上げる要因となることを明確に示唆しています。

TT_eki_kyori (東京駅からの直線距離):
赤い点(距離が長い物件)が左側(負のSHAP値)に、青い点(距離が短い物件)が右側(正のSHAP値)に分布しています。
これは、最寄り駅からの距離が遠くなるほど、地価予測値を押し下げる要因として作用していることを明確に示しています。

このように、Bee Swarm Plotは、各特徴量の値が予測にどのように影響するのか、その具体的な「方向性」と「強さ」を個々のデータポイントのレベルで理解するのに非常に有効です。

4-3. 個別の予測の解釈 (Waterfall Plot)

モデル全体がどう動くかを理解したところで、次は特定の1つの物件の予測がなぜその値になったのかを深掘りします。
Waterfall Plotは、ベースライン(平均予測値)からどのように予測値が形成されていくかを、特徴量ごとの貢献度を積み上げ式で示します。

ここでは例として、テストデータセットの最初のサンプル(インデックス 0 の物件)の予測を可視化します。

# 例として、テストデータセットの最初のサンプルの予測を可視化 (Waterfall Plot)
sample_idx = 0
print(f"テストデータセットのインデックス {sample_idx} の物件に対する予測の説明 (Waterfall Plot):")

shap.plots.waterfall(shap.Explanation(
    values=shap_values[sample_idx],
    base_values=explainer.expected_value, # ベースラインとなる予測値
    data=X_test_filtered.iloc[sample_idx], # 対象の物件のデータ
    feature_names=X_test_filtered.columns.tolist() # 特徴量名
), show=False)
plt.title(f"予測の内訳 (サンプル {sample_idx})")
plt.tight_layout()
plt.show()

#36.png

対象の物件(インデックス 0)とは?

今回の地価予測モデルのテストデータセットである X_test_filtered の中で、一番最初の行に位置する物件のデータを指します。

プログラミングでは、リストや表形式のデータ(PandasのDataFrameなど)の要素は、通常 「0」から始まる番号(これを「インデックス」と呼びます) で管理されます。
そのため、インデックス 0 は「最初の要素」を意味します。

コードスニペット中の sample_idx = 0 という記述は、どのサンプル(物件)について詳しく分析するかを「0番目のサンプル」と指定していることを示します。そして、X_test_filtered.iloc[sample_idx] の部分は、テストデータセットからこの 0番目 の物件のデータだけを抽出していることを意味します。

このように、Waterfall Plotは、モデルがまだ学習に使っていない新しい物件データの中から、具体的に「最初の1件」を選んで、その予測がどのような要因で形成されたのかを詳細に可視化しているのです。
これにより、モデルが個々の物件に対してどのように判断を下しているのかを、非常に具体的に理解することができます。

グラフから読み取れること

このWaterfall Plotは、対象の物件(インデックス 0)の地価予測が、データセット全体の平均予測値であるE[f(x)] = 13.33 (対数スケール) からどのように変化して、最終的な予測値 f(x) = 13.186 (対数スケール) になったかを示しています。

ベースラインからの変化

このサンプル物件の予測値 13.186 は、平均予測値 13.33 よりも低い値であることが分かります。
これは、予測を押し下げる要因が全体として優勢だったことを意味します。

予測を押し下げた主な要因(青い棒)

kakaku_r05 = 469000 (-0.06):
一昨年度の価格が469,000円であったことが、予測値を大きく押し下げています。

kakaku_r06 = 496000 (-0.06):
昨年度の価格が496,000円であったことも、同様に予測値を大きく押し下げています。

syakutiken = 60 (-0.02):
借地権割合が60%であることが、予測値を押し下げています。

rosenka = 400000 (-0.02):
路線価が400,000円であったことも、予測値を押し下げています。

その他:
ido(緯度)やchimei_rank(地名ランキング)などの要因もわずかに押し下げています。

予測を押し上げた要因(赤い棒)

kotei = 328000 (+0.02):
固定資産税評価額が328,000円であったことが、予測値を押し上げる要因として働いています。

TT_eki_kyori = 8244 (+0.01):
東京駅からの直線距離が8,244mであったことが、予測値をわずかに押し上げています。

hanzai_R6 = 2276 (+0.01):
犯罪件数が2,276件であったことも、わずかに押し上げています。

このWaterfall Plotからは、この物件の地価予測が、主に過去の価格(kakaku_r05, kakaku_r06)や借地権割合、路線価が押し下げ要因として、一方で固定資産税評価額や東京駅からの距離、犯罪件数がある程度押し上げ要因として作用した結果、最終的に平均よりも低い予測値になったことが明確に理解できます。

4-4. 特定の特徴量の依存性 (Dependence Plot)

特定の1つの特徴量の値が変化したときに、それが予測にどう影響するか(その特徴量のSHAP値がどう変化するか)を視覚化することで、より深い関係性を探ることができます。

ここでは、地価に大きな影響を与える「eki_kyori」(最寄り駅までの距離)に注目します。
interaction_indexを指定していないため、単一の特徴量 eki_kyori とそのSHAP値の関係が描かれます。

# 例えば 'eki_kyori' が予測にどう影響するか (rosenka との交互作用)
shap.dependence_plot(
    "eki_kyori",            # 横軸に表示する特徴量
    shap_values,            # SHAP値
    X_test_filtered,        # 元のデータ
    interaction_index="rosenka", # 色付けする(交互作用を考慮する)特徴量を指定
    show=False
)
plt.title("駅距離と予測への影響 (SHAP Dependence Plot - 路線価との交互作用)")
plt.tight_layout()
plt.show()

#37.png

グラフから読み取れること

このDependence Plotは、「eki_kyori」という特徴量の値が、地価予測への貢献度(SHAP値)にどのように影響するかを示しています。
今回は、点の「」を「rosenka」(路線価)の値で示し、駅距離と路線価の交互作用を読み解きます。

横軸:
eki_kyori (最寄り駅までの距離) の値。

縦軸:
eki_kyori のSHAP値(予測への貢献度)。
正のSHAP値は予測値を押し上げる貢献、負のSHAP値は押し下げる貢献を示します。

点の色:
グラフの右側に示されているカラーバーを見ると、rosenka (路線価) の値を示しています。
色が明るい(赤に近い)ほど路線価が高く、色が暗い(青に近い)ほど路線価が低いことを示します。

このグラフから、以下の重要な傾向が読み取れます。

駅距離が短い場合(〜約1,000mまで):
SHAP値は比較的高く(約0.005〜0.015)、予測値をプラスの方向に押し上げる要因として働いています。
この範囲では、駅距離が短いほど予測値に良い影響を与えています。
注目すべきは、この領域では点の色が比較的青色(路線価が低い)のものから赤色(路線価が高い)のものまで混在している点です。
駅に近ければ、路線価の高低に関わらず、駅距離自体がプラスの影響を与えていると解釈できます。

駅距離が1,000m〜1,300m付近:
SHAP値が急激に0付近まで、あるいはわずかにマイナスの領域に落ち込んでいます。
これは、この距離帯で予測への影響が大きく変化し、プラスの貢献がほとんどなくなることを示唆しています。
このあたりのプロットの色を見ると、青色(路線価が低い)の点が目立ち始めているかもしれません。

駅距離が長い場合(約1,300m超):
SHAP値は大きくマイナス(約-0.015〜-0.035)に落ち込み、予測値を大幅に押し下げる要因として作用していることが明確にわかります。
駅からの距離が長くなるほど、地価に負の影響を与える度合いが増しています。
この領域の点のほとんどが青色(路線価が低い)である場合、駅距離が遠い上に路線価も低い物件は、地価に非常に大きなマイナス影響を与える傾向があると解釈できます。

駅距離と路線価の交互作用

全体として、赤い点(路線価が高い物件)は、同じ駅距離の青い点(路線価が低い物件)よりも、SHAP値がやや高い位置に分布している傾向が見られる可能性があります
これは、「駅距離が遠くても、その土地が路線価の高い地域に属していれば、駅距離によるマイナス影響がいくらか緩和される」という交互作用を示唆します。
特に、駅距離が長くなるにつれて、赤い点(路線価が高い物件)が残っている部分と、青い点(路線価が低い物件)が顕著にマイナスに貢献している部分を比較すると、この交互作用がより明確になるでしょう。

このように、Dependence Plotで適切なinteraction_indexを指定することで、単一の特徴量だけでは見えない、複数の特徴量が組み合わさって予測に影響を与える複雑な関係性を深く洞察することができます。
これはモデルの振る舞いをより詳細に理解する上で非常に有用です。

5. SHAP値から得られた洞察と今後の展望

今回のSHAP値分析を通じて、構築した地価予測モデルの「ブラックボックス」を大きく開くことができました
単に予測精度が高いだけでなく、「なぜその予測が導き出されたのか」という根拠を、具体的な特徴量の貢献度として明確に理解することができました。

5-1. SHAP値から得られた主な洞察

過去の価格情報の圧倒的な重要性:
kakaku_r06(令和6年度価格)やkakaku_r05(令和5年度価格)が予測に最も大きな影響を与えていることがSHAP値のSummary Plot(Bar)で明確に示されました。
これは、不動産市場において、過去の価格推移が現在の価値を形成する上で極めて重要であるという直感を裏付けるものです。

公的評価額の確かな影響:
kotei(固定資産税路線価)やrosenka(相続税路線価)も非常に高い重要度を示し、公的な価格指標がモデルの予測において信頼できる根拠となっていることが確認できました。

距離要素の明確な影響:
eki_kyori(最寄り駅までの距離)やTT_eki_kyori(東京駅までの直線距離)は、距離が長くなるほど地価を押し下げる要因となることが、Bee Swarm PlotやDependence Plotで具体的に示されました。
特にeki_kyoriにおいては、ある程度の距離を超えると予測へのマイナス影響が顕著になる非線形な関係性も見て取れました。

個別物件の予測根拠:
Waterfall Plotは、特定の物件の予測値が、平均値からどのように各特徴量の影響を受けて形成されたかを分かりやすく示してくれました。
これにより、不動産鑑定や査定の際に、予測結果とともにその主要な理由を具体的に説明することが可能になります。

これらの洞察は、モデルの挙動を深く理解する上で非常に価値があります

6. 今後の展望

今回のSHAP値分析によって得られた知見は、地価予測モデルをさらに改善し、実用性を高めるための重要なヒントとなります

特徴量エンジニアリングの深化:
SHAP値で重要性が高く示された特徴量(例: 過去価格、駅距離)は、そのデータの品質向上や、さらに複雑な組み合わせによる新しい特徴量の創出(例: 駅距離に応じた非線形変換、過去価格の変動率など)を検討する価値があります。

モデルのデバッグと改善:
予測が大きく外れる物件があった場合、その物件のWaterfall PlotやDependence Plotを分析することで、モデルがどのような特徴量を誤解しているか、あるいはデータに異常値がないかなどを特定し、モデルの改善に繋げることができます。

意思決定の支援ツールとしての強化:
このモデルを実務で利用する際には、予測値だけでなく、SHAP値による「予測の根拠」も併せて提供することで、より信頼性が高く、納得感のある意思決定をサポートするツールとして進化させることが可能です。例えば、地価が高く予測された理由を「駅徒歩5分圏内であること」といった具体的な要素で説明することで、説得力も増します。

モデルの公平性・頑健性評価:
SHAP値は、特定の地域や物件属性に対してモデルが偏った予測をしていないかなど、モデルの公平性や頑健性を評価するための基盤としても活用できます。

今回の地価予測モデル構築は、データの準備からモデルの最適化、そしてSHAP値による解釈性向上まで、一連の機械学習プロジェクトの流れを実践する良い機会となりました。

この結果が、データ駆動型不動産評価の発展の一助となれば幸いです。

以上

3
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?