予約の入り方と曜日と天気予報から翌日の来店客数を見込み、生鮮食材の発注量の案を店舗ごとに出す
毎日15時の時点の予約の状況、曜日、天気予報、過去の来店実績から、翌日の来店客数を店舗ごとに予測します。予測をレシピと在庫で食材の量に直し、発注単位にそろえた発注量の案を店長に出します。
- 連携・自動化
- Google Apps Script/Python
- 対象業界
- 宿泊/飲食
- 対象部門
- 購買
- 対象業務
- 比較検討/集計・分析
- 主な課題
- データ分析に時間がかかる/判断に時間がかかる/属人化している
- AIで行う処理
- 予測
- 主な効果
- 判断支援/属人化解消/工数削減
- 導入難易度
- ★★★☆☆
- 実装レベル
- 本格構成
- 費用感
- 個別開発(大)
- 人間の確認
- 条件付き
01導入前 / 導入後の業務フロー
- 15時ごろ、店長が予約台帳を開き、翌日の予約の組数と人数を数える
- 天気予報を見て、雨なら少なめ、晴れの週末なら多めに見込む
- 前年の同じ曜日の客数を、POSの画面か手元のノートで確かめる
- 冷蔵庫を見て、主な食材の在庫を確かめる
- 翌日の客数の見込みから、食材ごとの必要な量を頭の中で計算する
- 在庫を引き、仕入先の発注単位に合わせて、受発注システムに入力する
- 翌日、足りなければ近くの店舗から借りるか、メニューを売り切れにする。余れば、日持ちしないものは廃棄する
- 人閉店時に、店長か閉店の担当が主な生鮮食材の在庫を在庫入力の表に入れる(現在も棚卸しとして行っている作業)
- 自動毎日15時、Python のプログラムが動き、POSの実績、予約台帳のその時点の状況、天気予報を取り込む
- 自動翌日の来店客数を、店舗ごとに中央の見込みと多めの見込みの2つで予測する
- 自動過去4週の同じ曜日のメニューの出数の割合とレシピから、食材ごとの必要な量を出す
- 自動品目の日持ちに応じて見込みを選び、在庫を引き、発注単位に切り上げて発注量の案にする
- 自動予測の根拠(予約数、曜日、天気、過去の同じ条件の実績)と一緒に、発注量の案を店舗ごとの表に出す
- 人店長が案を見て、団体の予約や近くのイベントなど、データに入っていないことがあれば直す
- 人直した品目には理由を一言で書き、受発注システムに入力する
- 自動翌日の閉店後に、実際の客数と出数を取り込み、予測との差を記録する
各工程の詳しい説明を読む
- 15時ごろ、店長が予約台帳を開き、翌日の予約の組数と人数を数える
- 天気予報を見て、雨なら少なめ、晴れの週末なら多めに見込む
- 前年の同じ曜日の客数を、POSの画面か手元のノートで確かめる
- 冷蔵庫を見て、主な食材の在庫を確かめる
- 翌日の客数の見込みから、食材ごとの必要な量を頭の中で計算する
- 在庫を引き、仕入先の発注単位に合わせて、受発注システムに入力する
- 翌日、足りなければ近くの店舗から借りるか、メニューを売り切れにする。余れば、日持ちしないものは廃棄する
(a)毎日20分、ピーク前の時間が取られる。 15時から17時は、ディナーの仕込みの時間でもあります。発注に20分かけると、その分だけ仕込みが他のスタッフに回ります。
(b)予約の読み方が店長によって違う。 前日15時に予約が30人入っているとき、それを「多い」と読むか「少ない」と読むかは、その曜日に当日の予約と飛び込みがどのくらい上乗せされるかを知っているかどうかで決まります。この知識は店長の頭の中にしかありません。
(c)5番の計算が荒い。 客数から食材の量を出すには、メニューごとの出数の割合と、1人前の使用量を掛け合わせる必要があります。40品目について毎日これを正確に行うのは現実的ではなく、「いつもの量」を少し増減させる形になります。
(d)店長が替わると、発注の結果が変わる。 異動や休みで別の人が発注すると、その店舗の廃棄と欠品が目に見えて変わります。 経験の違いが、そのまま食材の損に出ます。
- 【人】 閉店時に、店長か閉店の担当が主な生鮮食材の在庫を在庫入力の表に入れる(現在も棚卸しとして行っている作業)
- 【自動】 毎日15時、Python のプログラムが動き、POSの実績、予約台帳のその時点の状況、天気予報を取り込む
- 【自動】 翌日の来店客数を、店舗ごとに中央の見込みと多めの見込みの2つで予測する
- 【自動】 過去4週の同じ曜日のメニューの出数の割合とレシピから、食材ごとの必要な量を出す
- 【自動】 品目の日持ちに応じて見込みを選び、在庫を引き、発注単位に切り上げて発注量の案にする
- 【自動】 予測の根拠(予約数、曜日、天気、過去の同じ条件の実績)と一緒に、発注量の案を店舗ごとの表に出す
- 【人】 店長が案を見て、団体の予約や近くのイベントなど、データに入っていないことがあれば直す
- 【人】 直した品目には理由を一言で書き、受発注システムに入力する
- 【自動】 翌日の閉店後に、実際の客数と出数を取り込み、予測との差を記録する
7番目が、この設計の要です。 案は店長の判断を置き換えるものではありません。データに入っていないことを知っているのは店長です。 翌日に近くの会場でコンサートがある、常連の会社の送別会が入りそうだ、といったことは予約台帳にも天気予報にも出てきません。
8番目で理由を残すのは、モデルを直す材料にするためです。 同じ理由の修正が続けば、それはデータに足すべき項目です。「近くのイベント」が毎月出るなら、イベントの予定表を入力に足します。
02今回想定するシステム構成
POS(来店客数・メニューの出数) 予約台帳(15時時点の予約) 天気予報(その日に取得した予報)
│ │ │
└──────────────┬───────────────┴────────────────────────────┘
▼【トリガー】毎日15時
Python(pandas で取り込み・整形)
├──▶ 来店客数の予測(scikit-learn、中央の見込み/多めの見込み)
├──▶ メニューの出数の割合 × レシピ = 食材ごとの必要な量
└──▶ 日持ちで見込みを選び、在庫を引き、発注単位に切り上げ
▼
店舗ごとの発注量の案(根拠付き)
▼
【店長が確認・修正 → 理由を記録】
▼
受発注システムへ入力 ── 翌日、実績との差を記録| 役割 | 想定する製品 | 代替候補 |
|---|---|---|
| 実行環境 | Python(pandas と scikit-learn で予測と発注量の計算) | Google Apps Script(予測は外部で行い、集計と表の作成のみ) |
| 予測モデル | scikit-learn の HistGradientBoostingRegressor | LightGBM |
| 保管 | 共有ストレージ(取り込んだデータの写しと、予測・案・実績の記録) | データベース |
| 発注 | 既存の受発注システム(店長が入力) | 仕入先ごとの発注の画面 |
| 案の表示 | Google スプレッドシート(店舗ごとのシート) | Microsoft Excel(共有のブック) |
生成AIは使いません。 この構成で行うのは、過去の数字から翌日の数字を見込む予測と、その数字を食材の量に直す計算です。どちらも、同じ入力から毎回同じ答えが出る仕組みのほうが、店長に説明しやすくなります。
予測には、scikit-learn の HistGradientBoostingRegressor を使います。 scikit-learn のドキュメントでは、ヒストグラムにもとづく勾配ブースティングの回帰木で、データが大きい(1万件以上)場合には従来の GradientBoostingRegressor よりはるかに速いとされています。10店舗 × 数年分の日次のデータは数万行で、この範囲に入ります。
この構成で効くのは、3つの性質です。 1つ目は、loss='quantile' と quantile の値を指定すると分位点を予測できることです。中央の見込みには0.5、多めの見込みには0.8のように、別のモデルとして学習させます。2つ目は、欠けた値(NaN)をそのまま扱えることで、学習の途中で、欠けた値の行を分岐のどちら側に送るかを学びます。天気予報を取り損ねた日も、行ごと捨てずに済みます。3つ目は、categorical_features で店舗や曜日をカテゴリとして扱えることです。
データの取り込みには pandas の read_csv を使います。 POSや予約台帳の書き出しはCSVが多く、encoding、parse_dates、dtype、usecols で文字コード・日付の列・列の型・読む列を指定できます。
03どうやって実装するのか
処理の起点を決める
毎日15時に、Python のプログラムを定時で動かします。 サーバーの定時実行の仕組み(タスクスケジューラや cron)で起動します。
15時にするのは、仕入先の締めから逆算した時刻です。 発注の締めが17時なので、店長が案を見て直す時間を1時間以上取れる時刻にします。予約は15時以降にも入りますが、15時の時点の数で予測し、それ以降の分は「当日までに上乗せされる予約」として学習の側に含めます。 実行時刻を日によって変えると、この上乗せの量が変わり、予測がずれます。
実行の時刻は、学習にも使います。 過去のデータを作るときも、毎日15時の時点で見えていた予約の数を取り出して学習させます。ここが第1章で述べた、この構成のもう一つの中心です。
翌日の閉店後にも、1日1回、実績を取り込む処理を動かします。実際の客数と出数を取り込み、予測との差を記録します。
入力データを集める
| データ | 中身 | 取得元 |
|---|---|---|
| 来店客数の実績 | 店舗・日付・時間帯ごとの客数 | POSの書き出し |
| メニューの出数 | 店舗・日付・メニューごとの出数 | POSの書き出し |
| 予約の状況 | 各日15時時点の、翌日の予約の組数と人数、コースの予約数 | 予約台帳の書き出し(毎日15時に保存) |
| 天気予報 | その日に取得した翌日の予報(天気、最高気温、降水確率) | 契約している気象情報の提供元 |
| 暦 | 曜日、祝日、連休の何日目か、給料日の前後 | 本部で用意する暦の表 |
| レシピ | メニューごとの食材と1人前の使用量 | レシピ表 |
| 在庫 | 閉店時の主な生鮮食材の在庫 | 在庫入力の表 |
| 発注の条件 | 品目ごとの発注単位、納品の曜日、日持ち | 購買部の品目の表 |
予約の状況は、毎日15時に保存しておく必要があります。 予約台帳は、後から開くと最終的な予約の状態しか見えないことが多く、「前日15時に何人入っていたか」は、その時点で保存しておかないと残りません。 過去の分が残っていない場合は、予約台帳の「予約を受けた日時」の記録から、各日15時の時点の数を組み立て直します。
天気予報も、その日に取得した予報を保存します。 学習に実際の天気を使うと、予測の時点では分からない情報で学ぶことになります。発注の時点で店長が見ていたのと同じ「予報」で学習させます。 天気予報をどの提供元から取るかは、利用する気象情報のサービスに応じた個別の実装になります。
在庫は、いま行っている閉店時の棚卸しを使います。 主な生鮮食材の在庫を、品目と数量の表に入れる形にそろえるのが準備作業です。
データの取得方法を決める
| 取るもの | どこから | 取り方 |
|---|---|---|
| 来店客数・出数 | POS | 毎日の閉店後にCSVで書き出し、read_csv で読む |
| 予約の状況 | 予約台帳 | 毎日15時にCSVで書き出して保存する |
| 天気予報 | 気象情報の提供元 | 毎日15時に取得し、取得した日時と一緒に保存する |
| レシピ・発注の条件 | Excel の表 | 変更があったときに書き出す |
| 在庫 | 在庫入力の表 | 毎朝、前日の閉店時の分を読む |
POSの書き出しは、文字コードと日付の列に気をつけます。 read_csv の encoding で文字コードを、parse_dates で日付の列を、dtype で店舗コードを文字列として読むよう指定します。店舗コードを数値として読むと、先頭の0が落ちて別の店舗になります。
取り込んだデータは、毎日の写しを残します。 予約台帳もPOSも、後から修正が入ることがあります。予測に使ったその日のデータを残しておかないと、予測が外れた理由を後から確かめられません。
AIへ渡す前に整形する
- 日付をそろえる … 深夜0時をまたぐ営業の売上は、営業日にそろえます。 POSの日付が暦日のままだと、土曜の深夜の客が日曜に入ります
- 臨時休業と貸切を印にする … 臨時休業の日と、貸切で通常の営業をしなかった日は、学習から外すか印を付けます
- 予約の特徴を作る … 15時時点の予約の人数、組数、コースの予約数、過去4週の同じ曜日の「15時時点の予約」に対する「最終的な客数」の比を作ります
- 暦の特徴を作る … 曜日、祝日、連休の何日目か、給料日の前後を列にします
- メニューの出数の割合を出す … 店舗ごとに、過去4週の同じ曜日について、客1人あたりの各メニューの出数を出します
- メニューの入れ替えに備える … 新しいメニューは、出数の割合が過去に無いため、似たメニューの割合を仮に置き、2週間たったら実績に切り替えます
- 欠けた値はそのままにする … 天気予報を取り損ねた日は、値を埋めずに欠けたまま渡します。HistGradientBoostingRegressor は欠けた値をそのまま扱えます
3番目の「比」が、店長の頭の中にあった知識です。 第3章の(b)で、予約30人を多いと読むか少ないと読むかは、当日の予約と飛び込みがどのくらい上乗せされるかで決まると書きました。この上乗せの具合を、曜日と店舗ごとの数字として持たせます。
7番目で値を埋めないのは、埋めた値が実際の予報と違うからです。 前日の予報で埋めると、予報が変わった日の予測を、変わる前の予報で行うことになります。
AIに処理させる
させるのは、翌日の来店客数を、店舗ごとに2つの見込みで予測することです。
| 見込み | 設定 | 使う品目 |
|---|---|---|
| 中央の見込み | loss='quantile'、quantile=0.5 | 日持ちが1日の品目(葉物、鮮魚の刺身用など) |
| 多めの見込み | loss='quantile'、quantile=0.8 | 日持ちが2日以上で、欠品するとメニューが出せない品目(精肉、主菜の食材など) |
品目で見込みを使い分けるのは、外れたときの損が品目で違うからです。 刺身用の鮮魚は、余れば翌日には使えず全量が廃棄になります。多めに見込むほど廃棄が増えるので、中央の見込みで発注します。 一方、看板メニューのハンバーグのひき肉は、翌日の仕込みにも回せますが、足りなければ看板メニューが売り切れになります。こちらは多めの見込みで発注します。 どちらの見込みを使うかは、購買部の品目の表に持たせます。
予測したあとの計算は、規則で行います。
- 食材ごとの必要な量 = 予測した客数 × 客1人あたりの各メニューの出数 × そのメニューの1人前の使用量(メニューについて合計)
- 発注までの期間の分を足す … 翌日の納品が無い曜日がある品目は、次の納品日までの日数分を足します
- 在庫を引く … 閉店時の在庫を引きます
- 発注単位に切り上げる … ケースや束の単位に切り上げます。0以下なら発注しません
| させないこと | 理由 |
|---|---|
| 発注の確定 | 団体の予約やイベントを知っているのは店長 |
| 在庫の推測 | 在庫は数えた値を使う。入力が無ければ案を出さない |
| 品目ごとの見込みの選び方 | 購買部が品目の表で決める |
| 特別な日の予測 | 年末年始や店舗の周年のように過去の例が少ない日は、店長が決める |
4行目は、予測の限界をはっきりさせるためです。 大みそかや店舗の周年の催しのように、1年に1回しかない日は、学習に使える例がほとんどありません。 そういう日は、案に「過去の例が少ない日」と印を付け、店長の見込みを優先します。
指示内容を固定する
この構成には生成AIへの指示文がありません。 そのかわりに、予測と計算の手順をプログラムとして決めます。中心になる部分は次のとおりです。
import pandas as pd
from sklearn.ensemble import HistGradientBoostingRegressor
from sklearn.model_selection import TimeSeriesSplit
df = pd.read_csv("daily_features.csv", encoding="utf-8",
parse_dates=["business_date"], dtype={"store_code": "string"})
df["store_code"] = df["store_code"].astype("category")
df["weekday"] = df["weekday"].astype("category")
features = ["store_code", "weekday", "is_holiday", "holiday_seq", "payday_near",
"resv_people_15h", "resv_groups_15h", "resv_course_15h",
"walkin_ratio_4w", "forecast_rain_prob", "forecast_max_temp"]
train = df[~df["closed_or_private"]].sort_values("business_date")
models = {}
for q in (0.5, 0.8):
m = HistGradientBoostingRegressor(loss="quantile", quantile=q,
categorical_features="from_dtype")
models[q] = m.fit(train[features], train["guests"])
# 検証:未来のデータで過去を評価しないよう、時間の順に分ける
tscv = TimeSeriesSplit(n_splits=5, gap=1)
categorical_features="from_dtype" は、カテゴリ型の列を自動でカテゴリとして扱う既定の指定です。 店舗と曜日の列をカテゴリ型にしておけば、店舗ごと・曜日ごとの違いを数値の大小としてではなく、別々のまとまりとして学びます。
検証には TimeSeriesSplit を使います。 scikit-learn のドキュメントでは、時間の順に並んだデータに通常の交差検証を使うと、未来のデータで学習して過去のデータで評価することになるとされています。TimeSeriesSplit は、前のほうを学習、その次を評価に使い、学習に使うデータは回を追うごとに前の回を含む形で増えていきます。 gap で、学習と評価の間を空けることもできます。
評価で見るのは、当たり外れの平均だけではありません。 過去の同じ期間について、「この案どおりに発注していたら、廃棄と欠品はどうなっていたか」を、実際の店長の発注と並べて計算します。平均の誤差が小さくても、週末に少なめに外れ続けるモデルは、欠品を増やします。
出力形式を固定する
店長が見る表は、店舗ごとに次の形にします。
上段:翌日の見込み
| 項目 | 値 |
|---|---|
| 翌日 | 10月3日(土) |
| 見込み客数 | 中央 148人/多め 166人 |
| 根拠 | 15時時点の予約 52人(14組)、過去4週の土曜の上乗せ 2.8倍、晴れ・降水確率10% |
| 印 | なし(過去の例が少ない日のときは「要判断」) |
下段:発注量の案
| 品目 | 使う見込み | 必要な量 | 在庫 | 発注単位 | 案 | 店長の修正 | 理由 |
|---|---|---|---|---|---|---|---|
| リーフレタス | 中央 | 4.1kg | 1.0kg | 2kg箱 | 2箱 | ||
| 牛豚ひき肉 | 多め | 18.3kg | 6.5kg | 5kg袋 | 3袋 | ||
| 真鯛(刺身用) | 中央 | 3.6kg | 0kg | 1尾(約1.2kg) | 3尾 |
プログラムの内部では、同じ内容を次のJSONで持ちます。
{
"store_code": "007",
"business_date": "2026-10-03",
"guests": { "p50": 148, "p80": 166 },
"basis": { "resv_people_15h": 52, "resv_groups_15h": 14,
"walkin_ratio_4w": 2.8, "forecast": "晴れ 10%" },
"flag": "none | rare_day | missing_stock",
"orders": [
{ "item_code": "V-012", "quantile_used": "p50", "need_kg": 4.1,
"stock_kg": 1.0, "unit": "2kg箱", "proposal": 2,
"manager_qty": null, "reason": "" }
]
}
根拠の欄を必ず出すのは、店長が案を直すかどうかを判断するためです。 「148人」とだけ出ても、店長には直すかどうか決められません。「15時時点で52人、土曜の上乗せ2.8倍」と出れば、「明日は団体が抜けたから少ない」と自分の知識と比べられます。
manager_qty と reason は、店長が直した値と理由です。 翌日の実績と並べて残し、店長の修正が当たったか、案のままのほうが近かったかを後から比べられるようにします。
システムへ連携する
| つなぎ先 | 方式 | 内容 |
|---|---|---|
| POS | CSVの書き出し | 来店客数とメニューの出数を毎日取り込む |
| 予約台帳 | CSVの書き出し | 毎日15時に予約の状況を保存する |
| 気象情報の提供元 | 利用するサービスに応じた取得 | 翌日の予報を毎日15時に保存する |
| 在庫入力の表 | 表の読み取り | 前日の閉店時の在庫を読む |
| 案の表 | 店舗ごとのシートへの書き込み | 見込みと発注量の案を出す |
| 受発注システム | 店長が入力 | 確認・修正した発注量を入力する |
受発注システムには、自動で書き込みません。 案を確定するのは店長で、入力も店長が行います。自動で発注まで進めると、在庫の入力漏れや特別な日の読み違いが、そのまま翌日の納品になります。
人が確認する
店長は、毎日15時すぎに案の表を開き、次の順に見ます。
- 上段の根拠を読む … 予約の数と上乗せの比が、自分の知っている翌日の様子と合っているかを見ます
- 印を見る … 「要判断」の日は、案を参考にとどめて自分の見込みで決めます。「在庫の入力なし」の品目は、在庫を数えてから決めます
- 知っていることで直す … 団体の予約の見込み、近くのイベント、仕入先から聞いた入荷の事情などで直し、理由を一言で書きます
- 受発注システムに入力する
購買部は、毎週、店長の修正と実績を見ます。
- 店長が直した品目と、その結果が実績に近づいたかどうか
- 案のまま発注して、廃棄や欠品が出た品目
- 同じ理由の修正が続いている店舗
目標は、300件をならして1件6分です。 案を読んで直さずに入力する日は数分で終わり、要判断の日や在庫の入力が抜けた日は、従来に近い時間がかかります。
例外に対処する
| 起きること | 対応 |
|---|---|
| 在庫の入力が無い | その品目は案を出さず「在庫の入力なし」と出す。在庫を推測しない |
| POSの書き出しが届かない | 前日までのデータで予測し、「実績の取り込み遅れ」と印を付ける |
| 天気予報を取得できない | 欠けた値のまま予測する。根拠の欄に「予報なし」と出す |
| 予約台帳の15時の保存が失敗した | 予約を受けた日時の記録から15時時点の数を組み立て直す。できなければ「要判断」 |
| 新しい店舗で過去の実績が少ない | 業態と立地の近い店舗の実績で仮に予測し、「要判断」を付ける。3か月たったら自店の実績に切り替える |
| 年末年始や周年など過去の例が少ない日 | 「要判断」を付け、店長の見込みを優先する |
| 台風などで臨時休業した | その日を学習から外す印を付ける |
| 新しいメニューを始めた | 似たメニューの出数の割合を仮に置き、2週間後に実績へ切り替える |
| プログラムが15時に動かなかった | 店長に「今日は案がありません」と知らせ、従来どおり発注してもらう |
上の1行目を守ってください。 在庫は予測で埋めてよいものではありません。在庫の入力が無いまま案を出すと、冷蔵庫にあるものをもう一度発注することになります。
最後の行は、仕組みが止まっても発注は止めないためです。 案が無い日は従来どおりに店長が決めます。案は、あれば店長の時間が減るものであって、無ければ発注できないものにはしません。
記録を残す
- その日の取り込みデータの写し(POS、15時時点の予約、天気予報、在庫)
- 予測に使ったモデルの版と学習の期間
- 見込み客数(中央・多め)と、発注量の案の全品目
- 店長が直した値と理由
- 翌日の実際の客数と出数、予測との差
- 購買部が月に一度集計する、店舗ごとの廃棄と欠品の件数
モデルの版を残すのは、学習し直したときに比べるためです。 月に一度、直近までのデータで学習し直します。新しい版で予測がよくなったかを、同じ期間の差で確かめてから切り替えます。
04実装レベルの3段階
本記事の想定は本格構成です。 第10章の6分は、発注量の案まで出たものを店長が確かめて入力する前提で置いた数字です。 半自動化で減るのは、第4章の①の7分です。 見込み客数は出ますが、食材ごとの量に直す計算は店長が行うので、③は残ります。本格構成で③が無くなり、②も在庫を数えるだけになります。 本格構成に進む前に、レシピ表を確かめてください。 1人前の使用量が実態と合っていないと、客数の予測が当たっても発注量は外れます。レシピ表の使用量と、実際の仕入量と出数から逆算した使用量を、主な10品目で比べます。
05工数削減シミュレーション
導入後 300件 × 6分 ÷ 60 = 30 時間/月
自社条件で導入効果を整理したい方へ
このユースケースを自社に当てはめた場合の前提値と削減見込みを、業務ヒアリングをもとに整理します。
06向いている企業・向いていない企業
- 予約を受ける業態の飲食店を複数店舗持ち、野菜・鮮魚・精肉などの日持ちしない食材を店長が毎日発注している場合。POSの来店客数とメニューの出数、予約台帳のデータが1年分以上たまっている場合。発注量が店長の経験に頼っていて、店長が替わると廃棄や欠品が増える場合。ホテルのレストランや朝食会場のように、予約や宿泊の人数から食数を見込む業務にも当てはまります。
- 1〜2店舗で、店長が毎日の発注を短い時間で終えられている場合。来店客数やメニューの出数の記録が残っていない、または半年分に満たない場合。食材の大半が日持ちする仕入品で、週単位の発注で足りる場合(発注点を決める構成のほうが合います)。発注の最終判断を店長から外したい場合(この構成は案を出すまでです)。
07最小構成で試す方法
- 1店舗を選び、過去1年分の日別の来店客数を POS から書き出す
- 同じ期間について、予約台帳の「予約を受けた日時」から、各日の前日15時の予約の人数を組み立てる
- 表計算で、曜日ごとに「前日15時の予約の人数」と「実際の客数」を並べ、上乗せの比を出す
- 過去4週の同じ曜日の上乗せの比 × 前日15時の予約で、翌日の客数を見込む
- その見込みを、過去3か月について実際の客数と比べる
これは予測モデルではなく、上乗せの比だけを使った簡単な見込みです。 それでも、予約の数を「その時点で見えていた数」で扱うと、店長の見込みにどこまで近づくかが分かります。
| 出てきた内容 | 判断 |
|---|---|
| 簡単な見込みでも店長の見込みに近い | 予測モデルを作る価値がある。 天気と暦を足して差を詰める |
| 前日15時の予約の数が組み立てられない | 予約台帳の記録の見直しが先。 毎日15時の保存を今日から始める |
| 曜日によって上乗せの比がばらつく | 暦の特徴(祝日、給料日)を足す必要がある |
| 店長の見込みより大きく外れる日が続く | 店長が何を見ているかを聞き取り、入力に足す |
2行目が出たら、その日から保存を始めてください。 予約台帳の過去の状態は、後から取り戻せないことがあります。3か月保存すれば、試しを始められます。
08実装時につまずきやすいポイント
| 問題 | 対策 |
|---|---|
| 最終的な予約数で学習して、実際の発注で外れる | 15時時点の予約の数で学習する。 毎日15時に保存する |
| 実際の天気で学習して、予報の日に外れる | その日に取得した予報で学習する |
| 通常の交差検証で評価して、実運用より当たって見える | TimeSeriesSplit で時間の順に分けて評価する |
| 平均の誤差は小さいのに欠品が増える | 案どおりに発注した場合の廃棄と欠品で評価する |
| 店舗コードの先頭の0が落ちる | read_csv の dtype で文字列として読む |
| 深夜の営業が翌日に入る | 営業日にそろえる |
| 在庫の入力漏れで重複して発注する | 在庫が無い品目は案を出さない |
| レシピの使用量が実態と違い、発注量が外れる | 仕入量と出数から逆算した使用量と比べて直す |
| 年末年始や周年の日に大きく外れる | 過去の例が少ない日に印を付け、店長の見込みを優先する |
| 店長が案を見ずに入力する、または全部直す | 修正と理由を記録し、どちらが実績に近かったかを週に一度返す |
上の3行が、この構成の失敗のほとんどです。 どれも、予測の時点では分からない情報で学習や評価をしてしまうところから起きます。手元の検証ではよく当たるのに、実際に使うと外れる、という形で現れます。「発注を決める15時に、何が見えていたか」だけで学習と評価を組めるかで、使えるかどうかが決まります。
最後の行は、仕組みを店長に使ってもらうための対策です。 案を信じすぎても、まったく信じなくても、この構成の意味がなくなります。店長の修正が当たったのか外れたのかを数字で返すと、案とのつきあい方が店長ごとに定まってきます。
09セキュリティ・AIガバナンス上の注意点
この構成で扱うデータ: 店舗ごとの来店客数とメニューの出数、予約の組数と人数、在庫と発注量です。予約台帳には予約者の氏名と電話番号が入っていますが、この構成では使いません。
- 予約台帳から個人の情報を持ち出さない … 予測に使うのは、日ごとの予約の組数・人数・コースの予約数だけです。書き出しの段階で氏名と電話番号の列を落とし、集計した数だけを保存します
- 外部のAIサービスにデータを渡さない … 予測は自社の環境で Python が行います。売上や客数を外部のサービスに送る経路はありません
- 発注を自動で確定しない … 案を確定するのは店長です。自動発注にすると、在庫の入力漏れや特別な日の読み違いがそのまま納品になります
- 予測の限界を案の表に書く … 過去の例が少ない日には「要判断」を付けます。案がいつも正しいかのように見せないことが、店長の判断を残すために必要です
- 店長の評価に使わない … 店長の修正と実績の差は、モデルを直す材料です。これで店長を評価すると、店長は修正をしなくなり、データに入っていない知識が失われます
誤りが起きた場合のリスクは、発注量の過多による廃棄と、過少による欠品の2つです。 いずれも食材の損と売り逃しで、健康や安全に直結するものではありません。ただし、欠品のときに代わりの食材を使う場合は、アレルゲンの情報が変わることがあるため、代替品の扱いは品質管理の手順に従ってください。
10まず何から始めるか
1週目:予約台帳の保存を始める
今日から、毎日15時に予約台帳の状態をCSVで保存します。あわせて、予約台帳の「予約を受けた日時」の記録から、過去の15時時点の数を組み立てられるかを確かめます。
2週目:1店舗で簡単な見込みを試す
第8章の手順で、1店舗の過去1年分について上乗せの比による見込みを作り、店長の実際の発注の元になった見込みと比べます。
3週目:品目の表を作る
購買部が、生鮮食材約40品目について、発注単位、納品の曜日、日持ち、中央と多めのどちらの見込みを使うかを表にします。あわせて、主な10品目でレシピの使用量を実態と比べます。
4週目:予測モデルを学習させる
10店舗の過去のデータで予測モデルを学習させ、TimeSeriesSplit で評価します。案どおりに発注していた場合の廃棄と欠品を、実際の店長の発注と並べます。
2か月目: 3店舗で、案の表を店長に見てもらいながら従来どおりに発注してもらい、案と店長の発注の差と、翌日の実績を記録します。 3か月目以降: 10店舗に広げ、1件20分が何分になったかを店長への聞き取りで確かめます。店長の修正の理由の一覧をもとに、最初の入力の追加(イベントの予定表など)を終えた時点で、この構成は完成です。
11関連ユースケース
12この仕組みを理解するための記事
13技術仕様の確認日・参考情報
| 確認した内容 | 情報源 | 確認日 |
|---|---|---|
HistGradientBoostingRegressor がヒストグラムにもとづく勾配ブースティングの回帰木で、1万件以上のデータでは GradientBoostingRegressor よりはるかに速いこと。loss='quantile' と quantile で分位点を予測できること。欠けた値(NaN)をそのまま扱い、分岐ごとに欠けた値の行をどちらへ送るかを学ぶこと。categorical_features の既定値が "from_dtype" であること | scikit-learn: HistGradientBoostingRegressor | 2026-09-29 |
時間の順に並んだデータに通常の交差検証を使うと、未来のデータで学習して過去のデータで評価することになること。TimeSeriesSplit では学習に使うデータが回を追うごとに前の回を含む形で増えること。gap で学習と評価の間を空けられること | scikit-learn: TimeSeriesSplit | 2026-09-29 |
read_csv の encoding、parse_dates、dtype、usecols で、文字コード・日付の列・列の型・読む列を指定できること | pandas: read_csv | 2026-09-29 |
天気予報の取得方法は、利用する気象情報のサービスによって異なります。 本記事では特定のサービスの仕様は扱っていません。
実装ステータス:構成例。 公開仕様に基づいて設計した構成であり、当社で実際に構築・検証したものではありません。工数の数値はモデル条件による試算です。
自社の業務に使えるAI活用候補を整理します
このユースケース(UC-0309)についてのご相談はこちらから。
