🗺️

【第1回】SIGNATE 第2回 国土交通省 地理空間情報データチャレンジ: 初見とベースライン

に公開

SIGNATE 第2回 国土交通省 地理空間情報データチャレンジ: 初見とベースライン


🗺️ はじめに

この記事は、私が参加している 「第2回 国土交通省 地理空間情報データチャレンジ」(SIGNATE主催)の取り組みを記録するシリーズの第1回です。

このシリーズの目的は以下のとおりです:

  • 機械学習および地理空間分析の理解を より深める
  • 他の参加者の参考になる 実践的な知見を共有する

本記事では、コンペ概要、データセットの特徴、評価指標のポイント、そして初期ベースラインモデルの構築について扱います。


🎯 コンペ概要

項目 内容
コンペティション 第2回 国土交通省 地理空間情報データチャレンジ
タスク 国の地理空間データを用いた不動産売買価格の予測
評価指標 MAPE(Mean Absolute Percentage Error)
コードリポジトリ mmrbulbul/ml-competition-notes

📋 タスク説明

国土数値情報(地理空間データ)および提供された物件データを用いて、不動産売買価格 を予測します。

重要ポイント:地理空間データの活用は 必須 です。

地理空間データにはまだ不慣れなので、まずは「表形式データだけでどこまで戦えるか?」を試したいと考えています。ただし、前回大会の上位者は地理空間特徴量を大きく活用しており、この領域は避けて通れないと感じています。


📊 データセット概要

  • Train: 363,924 サンプル / 149 特徴量
  • Test: 112,437 サンプル(ターゲットなし)
  • Target: money_room(物件の売買価格・円)

特徴量には、築年数・構造などの物件属性、緯度経度や住所といった位置情報、日付関連情報、最寄り駅や周辺施設までの距離など、多岐にわたる要素が含まれます。

一見すると 149 特徴量ですが、data_definition.xlsx を確認すると、以下の列はスラッシュ区切りで複数の値が入っており、展開が必要です:

["building_tag_id", "unit_tag_id",
 "reform_interior", "reform_exterior", "reform_wet_area",
 "statuses"]

⚠️ 重要:時系列のズレ

README.pdf によると、学習データとテストデータは掲載時期が異なります

学習用データ:2019〜2022年の 1月または 7月
テストデータ:2023年の 1月または 7月

この時間差から考えられるポイント

  • 不動産市場の変化による データ分布のズレ
  • 時間関連特徴量の重要度が増す
  • 訓練・テストの分布比較が必須
  • バリデーションはランダム分割より 時系列分割の方が適切

これらは次回以降で詳しく扱います。


📏 評価指標:MAPE

評価指標は MAPE(平均絶対パーセンテージ誤差) です。

MAPE = (1/n) * Σ |y_true - y_pred| / |y_true| * 100

直感的で分かりやすい一方、学習の目的関数としては扱いにくい という課題があります。

主な理由:

  • 0 に近いターゲットで不安定
  • 対称性がなく、過小予測にバイアスがかかりやすい

解決策:ログ変換

相対誤差に強い表現にするため、ターゲットに log 変換 を行い、学習時には MAE / RMSE / Huber Loss を使用するのが一般的です。

  • RMSE:大きな誤差に敏感(補正が必要な場合あり)
  • MAE:MAPE の性質にやや近い
  • Huber:MAE と RMSE の中間(α の調整が必要)

🧪 ベースラインモデルの構築

まずはシンプルなベースラインを作成します。

前処理

実施した主な処理

  1. スラッシュ区切りタグの one-hot 展開
    → 特徴量数:149 → 約 509

  2. 日付系特徴量の 統一フォーマット化

  3. 欠損値処理

    • 完全に空の列(欠損率=1.0)を削除
    • カテゴリカル列を文字列に統一
  4. 地理情報の変換

    • addr1_1addr1_2(コード)→ 都道府県名、市区町村名

1. 欠損値の確認と処理

まず、欠損値の状況を確認します:

def get_missing_report(df):
    missing_df = df.isnull().sum(axis=0).reset_index()
    missing_df.columns = ['column_name', 'missing_count']
    missing_df = missing_df.loc[missing_df['missing_count']>0]
    missing_df["missing_ratio"] = missing_df["missing_count"]/len(df)
    return missing_df

train_missing = get_missing_report(train_df)

完全に空の列(欠損率=1.0)を削除します:

# trainとtestから削除
drop_cols = train_missing[train_missing.missing_ratio==1].column_name
train_df.drop(drop_cols, axis=1, inplace=True)
test_df.drop(drop_cols, axis=1, inplace=True)

2. スラッシュ区切り特徴量の展開

data_definition.xlsx を確認すると、以下の列はスラッシュ区切りで複数の値が入っています:

slashed_columns = ["building_tag_id", "unit_tag_id",
                   "reform_interior", "reform_exterior", "reform_wet_area",
                   "statuses"]

例:"210301/210101/210201" → 3つの個別特徴量に展開

まず、タグIDから内容へのマッピング辞書を作成:

tag_master = pd.read_excel(f"{ROOT_DIR}/data_definition.xlsx",
                           sheet_name=data_definition.sheet_names[2])
tag_master = tag_master[['タグID', 'タグ内容']]
tag_master["タグID"] = tag_master["タグID"].astype("str")
tag_master.set_index('タグID', inplace=True)
tag_master = tag_master.to_dict()['タグ内容']

次に、スラッシュ区切りの列を展開する関数:

def get_slashed_tags(df):
    """スラッシュ区切りの値を持つ列を個別の列に変換する"""
    temp_dfs = []
    for col in slashed_columns:
        temp_df = df[col].str.get_dummies(sep="/").astype("str")
        temp_df.rename(columns=tag_master, inplace=True)
        new_col_name = [f"{col} " + c for c in temp_df.columns]
        temp_df.columns = new_col_name
        temp_dfs.append(temp_df)
    temp_dfs = pd.concat(temp_dfs, axis=1)
    return temp_dfs

重要: pandas.get_dummies() を使用するため、trainとtestで列数が一致しない可能性があります。
そこで、結合 → 展開 → 再分割 という手順を取ります:

# trainとtestを結合
combined_df = pd.concat([train_df, test_df])
# スラッシュ区切り列を展開
slashed_df = get_slashed_tags(combined_df)

# 新しく生成された列名を保存
tag_columns = slashed_df.columns
# 抽出した特徴量を結合
combined_df = pd.concat([combined_df, slashed_df], axis=1)
# スラッシュ区切りの列を削除
combined_df.drop(slashed_columns, axis=1, inplace=True)
# trainとtestに分割
train_df = combined_df[:len(train_df)]
test_df = combined_df[len(train_df):]

これにより、374列の新しい特徴量が追加され、合計約509列になりました。

3. モデル構築

ターゲットを対数変換し、CatBoostで学習します。

損失関数には、ベースラインのシンプルさを優先して MAE を使用します。将来的にはRMSEやHuber損失との比較も検討していきます。

# ターゲットを対数変換
train_y = np.log1p(train_money_room)

# モデルパラメータ
params = {
    "loss_function": "MAE",
    "task_type": "GPU",  # CPUで実行する場合は削除
    "random_seed": 2025,
    "verbose": 1000,
    "iterations": 5000
}

# モデル学習
model = CatBoostRegressor(**params)
model.fit(train_df, train_y, cat_features=categorical_cols)

# 予測
train_pred = np.expm1(model.predict(train_df))

# 誤差計算
from sklearn.metrics import mean_absolute_percentage_error
mape_error = mean_absolute_percentage_error(train_money_room, train_pred)
print("MAPE Error ", mape_error)

MAPE Error 0.1312

ベースライン結果

このシンプルなベースラインモデルで提出した結果:

Public Leaderboard Score: 17.64 (MAPE)

表形式データのみを用いた初回提出としては、悪くないスタート地点です。

特徴量重要度の分析

CatBoostの特徴量重要度を確認すると、以下のトップ20が得られました:

Rank Feature Name Importance
1 house_area 13.977
2 year_built 12.145
3 City/town/village name 8.936
4 full_address 5.212
5 madori_kind_all 4.588
6 building_create_date 3.458
7 snapshot_land_area 2.821
8 eki_name1 2.512
9 post1 2.427
10 walk_distance1 2.186
11 Prefecture name 2.016
12 money_kyoueki 1.802
13 floor_count 1.691
14 lon 1.399
15 target_ym 1.286
16 rosen_name1 1.277
17 el 1.254
18 bus_time1 1.043
19 lat 1.024
20 statuses 浴室乾燥機 0.978

重要な気づき

  • 物件属性(面積、築年)が圧倒的に重要
  • 地理情報(市区町村、住所、駅、経緯度)が上位にランクイン
  • 時系列情報(target_ym)も重要度が高い
  • 特徴量展開により生成された statuses 浴室乾燥機 がトップ20入り。この結果は、スラッシュ区切りタグの展開が有効であったことを示している

これらの特徴量を起点に、さらなる特徴量エンジニアリングを検討していきます。


🗂️ 今後の予定

ベースラインを構築したことで、改善すべきポイントが見えてきました。今後は以下の領域に取り組んでいく予定です(詳細と順序はTBD):

  • 詳細なEDA(探索的データ分析)

    • データ分布、時系列変化、地域特性の理解を深める
  • バリデーション戦略の改善

    • 時系列分割など、テストデータの特性を考慮したCV戦略
  • 欠損値処理の見直し

    • より適切な補完方法や欠損パターンの活用
  • 特徴量エンジニアリング

    • 地理空間特徴量(コンペの必須要件)
    • 時系列・集約・交互作用特徴量
  • モデルの高度化

    • アンサンブル、時系列ドリフト対策など
  • 誤差分析

    • 予測精度の改善ポイントを特定

🧵 まとめ

今回のポイント

  • シンプルなベースラインモデルを構築

    • スラッシュ区切り特徴量の展開(149 → 509特徴量)
    • Log変換 + MAE損失でCatBoostを学習
    • Public Leaderboard: 17.64 MAPE
  • 特徴量重要度の分析

    • 物件属性(house_area、year_built)が最重要
    • 地理情報(市区町村、住所、駅、経緯度)が上位
    • 時系列情報(target_ym)の重要性を確認
  • 今後の改善方向性を明確化

    • バリデーション戦略(時系列分割)
    • 欠損値処理の改善
    • 地理空間特徴量の追加(必須要件)
    • 特徴量エンジニアリングの強化

次回は 詳細なEDA を行い、データの特性をより深く理解していきます。お楽しみに!

本文の一部は、AI による翻訳・文章生成を参考にしています。

Discussion