

Kaggle Getting Started コンペ「Store Sales – Time Series Forecasting」に挑戦しました。エクアドルのスーパーマーケットチェーンの実データを使い、約4,000の店舗×商品カテゴリの組み合わせについて、将来15日間の売上(unit sales)を予測する時系列予測の入門コンペです。評価指標はRMSLE(対数平均二乗誤差平方根)で、数値が小さいほど高精度になります。
今回は Google Gemini(無料版 → Google AI Studio)とのチャットをそのままKaggle Notebook用のPythonコードに落とし込んでいく、という進め方で取り組みました。仕上げのスライドはNotebook LMで作成し、反復的なスコア改善の軌跡を「ブレイクスルーの系譜」として図解しています。
最終スコア:RMSLE 0.429(ベースラインの線形回帰 0.902 から半分以下に短縮、上級者・銀メダル圏内レベル)
- 1. このコンペについて:4種のデータでRMSLEを最小化せよ
- 2. 武器選定:Gemini(無料版)か、Google AI Studioか
- 3. 戦場の見取り図:スコア階層と今回の目標
- 4. EDAで判明した3つの壁
- 5. Phase 1:ベースラインの構築と線形モデルの限界
- 6. パラダイムシフト:決定木モデル(LightGBM)への移行
- 7. Phase 2:過去の力 ― ラグと移動平均の導入
- 8. アンサンブルの罠
- 9. Phase 3:Target Encodingによる上級への突破口
- 10. Phase 4:精密射撃 ― 決定木の弱点を補うチューニング
- 11. Final Breakthrough:神は細部に宿る
- 12. 進化の系譜:反復的ブレイクスルーの統合マトリックス
- 13. Key Takeaways:AI時代のデータ分析戦略
- 14. まとめと次の課題
このコンペについて:4種のデータでRMSLEを最小化せよ

与えられる材料は「店舗情報(54 stores)」「原油価格(Oil prices)」「休日データ(Holidays)」「過去の売上(Transaction history)」の4種類。これらを組み合わせて、約4,000カテゴリ(店舗×商品ファミリー)それぞれの将来15日間の売上を予測し、RMSLEを最小化するのがミッションです。706チームが参加するGetting Started枠のコンペで、時系列予測の基礎を一通り学べる設計になっています。
武器選定:Gemini(無料版)か、Google AI Studioか

結論としては、無料版(またはGoogle AI Studio)から始めて、必要になったら課金を検討するという順序が最も合理的でした。train.csvは数百万行ありますが、Geminiにデータそのものを学習させるわけではなく、あくまで「予測コードを生成させる」「分析戦略を立案させる」ための相棒として使う、という役割分担だからです。
ただし同じGeminiモデルでも、通常のチャットとGoogle AI Studioでは出力の質に無視できない差が出ました。コンテキストウィンドウは最大200万トークンでKaggleの議論や全CSVを一括読み込みでき、システムプロンプトで「Kaggle Grandmaster」としてのペルソナを固定でき、さらにバックグラウンドでPythonを実行して重いCSVを直接分析できる(Code Execution)。この3点の差が、結論として「圧倒的なコンテキストと役割固定が可能なGoogle AI Studio一択」という判断につながりました。
戦場の見取り図:スコア階層と今回の目標

このコンペのスコア帯は、初級(0.90〜、線形回帰・単純なカレンダー)→初中級(0.60〜0.89、外部データ導入)→中級(0.45〜0.59、LightGBM・単純なラグ)→上級(0.41〜0.44、16日以上のラグ・高度な特徴量)→最上級/神(0.40未満、アンサンブル・ドメイン知識の極致)という階段構造になっています。今回のターゲット到達点はRMSLE 0.429(銀メダル圏内)に定め、実際にそこへ到達するまでの過程を以下に記録しています。
EDAで判明した3つの壁

本格的にコードを書く前に、このデータセット特有の難しさを3つの壁として整理しました。
- 課題1:外部ショック――2016年4月16日のエクアドル地震直後、特定カテゴリの売上が急増している。これを外れ値として除外するか、特需フラグを立てて学習させるか?
- 課題2:ラグの乖離――予測期間は15日間あり、将来の予測に「前日の売上」は使えない(未知のため)。この乖離をどう補うか?
- 課題3:階層構造――全体データは54店舗×33カテゴリに分かれる。1つの巨大モデルで解くか、個別モデルを作るか?
この3つの問いが、そのまま後続の特徴量エンジニアリングの設計方針になっています。
Phase 1:ベースラインの構築と線形モデルの限界

まずはLinear Regression+カレンダー特徴量(曜日・月)だけのシンプルなベースラインでRMSLE 0.902。そこに原油価格(Oil)と休日(Holiday)を加えたRidge回帰でRMSLE 0.761まで改善しました。0.90から0.76への初期改善は確かな前進でしたが、線形回帰には根本的な限界があります。「時代のトレンド」や、休日とプロモーションが重なるような「非線形な相乗効果」を捉えきれないのです。
パラダイムシフト:決定木モデル(LightGBM)への移行

線形回帰は「複雑な条件分岐ができない」という構造的な限界を持ちます。そこでモデルをLightGBMに変更し、経過日数(Time Step)を追加したところ、RMSLE 0.761 → 0.471とスコアがほぼ半減する大きなブレイクスルーが起きました。決定木モデルは「週末か?」「原油価格は下がったか?」といった条件分岐を自動的に学習でき、カテゴリ間の複雑な関係性を線形モデルよりずっと的確に捉えられます。
Phase 2:過去の力 ― ラグと移動平均の導入

「過去の売上」は最大の予測因子です。ただし予測対象日から見て、テスト期間(15日間)の未来を予測するためには、データリークを防ぎつつ16日以上前のデータに依存するラグ設計が必須になります。Lag 16・21・30と移動平均(Rolling Mean)を追加した結果、RMSLE 0.471 → 0.461で0.5の壁を完全に突破しました。
アンサンブルの罠

ここで一度スコアが後退します。LightGBMとRidge回帰を0.85:0.15の加重平均でアンサンブルしたところ、RMSLE 0.461 → 0.464とわずかに悪化しました。原因はスケールの不一致です。LightGBMは店舗ID(1〜54)を正しく「カテゴリラベル」として扱いますが、線形モデルのRidgeは同じ数値を「純粋な数値の大小」(54は1の54倍)として誤解釈してしまいます。異なる特性を持つモデルを単純に足し合わせると、スケーリングの不在によって逆効果になることがある、という学びを得た停滞期でした。
Phase 3:Target Encodingによる上級への突破口

「店舗×商品」という細かいカテゴリの相互作用を、Target Encoding(学習データのみで計算)によって強力なベースライン数値に変換します。さらに原油価格の7日間移動平均を加えて経済の大きなトレンドを把握させたところ、RMSLE 0.464 → 0.440まで改善し、ついに上級者の壁に到達しました。
Phase 4:精密射撃 ― 決定木の弱点を補うチューニング

ここからは0.440を土台に、決定木モデルが苦手なポイントをピンポイントで補うチューニングです。Time Step(経過日数)で「全体的な経済成長による右肩上がりのトレンド」を明示的に教え、将来の過小評価を防ぎます。また Rolling Volatility(売上の標準偏差)を加え、毎日安定して売れる商品と特売で爆発するプロモーション商品の「波の激しさ」を区別できるようにしました。num_leaves=127でパターン認識を深め、L1/L2正則化で過学習を防止。これが最終突破への助走になります。
Final Breakthrough:神は細部に宿る

最後の一手は2つのドメイン知識でした。1つはLag 364(364日=52週間前、昨年同曜日のトレンド)の追加。「去年の今頃、何が起きたか」は非常に強力な指標です。もう1つは1月1日の学習データからの除外。エクアドルの元旦はほぼ全店舗が休業で売上がゼロになりますが、8月後半のテスト期間を予測する上でこの特異日は純粋なノイズにしかなりません。学習率0.015・feature_fraction 0.6という慎重なチューニングと合わせて、Final Score RMSLE 0.429、銀メダル圏内を達成しました。最終コードの中核部分はこちらです。
# --- 【改善1】ノイズ除去 ---
# 1月1日は多くの店舗が休みで特殊すぎるため、学習から外す
train = train[train['date'].dt.strftime('%m-%d') != '01-01']
# --- 【改善2】フーリエ特徴量(年周期の滑らかな波) ---
day_of_year = data['date'].dt.dayofyear
data['sin_year'] = np.sin(2 * np.pi * day_of_year / 365.25)
data['cos_year'] = np.cos(2 * np.pi * day_of_year / 365.25)
# --- 【改善3】ラグの拡充 (1年前の動きを追加) ---
group_cols = ['store_nbr', 'family']
# 直近ラグ(16-30日前)に加えて、364日前(ちょうど52週間前)を追加
for lag in [16, 21, 30, 364]:
data[f'lag_{lag}'] = data.groupby(group_cols)['sales'].shift(lag)
params = {
'objective': 'regression',
'metric': 'rmse',
'learning_rate': 0.015, # さらに慎重に学習
'num_leaves': 180, # 表現力を強化
'feature_fraction': 0.6, # 特徴量を間引いて過学習防止
'bagging_fraction': 0.8,
'bagging_freq': 5,
'lambda_l1': 0.5,
'lambda_l2': 0.5,
}
進化の系譜:反復的ブレイクスルーの統合マトリックス

| # | スコア (RMSLE) | 主要な変更・追加した手法 | 得られた知見 |
|---|---|---|---|
| 1 | 0.902 | 線形回帰 + 曜日・月・給料日 | 基本的なカレンダー情報だけでは時系列の複雑さを捉えきれない |
| 2 | 0.761 | Ridge回帰 + 油価(oil) + 休日(holiday) + 店舗情報 | 石油価格(経済指標)と祝日が売上に直結することを証明 |
| 3 | 0.471 | LightGBM導入 + 経過日数(Time Step) | 線形モデル→決定木モデルへの変更で、カテゴリ間の複雑な関係を学習 |
| 4 | 0.461 | ラグ特徴量 (Lag 16, 21, 30) + 移動平均 | 「過去の売上」が最大の予測因子であることを確認。0.5の壁を突破 |
| 5 | 0.464 | Ridgeとのアンサンブル | スケールの不一致がノイズを生むという失敗からの学び |
| 6 | 0.445 | ターゲットエンコーディング | カテゴリの平均ベースラインが学習効率を劇的に向上 |
| 7 | 0.440 | 店舗×カテゴリのターゲットTE + Volatility | 店舗×商品の細かな特性と波の激しさをモデル化 |
| 8 | 0.429 | Lag 364 + 1月1日の除外 | 高度なドメイン知識とノイズ除去が最後の壁を破る |
大きな転換点は3ステップ目です。線形回帰・Ridgeでは0.7〜0.9台で頭打ちだったスコアが、LightGBM導入だけでほぼ半減しました。一方でステップ5(Ridgeとのアンサンブル)はスコアが逆に悪化しています。地味に失敗した施策も含めて記録しておくのが、後から見返したときに役立ちました。
Key Takeaways:AI時代のデータ分析戦略

今回のワークフローを振り返って得られた学びは3つです。
- 全体像のコンテキスト共有:単にコードを要求するのではなく、データスキーマ・コンペのルール・Kaggleの議論をAI Studioの200万トークンの文脈に全投入する。
- 仮説に基づいた指示:「特徴量を作れ」ではなく、「経済の遅行性を捉えるため原油価格の7日移動平均を作れ」と、人間の仮説に基づくエンジニアリングを指示する。
- エラーと限界のディスカッション:文法エラーの修正だけでなく、「線形モデルと決定木のアンサンブルがなぜ失敗したか」という構造的・数学的な限界についてAIと議論する。
AIは単なるコーダーではなく、最強の壁打ち相手である。
まとめと次の課題
線形回帰のベースライン(0.902)からLightGBM+ラグ特徴量+ターゲットエンコーディング+年周期ラグまで積み上げて、最終的にRMSLE 0.429、上級者・銀メダル圏内レベルまで到達しました。
次に伸ばすなら、0.40切り(銀〜金メダル圏内)に向けて次のあたりが課題です。
- LightGBM + XGBoost + CatBoostなど、決定木系モデル同士の質の高いアンサンブル
- 地震発生直後のデータをフラグ化するか除外するかの精緻な検証
- 店舗ごと・カテゴリごとにモデルを分離するアプローチとの比較
追加:notebook lmで作成した説明pdf