Day 087 — Kaggle API — データダウンロード・提出の自動化

2026-07-07 水 / Phase 3 コーディング Kaggle API / LightGBM / OOF / 実験ログ / 自動提出

📚 背景知識(読んでから問題へ)

🤖
Day 086 で AutoML(AutoGluon・PyCaret)を学びました。今日はKaggle APIを使い、データダウンロードから提出・実験ログ記録まで完全自動化するパイプラインを構築します。Expert 以上のプレイヤーは全員これを使っています。

Kaggle API でできること

ブラウザ操作を Python スクリプト1行に置き換えられます。

作業手動(ブラウザ)API(1行)
データダウンロード数クリック + 解凍 (2〜5分)kaggle competitions download -c titanic
予測ファイルの提出ブラウザ操作 3分kaggle competitions submit -f submission.csv -m "memo"
スコア確認ブラウザで確認kaggle competitions submissions -c titanic
上位ノートブック取得手動検索 + クリックkaggle kernels list --competition titanic
データセット取得検索 + ダウンロードkaggle datasets download user/dataset
💡
Expert への境界線: ブラウザを使わずにコマンドで全操作できるようになると、実験のサイクルが劇的に速くなります。「1日10回試行」も苦にならなくなります。

API キーの取得方法

# 1. Kaggle にログイン後
# 2. 右上アイコン → Account → API セクション
# 3. "Create New Token" → kaggle.json がダウンロードされる

$ mkdir -p ~/.kaggle
$ mv kaggle.json ~/.kaggle/kaggle.json
$ chmod 600 ~/.kaggle/kaggle.json # セキュリティ必須

⚙️ API セットアップ — 環境別の手順

環境kaggle.json の配置方法注意点
ローカル(Mac / Linux) ~/.kaggle/kaggle.json に配置 + chmod 600 一度設定すれば恒久的
Google Colab files.upload() でアップロード → ~/.kaggle/ に移動 セッションごとに必要
Kaggle Notebooks Settings → Add-ons → Environment Variables に設定 セッション間で保持
GitHub Actions / CI Secrets に KAGGLE_USERNAME / KAGGLE_KEY を設定 セキュアな自動化が可能

🗂️ データスキーマ — Titanic(API でダウンロードした後のファイル構成)

ファイル名行数目的API後の配置
train.csv891行学習データ(Survived ラベルあり)./data/titanic/train.csv
test.csv418行テストデータ(Survived なし)./data/titanic/test.csv
gender_submission.csv418行提出フォーマットのサンプル./data/titanic/gender_submission.csv

特徴量一覧(モデルに投入するカラム)

カラム名説明前処理
Survivedint生存フラグ(目的変数)target に指定
Pclassint客室クラス (1/2/3)そのまま
Sexstr性別 male/femalemale=0, female=1
Agefloat年齢(欠損 ~20%)中央値で補完
Farefloat運賃中央値で補完
SibSpint兄弟・配偶者の同乗数そのまま
Parchint親・子の同乗数そのまま
Embarkedstr乗船港 C/Q/SOne-Hot エンコード
FamilySizeint新規: SibSp + Parch + 1派生特徴量
IsAloneint新規: FamilySize==1 → 1派生特徴量

🎯 問題

⚠️
Kaggle API の実行には ~/.kaggle/kaggle.json が必要です。まず Kaggle アカウントで API キーを取得してください。データダウンロード以外の部分は JSON キーがなくてもコードを読んで理解できます。

タスク1: Kaggle API のセットアップと動作確認

1
kaggle パッケージをインストールして接続テストを実行する
kaggle competitions list --search titanic でエラーが出なければ OK
2
Titanic データを API でダウンロードして内容を確認する
-p ./data/titanic/ --unzip オプションを使う

タスク2: モデル学習・予測・提出

1
train.csv を読み込み、特徴量工学(FamilySize / IsAlone 追加)を実施する
2
LightGBM で 5-Fold CV を実行し CV AUC を表示する
3
test.csv に対して予測し submission.csv を作成する
必須カラム: PassengerId / Survived(0 か 1)
4
kaggle competitions submit コマンドで自動提出する

タスク3: 提出履歴の取得と実験ログ記録

1
提出スコアを API で取得して表示する
2
実験結果を JSONL ファイルに追記して記録する

📊 5-Fold CV AUC — 目標スコア感(参考値)

今回の特徴量(FamilySize / IsAlone 追加)で期待される各フォールドの AUC スコア

Fold 1
AUC ≈ 0.87
0.87
Fold 2
AUC ≈ 0.86
0.86
Fold 3
AUC ≈ 0.88
0.88
Fold 4
AUC ≈ 0.85
0.85
Fold 5
AUC ≈ 0.87
0.87
CV Mean (目標)
CV AUC ≈ 0.866
0.866
🏆
CV AUC 0.86 以上は Titanic の 上位25%圏内(Silverメダル圏)の目安です。FamilySize / IsAlone を追加することで Day 081 のベースライン(約 0.83)からスコアが改善します。

🗺️ 完全自動提出パイプライン図

Python スクリプト1本で「データ取得 → 学習 → 提出 → ログ記録」を完結させる

📥 データ取得 kaggle competitions download train.csv / test.csv 🔧 特徴量工学 Sex → 0/1 FamilySize IsAlone 🔁 5-Fold CV LightGBM OOF AUC 確認 CV AUC ≈ 0.866 🚀 自動提出 kaggle competitions submit submission.csv 📝 実験ログ JSONL CV/LBスコア 記録・比較 Step 1 Step 2 Step 3 Step 4 Step 5

💡 ヒント

ヒント1 方向性

kaggle パッケージは pip install kaggle でインストール。~/.kaggle/kaggle.json が正しく配置されていれば kaggle competitions list でコンペ一覧が取得できます。Colab では from google.colab import files; files.upload() で JSON をアップロードできます。

ヒント2 アプローチ
  • ダウンロード: kaggle competitions download -c titanic -p ./data/ --unzip
  • 提出: kaggle competitions submit -c titanic -f submission.csv -m "message"
  • Python から: subprocess.run(["kaggle", "competitions", "submit", ...])
  • FamilySize: df['SibSp'] + df['Parch'] + 1
ヒント3 コード骨格
import subprocess, os
os.makedirs('./data/titanic', exist_ok=True)

# データダウンロード
subprocess.run(['kaggle', 'competitions', 'download',
    '-c', 'titanic', '-p', './data/titanic/', '--unzip'])

# FamilySize 特徴量
df['FamilySize'] = df['SibSp'] + df['Parch'] + 1
df['IsAlone']    = (df['FamilySize'] == 1).astype(int)

# 自動提出
subprocess.run(['kaggle', 'competitions', 'submit',
    '-c', 'titanic', '-f', './submission.csv',
    '-m', 'lgbm-baseline-day087'])

模範解答

# ===== インストール(Colabでは実行が必要) =====
# !pip install kaggle lightgbm -q

import os, subprocess, warnings, json
import numpy as np
import pandas as pd
from sklearn.model_selection import StratifiedKFold
from sklearn.metrics import roc_auc_score
warnings.filterwarnings('ignore')

# ━━━ タスク1: データダウンロード ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
os.makedirs('./data/titanic', exist_ok=True)
subprocess.run([
    'kaggle', 'competitions', 'download',
    '-c', 'titanic', '-p', './data/titanic/', '--unzip'
], check=True)

train = pd.read_csv('./data/titanic/train.csv')
test  = pd.read_csv('./data/titanic/test.csv')
print(f"train: {train.shape}, test: {test.shape}")


# ━━━ タスク2: 特徴量工学 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
def feature_engineering(df):
    df = df.copy()
    df['Sex']        = df['Sex'].map({'male': 0, 'female': 1})
    df['Age']        = df['Age'].fillna(df['Age'].median())
    df['Fare']       = df['Fare'].fillna(df['Fare'].median())
    df['Embarked']   = df['Embarked'].fillna('S')
    df               = pd.get_dummies(df, columns=['Embarked'], prefix='Emb')
    df['FamilySize'] = df['SibSp'] + df['Parch'] + 1
    df['IsAlone']    = (df['FamilySize'] == 1).astype(int)
    return df

features = ['Pclass', 'Sex', 'Age', 'Fare',
            'SibSp', 'Parch', 'FamilySize', 'IsAlone',
            'Emb_C', 'Emb_Q', 'Emb_S']

train_fe = feature_engineering(train)
test_fe  = feature_engineering(test)
for col in features:
    if col not in test_fe.columns:
        test_fe[col] = 0

X      = train_fe[features].values
y      = train['Survived'].values
X_test = test_fe[features].values


# ━━━ LightGBM 5-Fold CV ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
import lightgbm as lgb

params = {
    'objective':       'binary',
    'metric':          'auc',
    'num_leaves':      31,
    'learning_rate':   0.05,
    'feature_fraction': 0.9,
    'bagging_fraction': 0.8,
    'bagging_freq':    5,
    'verbosity':       -1,
    'random_state':    42,
}

skf        = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)
oof_preds  = np.zeros(len(X))
test_preds = np.zeros(len(X_test))
fold_scores = []

for fold, (tr_idx, val_idx) in enumerate(skf.split(X, y)):
    X_tr, X_val = X[tr_idx], X[val_idx]
    y_tr, y_val = y[tr_idx], y[val_idx]

    model = lgb.train(
        params,
        lgb.Dataset(X_tr, label=y_tr),
        num_boost_round=500,
        valid_sets=[lgb.Dataset(X_val, label=y_val)],
        callbacks=[lgb.early_stopping(50, verbose=False),
                   lgb.log_evaluation(period=-1)],
    )
    val_pred = model.predict(X_val, num_iteration=model.best_iteration)
    oof_preds[val_idx] = val_pred
    test_preds += model.predict(X_test, num_iteration=model.best_iteration) / 5

    auc = roc_auc_score(y_val, val_pred)
    fold_scores.append(auc)
    print(f"  Fold {fold+1}: AUC = {auc:.4f}")

cv_mean = np.mean(fold_scores)
oof_auc = roc_auc_score(y, oof_preds)
print(f"\nCV AUC: {cv_mean:.4f}  |  OOF AUC: {oof_auc:.4f}")


# ━━━ submission.csv 作成 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
submission = pd.DataFrame({
    'PassengerId': test['PassengerId'],
    'Survived':    (test_preds >= 0.5).astype(int)
})
submission.to_csv('./submission.csv', index=False)
print(f"\nsubmission.csv: {submission.shape}")
print(f"生存予測率: {submission['Survived'].mean():.2%}")


# ━━━ 自動提出(実際に提出する場合はコメントを外す) ━━━━━━━━━━━━━
# subprocess.run(['kaggle', 'competitions', 'submit',
#     '-c', 'titanic', '-f', './submission.csv',
#     '-m', f'lgbm-5fold-cv{cv_mean:.4f}-day087'])


# ━━━ 実験ログ記録 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
log = {
    'date':    '2026-07-07',
    'day':     87,
    'model':   'LightGBM',
    'cv_auc':  round(cv_mean, 4),
    'oof_auc': round(oof_auc, 4),
    'n_feat':  len(features),
    'message': f'lgbm-5fold-cv{cv_mean:.4f}',
}
with open('./experiment_log.jsonl', 'a') as f:
    f.write(json.dumps(log, ensure_ascii=False) + '\n')
print("\n実験ログ:", json.dumps(log, indent=2, ensure_ascii=False))

🪜 Step-by-Step 解説

1Kaggle API の設定とデータ取得

$ pip install kaggle
$ mkdir -p ~/.kaggle
$ mv kaggle.json ~/.kaggle/
$ chmod 600 ~/.kaggle/kaggle.json # セキュリティ必須

# データダウンロード(-p: 保存先, --unzip: 自動解凍)
$ kaggle competitions download -c titanic -p ./data/titanic/ --unzip
📂
なぜ --unzip が必要か? → Kaggle は ZIP 形式で配信します。--unzip がないと titanic.zip が残り、別途 unzip コマンドが必要になります。

2FamilySize と IsAlone の意味

df['FamilySize'] = df['SibSp'] + df['Parch'] + 1  # +1 は自分自身
df['IsAlone']    = (df['FamilySize'] == 1).astype(int)
FamilySize意味生存率の傾向
1(IsAlone=1)一人旅△ 中程度(孤立しやすい)
2〜4小家族↑ 高い(助け合える)
5〜7大家族↓ 低い(全員を避難させるのが困難)
8+超大家族↓↓ 非常に低い

この非線形な関係を LightGBM は木構造で自然に捉えます。線形回帰では表現が難しい特徴量です。

3OOF(Out-of-Fold)予測の仕組み

Val Train Fold 1 Val Fold 2 Val Fold 3 Val Fold 4 Val Fold 5 Val ブロックを全フォールドで集めると OOF(891件)が完成

4提出コマンドとメッセージの書き方

$ kaggle competitions submit \
  -c titanic \
  -f ./submission.csv \
  -m "lgbm-5fold-familysize-cv0.8660-day087"
フラグ意味ベストプラクティス
-cコンペ ID(URL の末尾)titanic, house-prices-advanced
-f提出ファイルパス提出前に行数・カラムを必ずチェック
-m提出メモ(Public LB に表示)モデル名 + CV スコア + 何を試したかを含める

5実験ログを JSONL で記録する

with open('./experiment_log.jsonl', 'a') as f:
    f.write(json.dumps(log) + '\n')

# 後から DataFrame として読める
df_log = pd.read_json('./experiment_log.jsonl', lines=True)
df_log.sort_values('cv_auc', ascending=False)
💡
JSONL は「1行 = 1実験」の形式。後から pd.read_json('log.jsonl', lines=True) で読み込めば「どの特徴量が効いたか」「CV と LB のギャップはどれくらいか」を比較できます。

🧮 OOF(Out-of-Fold)の直感(文系向け)

📐
OOF とは:
5-Fold CV で「自分が学習に使われていないときに予測された値」を全フォールドで集めたものです。全 891 件について「自分自身を学習に使わずに予測した確率」が得られるので、より現実に近いスコアを計算できます。
評価方法意味Kaggleでの用途
CV AUC(各フォールドの平均) 5回の検証の平均スコア モデル改善の判断基準
OOF AUC 全データを疑似テストとして評価 CV より安定した評価。LBとの相関が高い
Public LB スコア Kaggle が評価する実際のスコア 30% のテストデータで計算
Private LB スコア 最終的なランキングスコア 残り 70% で計算(コンペ終了後公開)

OOF AUC と Public LB の相関イメージ(Expert 以上が重視する理由)

OOF AUC Public LB 高い相関 (r≈0.95) 0.78 0.92

🏆 Kaggleでの実践的な使い方

提出回数の戦略的な使い方

Titanic は1日5回まで提出できます。Expert 以上はこう使います:

提出回数目的何を確認するか
1回目(早期)ベースラインの把握CV との乖離を確認(0.02 以内なら CV 信頼 OK)
2〜3回目(中盤)特徴量の効果検証追加した特徴量が LB を改善しているか
4〜5回目(終盤)ファイナル選択CV が高い版 vs 安定版の2本を最終選択に残す
自動提出スクリプト(再利用テンプレート)
# run_submit.py — コンペを変えても使い回せるテンプレート
import subprocess, sys

COMPETITION = "titanic"        # コンペIDだけ変える
SUBMISSION  = "./submission.csv"
MESSAGE     = sys.argv[1] if len(sys.argv) > 1 else "auto-submit"

result = subprocess.run([
    'kaggle', 'competitions', 'submit',
    '-c', COMPETITION,
    '-f', SUBMISSION,
    '-m', MESSAGE,
], capture_output=True, text=True)

print(result.stdout)
if result.returncode != 0:
    print("エラー:", result.stderr)

使い方: python run_submit.py "lgbm-cv0.87-familysize"

⚠️ よくある誤解・ミス

誤解・ミスなぜ起こるか正しい理解
kaggle.json の配置場所を間違える パスの大文字・隠しディレクトリを見落とす ~/.kaggle/kaggle.json.kaggle ディレクトリが必要)
chmod 600 を忘れる Unix のパーミッション設定を知らない 600 = 自分だけ読み書き可。APIキー流出防止に必須
test データでエンコード列が増減する pd.get_dummies がデータ依存で列数が変わる 全カラムを reindexalign で揃える
LB を提出して過学習させる LB スコアを見て特徴量を選び続ける CV を優先。LB への最適化は Private LB 崩れの原因
実験ログを取らない 「覚えているはず」と思う 3日後には何が効いたか必ず忘れる。JSONL で自動記録

🚀 次のステップ

  • 発展: kaggle kernels list --competition titanic --sort-by voteCount で上位ノートブックをダウンロードし、自分のモデルと比較する
  • 次回予告: Phase 3 最終テーマ「コンペ選定基準 — 自分に合ったコンペの見つけ方」(Day 088)

📝 自己評価(解いた後に記入)

自分の回答・気づき・メモ: