飲食店の30分ごとの来店客数を予約・曜日・天気・近隣の催しから見込み、翌週のシフトに要るホールと厨房の人数の案を店長へ出す
予約台帳・POSレジの来店記録・気象庁の週間天気予報・近隣の催しの予定から、翌週の店ごと・30分ごとの来店客数を見込み、ホールと厨房に要る人数の案にして店長へ渡します。時間帯ごとの人数の決め方が、店長の勘から数字になります。
- 連携・自動化
- Python
- 対象業界
- 宿泊/小売/飲食
- 対象部門
- 人事
- 対象業務
- 集計・分析
- 主な課題
- 人手が足りない/判断に時間がかかる/属人化している
- AIで行う処理
- 予測
- 主な効果
- 判断支援/属人化解消/工数削減
- 導入難易度
- ★★★☆☆
- 実装レベル
- 本格構成
- 費用感
- 個別開発(大)
- 人間の確認
- 条件付き
01導入前 / 導入後の業務フロー
- 毎週水曜、各店の店長が予約台帳で翌週の日ごとの予約を開く
- POSレジの管理画面で、前年と先月の同じ曜日の時間帯別の客数を見る
- 天気予報のサイトで翌週の天気を見て、雨の日の増減を思い出しながら客数を見込む
- 近くの催しの予定を、掲示や取引先からの話で確かめて上乗せする
- 時間帯ごとに、ホールと厨房の人数を表計算の表に書き込む
- 22時以降の時間帯に18歳以上の人数がそろうかを、名簿と照らして確かめる
- 人数の表を人事部へ送り、勤務の希望と合わせてシフトを組む
- 自動毎日、気象庁の府県週間天気予報の電文を取得して保存する
- 自動毎週水曜の朝、POSレジの会計の記録と予約台帳を書き出す
- 自動Python が、店・日・30分ごとの入店客数と、時点ごとの予約済みの人数を数える
- 自動学ばせたモデルで、翌週の30分ごとの入店客数を真ん中と多めの2本で見込む
- 自動時間帯ごとの滞在の時間を重ねて、在席の客数に直す
- 自動在席の客数からホールの人数を、入店の客数から厨房の人数を、店ごとの換算の表で出す
- 自動催しの日、見込みの幅が広い時間帯、先週から大きく変わった日、22時以降の枠に印を付ける
- 人店長が印の付いた日と時間帯だけを確かめ、人数を直すか決める
- 自動確かめた人数の表を、シフト作成の仕組みに取り込める形で書き出す
- 人店長が勤務の希望と合わせてシフトを組み、金曜に確定する
- 自動翌週、実際の入店客数と見込みの差を記録する
各工程の詳しい説明を読む
- 毎週水曜、各店の店長が予約台帳で翌週の日ごとの予約を開く
- POSレジの管理画面で、前年と先月の同じ曜日の時間帯別の客数を見る
- 天気予報のサイトで翌週の天気を見て、雨の日の増減を思い出しながら客数を見込む
- 近くの催しの予定を、掲示や取引先からの話で確かめて上乗せする
- 時間帯ごとに、ホールと厨房の人数を表計算の表に書き込む
- 22時以降の時間帯に18歳以上の人数がそろうかを、名簿と照らして確かめる
- 人数の表を人事部へ送り、勤務の希望と合わせてシフトを組む
(a)見込み方が店長ごとに違う。 2番目で前年を見る店長もいれば、先月だけを見る店長もいます。経験の長い店長の店は当たり、異動してきたばかりの店長の店は外れます。 人事部から見ると、店ごとに人数の出し方がばらばらです。
(b)時間帯への割り振りが粗い。 1日の客数の見込みは立っても、それを30分ごとに割り振る段で「昼は11時半から13時半が山」と大づかみになります。山の前後の30分に人が足りず、片付けが追いつかない時間帯ができます。
(c)天気の効き方を覚えていられない。 同じ店でも昼と夜で効き方が違います。店長が覚えているのは、印象に残った何日かです。
(d)余った時間帯は記録に残らない。 足りなかった日は覚えていますが、早上がりをお願いした日は残りません。見込みは多めの側にずれ、人件費の枠を超える週が出ます。
- 【自動】 毎日、気象庁の府県週間天気予報の電文を取得して保存する
- 【自動】 毎週水曜の朝、POSレジの会計の記録と予約台帳を書き出す
- 【自動】 Python が、店・日・30分ごとの入店客数と、時点ごとの予約済みの人数を数える
- 【自動】 学ばせたモデルで、翌週の30分ごとの入店客数を真ん中と多めの2本で見込む
- 【自動】 時間帯ごとの滞在の時間を重ねて、在席の客数に直す
- 【自動】 在席の客数からホールの人数を、入店の客数から厨房の人数を、店ごとの換算の表で出す
- 【自動】 催しの日、見込みの幅が広い時間帯、先週から大きく変わった日、22時以降の枠に印を付ける
- 【人】 店長が印の付いた日と時間帯だけを確かめ、人数を直すか決める
- 【自動】 確かめた人数の表を、シフト作成の仕組みに取り込める形で書き出す
- 【人】 店長が勤務の希望と合わせてシフトを組み、金曜に確定する
- 【自動】 翌週、実際の入店客数と見込みの差を記録する
8番目が、この設計の分かれ目です。店長が見るのは7日分の全部の時間帯ではありません。 印の無い日はそのまま通し、印の付いた日と時間帯だけを、催しや店の事情と照らして直します。 全部を見直す設計にすると、60.0時間はほとんど減りません。
1番目を毎日動かすのは、学ばせる材料を作るためです。 シフトを決める時点で分かるのは天気予報で、実際の天気ではありません。当時の予報を残さないと、本番と同じ条件で学ばせられません。
02今回想定するシステム構成
POSレジ(会計の記録)/予約台帳(予約と取消の記録) │ 入店時刻・人数・会計時刻/予約日時・人数・予約を受けた日・取消日 ▼ 気象庁防災情報XML(府県週間天気予報)── 毎日取得して保存 ▼【トリガー】毎週水曜 6:00 Python(pandas) ├──▶ 店・日・30分ごとの入店客数と、時点ごとの予約済みの人数 ├──▶ 催しの予定表、祝日、店ごとの換算の表 ▼ scikit-learn HistGradientBoostingRegressor │ loss="poisson"(真ん中)と loss="quantile", quantile=0.8(多め) ▼ 在席の客数への換算 ── 時間帯ごとの滞在の時間 ▼ 人数への換算 ── ホール(在席)/厨房(入店)、最低人数 ▼ 印付け ── 催し/幅の広い時間帯/先週から大きく変わった日/22時以降 ▼ 【店長が印の付いたところだけ確かめる】 ▼ シフト作成の仕組みへ時間帯ごとの必要人数の表
| 役割 | 想定する製品 | 代替候補 |
|---|---|---|
| 実行環境 | Python(pandas・scikit-learn) | R |
| 処理エンジン | scikit-learn の HistGradientBoostingRegressor(ポアソン損失と分位点の損失) | LightGBM、statsmodels の一般化線形モデル |
| 集計 | Python(30分ごとの入店と在席の客数、人数への換算) | POSレジの帳票 |
| 天気予報の取得元 | 気象庁防災情報XML(府県週間天気予報) | 気象業務支援センター、民間の気象会社の配信 |
| 結果の出力先 | シフト作成の仕組みに取り込む必要人数の表 | 店長への共有フォルダ |
POSレジにも予約台帳にも書き込みません。 記録を読むだけで、予約の受け付けや席の割り当てには触れません。 シフト作成の仕組みにも、取り込める形の表を渡すだけで、誰をどの時間帯に入れるかは店長が決めます。
見込みの中心は、scikit-learn の HistGradientBoostingRegressor です。 損失には squared_error・absolute_error・gamma・poisson・quantile があり、poisson は対数のリンクを使い、目的変数が0以上であることを求めます。 30分ごとの入店客数は0以上の数なので、真ん中の見込みにはこれを使います。多めの側は loss="quantile" と quantile=0.8 で別に作ります。分位点の損失はピンボール損失です。
このモデルを選ぶ理由は3つあります。 1つ目は、欠けている値をそのまま扱えることです。分かれ目ごとに、欠けた値を左右どちらへ送ると良くなるかを学びます。週間天気予報が届かない先の日は、降水確率の列を空のまま渡せます。2つ目は、categorical_features で店や時間帯を区分の列として扱えることです。3つ目は、monotonic_cst で「予約済みの人数が多いほど、入店も多い」という向きを決められることです。
天気は、気象庁防災情報XMLから取ります。 気象庁はXML形式の電文をAtom形式のフィードで公開しており、定時に発表される情報の高頻度のフィードは毎分更新で直近10分以上、長期のフィードは毎時更新で数日分の電文を載せています。定時のフィードには「府県週間天気予報」が含まれます。 公開の案内では、迅速・確実な入手が要る場合は気象業務支援センターなどを使うよう示され、1日10GB以上のダウンロードが確認されると、アクセス元のIPアドレスを遮断するとされています。取得済みの電文を取り直さない作りにします。
生成AIは使いません。 見込みも人数も数字で出し、印の理由も決まった言葉で付けます。人数を文章で考えさせると、根拠の無い上乗せが混ざります。
03どうやって実装するのか
処理の起点を決める
本体は毎週水曜の朝6時に動かします。 シフトの確定が金曜なので、店長が水曜と木曜のうちに印の付いたところを確かめ、勤務の希望と合わせる時間を残せる時刻にします。前日の閉店後の締めの処理が終わり、POSレジの記録が書き出せる時刻を選びます。
天気の電文の取得は、それとは別に毎日動かします。 長期のフィードを1日数回読み、自店の地域の府県週間天気予報の電文が新しく載っていれば取って保存します。フィードに載る電文は数日で消えるので、取りこぼすと、その日の予報は二度と手に入りません。
見込む先は、翌週の月曜から日曜までの7日分です。 金曜の確定の後、日曜の夕方にもう一度動かし、週間天気予報が新しくなった日だけ見込みを出し直して、確定した人数との差が大きい時間帯を店長に知らせます。
入力データを集める
| データ | 中身 | 取得元 |
|---|---|---|
| 会計の記録 | 店、伝票番号、入店時刻、人数、会計時刻、席の種類(テーブル・座敷・カウンター) | POSレジの書き出し |
| 予約の記録 | 店、予約日時、人数、席の種類、予約を受けた日、取り消した日 | 予約台帳の書き出し |
| 天気予報 | 地域、発表日時、対象日、天気、降水確率、最高・最低気温 | 気象庁防災情報XML(保存したもの) |
| 催しの予定表 | 会場、日、開始と終了の時刻、規模(収容の人数) | 各店の店長がつける表 |
| 店ごとの換算の表 | 1人のホールが受け持てる在席の客数、1人の厨房がさばける入店の客数、時間帯ごとの最低人数 | 店長と人事部で決める表 |
質を決めるのは、会計の記録の入店時刻と会計時刻です。 この2つが無いと、30分ごとの入店も在席も作れません。 POSレジによっては、会計時刻しか残らず、入店時刻は注文の最初の時刻で代わりにすることがあります。どちらが残るかを最初に確かめます。
予約の記録は「予約を受けた日」が要ります。 翌週の予約は、水曜の時点ではまだ全部入っていません。過去の各日について「7日前の時点で何人の予約が入っていたか」を作れないと、 予約の入り方を特徴として使えません。
データの取得方法を決める
POSレジと予約台帳は、帳票の書き出しの機能で表の形のファイルを出し、共有フォルダに置かれたものを読みます。製品によって書き出しの方法と列の名前が違います。 APIを持つものなら定時に取りに行きます。
| 取るもの | どこから | 何に使うか |
|---|---|---|
| 過去2年分の会計 | POSレジ | 30分ごとの入店と、時間帯ごとの滞在の時間 |
| 過去2年分の予約と取消 | 予約台帳(取消も含む) | 「何日前の時点で何人」の列 |
| 翌週の予約 | 予約台帳(取消されていないもの) | 見込むときの予約済みの人数 |
| 府県週間天気予報 | 保存しておいた電文 | 降水確率と気温の列 |
| 催しの予定 | 店長の表 | 特徴の列と、印付け |
天気の電文は、地域で選びます。 府県週間天気予報は府県ごとの電文に、地域ごとの予報が入っています。店ごとに、どの電文のどの地域を使うかの対応表を持たせます。 隣の県との境に近い店でも、1つの地域に決めます。
過去の予約は、取消も含めて取ります。 取消された予約を除いて書き出すと、「7日前には入っていたが、後で取り消された」という動きが消え、 取消の多い宴会の時期を多めに見込みます。
AIへ渡す前に整形する
- 入店を30分の枠に落とす … 入店時刻を30分単位で切り捨て、店・日・枠ごとに人数を足します。来店のなかった枠は0として作ります
- 滞在の時間を出す … 会計時刻から入店時刻を引き、店・曜日の区分・枠ごとに中央値を取ります。3時間を超える伝票は、締め忘れとして外します
- ある時点の予約済みの人数を作る … 過去の各日・枠について「7日前の時点で、取り消されずに入っていた人数」を作ります
- 当時の天気予報をつなぐ … 対象日ごとに、7日前の時点で最も新しかった週間天気予報の降水確率と気温をつなぎます。予報の範囲の外の日は空のままにします
- 学ばせる組を作る … 「店」「曜日」「枠」「祝日・連休」「予約済みの人数」「降水確率」「最高気温」「催しの有無と規模」「催しの終了からの経過」を特徴に、その枠の入店客数を答えにします
- 異常な日を外す … 臨時休業、貸し切り、改装の前後、POSレジの入れ替えの前後は、学ばせる組から外します
- 検証の分け方を決める … 時系列を崩さないよう
TimeSeriesSplitで、過去で学んで後の期間で確かめる組を作ります
4番目で、実際の天気をつながないでください。 実際に雨だった日を学ばせると、モデルは天気が確実に分かっている前提で学びます。本番で渡せるのは予報だけなので、当時の予報で学ばせます。 保存を始めて間もない期間は、天気の列を空のまま学ばせ、たまるにつれて効き方を見ます。
7番目で gap を使います。 TimeSeriesSplit の gap は、学ばせる期間の終わりから確かめる期間の前に除く数です。同じ日の別の枠が両側に入ると、答えが漏れて当たりすぎます。 少なくとも7日分を空けます。
AIに処理させる
させるのは、店・日・30分ごとの入店客数を、真ん中と多めの2本で見込むことだけです。 在席への換算、人数への換算、印付けは、決まった計算で行います。
| 見込むもの | 使うもの | 判断できないときの扱い |
|---|---|---|
| 30分ごとの入店客数(真ん中) | 予約済みの人数、曜日、枠、天気予報、催し | 学ばせた範囲を外れる日は low_history |
| 30分ごとの入店客数(多め) | 同上 | 同上 |
| 多めの側との幅 | 分位点0.8と真ん中の差 | 幅が真ん中の4割を超える枠は wide |
右端の列が、店長が見るところを決めます。 low_history は、開店して日の浅い店や、過去に例の無い規模の催しの日です。モデルの見込みを信じる根拠が薄いところだけを、店長に回します。
| させないこと | 理由 |
|---|---|
| 誰をどの時間帯に入れるかの決定 | 勤務の希望と労働時間は店長と人事部が見る |
| 18歳未満のスタッフの割り当ての判断 | 年齢の名簿と照らすのは店長。モデルは年齢を知らない |
| 換算の表の書き換え | 受け持てる客数は店長と人事部が決める |
| 確定したシフトの自動の変更 | 働く人の予定に直接かかわる |
| 催しの規模の推測 | 店長の表にある値だけを使う |
4行目がいちばん起きやすい失敗です。 日曜の出し直しで客数の見込みが下がったからといって、確定したシフトを自動で減らすと、働くつもりで予定を空けていた人の勤務が前日に消えます。 出し直しの結果は、店長への知らせにとどめます。
指示内容を固定する
この構成は生成AIを使わないので、プロンプトの代わりに、モデルの設定と換算の規則を書いておきます。
【モデル】
- HistGradientBoostingRegressor(loss="poisson") 真ん中
- HistGradientBoostingRegressor(loss="quantile", quantile=0.8) 多めの側
- categorical_features = ["store", "weekday", "slot", "holiday_type"]
- monotonic_cst = {"booked_7d": 1} 予約済みの人数が多いほど、入店も多い向き
- 目的変数 = その30分の枠の入店客数(0以上)
- 天気の列は予報の範囲の外なら空のまま渡す(埋めない)
【在席への換算】
- 在席の客数(t) = Σ 入店客数(s) × [ s ≦ t < s + 滞在の時間(s) ]
- 滞在の時間は 店・曜日の区分・枠 ごとの中央値(30分単位に丸める)
- 予約の宴会は、予約台帳のコースの時間を滞在の時間とする
【人数への換算】
- ホール = 在席の客数 ÷ 1人が受け持てる在席の客数(切り上げ)
- 厨房 = 入店客数 ÷ 1人がさばける入店の客数(切り上げ)
- どちらも、時間帯ごとの最低人数を下回らない
- 真ん中の人数 = 確定のシフトの目安、多めの人数 − 真ん中 = 声をかけておく人
【印を付ける条件】
- low_history:その店・曜日・枠で学ばせた組が 30 未満、または催しの規模が過去の最大を超える
- wide:多めの入店 − 真ん中の入店 が、真ん中の 4 割を超える
- event:催しの予定表で、終了の時刻から 2 時間以内の枠
- jump:先週の同じ日の見込みから、ホールか厨房が 2 人以上変わった
- late:22時以降の枠(18歳以上で組む必要があることを店長に示す)
- 印の無い日は、店長の確認なしで表に載せる
「天気の列を埋めない」を書いておかないと、平年の値で埋める実装になりがちです。 埋めると、予報が無い日をモデルは「平年並みの天気と分かっている日」として扱います。分からないことは、分からないまま渡します。
monotonic_cst の向きは、学ばせる前に記録で確かめます。 宴会の多い時期は、予約が多い日ほど予約なしの客が入れず、入店の合計が予約ほどには増えない店もあります。 向きの制約を付けた場合と付けない場合の両方で、確かめる期間の外れ方を比べてから決めます。
late の印は、人数を変えるための印ではありません。 22時以降の枠に、18歳以上のスタッフで人数がそろうかを、店長が名簿と照らして確かめるための目印です。
出力形式を固定する
店・日ごとに、次の形のJSONを作り、表にしてシフト作成の仕組みへ渡します。
{
"store": "",
"date": "",
"weather_forecast": { "issued_at": "", "pop": null, "temp_max": null },
"slots": [
{ "slot": "11:30",
"arrivals": { "booked": 0, "p50": 0, "p80": 0 },
"seated": { "p50": 0, "p80": 0 },
"hall_staff": { "p50": 0, "p80": 0 },
"kitchen_staff": { "p50": 0, "p80": 0 },
"flags": ["low_history | wide | event | jump | late"] }
],
"model_version": "",
"manager_override": [ { "slot": "", "hall": null, "kitchen": null, "reason": "" } ]
}
1つ目の理由は、booked と p50 を並べて見せられることです。 いま入っている予約の人数と、見込みの入店の差が、そのまま「予約なしで来る人」と「これから入る予約」の分です。店長は、この差が店の感覚と合うかを見れば足ります。
2つ目は、arrivals と seated を分けて持てることです。 ホールの人数は seated から、厨房の人数は arrivals から出ています。どちらが人数を押し上げたのかが、表の上で分かります。 宴会の日は在席が長く伸び、昼の山は入店が鋭く立ちます。
3つ目は、weather_forecast で見込みの前提が残ることです。 どの発表時刻の予報を使ったかを残すので、外れた日に「予報が外れたのか、モデルが外れたのか」を後から分けられます。 予報の範囲の外の日は pop が null のまま残ります。
4つ目は、manager_override で直した記録が残ることです。 店長が人数を直したら、枠と人数と理由を書きます。シフトに渡るのは直した後の値で、モデルの値も残ります。
システムへ連携する
| つなぎ先 | 方式 | 内容 |
|---|---|---|
| POSレジ | 帳票の定時の書き出し、またはAPI | 会計の記録(入店時刻・人数・会計時刻) |
| 予約台帳 | 帳票の定時の書き出し、またはAPI | 予約と取消の記録 |
| 気象庁防災情報XML | 長期のフィードの定時の読み取り | 府県週間天気予報の電文 |
| 催しの予定表 | 表の読み取り | 会場・日・時刻・規模 |
| 店長の確認の表 | 表の書き出しと読み取り | 印の付いたところと、直した人数・理由 |
| シフト作成の仕組み | 必要人数の表の取り込み | 時間帯ごとのホールと厨房の真ん中の人数と、声をかけておく人の枠 |
シフト作成の仕組みには、人数の表を渡すだけです。 取り込みは店長が行い、取り込んだ後に誰を入れるかは、勤務の希望と労働時間、年齢を見て人が決めます。 必要人数の列を自動で書き換える経路があっても、確定したシフトに触れる経路は作りません。
モデルの学ばせ直しは、月に一度、別の処理として動かします。 毎週の見込みは、その月に決めた版のモデルで出します。学ばせ直した版は、直近の8週を確かめる期間にして前の版と外れ方を比べ、良くなっていたときだけ切り替えます。 毎週学ばせ直すと、見込みが変わった理由が記録の変化なのかモデルの変化なのか、店長にも人事部にも分からなくなります。
人が確認する
店長が見るのは、印の付いた日と時間帯だけです。 印の無いところはそのまま表に載ります。7日分の全部の枠を開く設計にすると、第10章の16.0時間には収まりません。
eventとlow_historyを先に見る … 催しの終演の後と、記録の少ないところです。会場の催しの客層が、この店に来る客層かを考えますwideを確かめる … 見込みの幅が広い時間帯です。真ん中で組み、声をかけておく人を多めに持ちますjumpを確かめる … 先週から人数が大きく変わった日です。大口の宴会の予約や取消が入っていないかを、予約台帳で見ますlateを名簿と照らす … 22時以降の枠に、18歳以上のスタッフで人数がそろうかを確かめます- 直したら理由を書く …
manager_overrideに枠と人数と理由を残します
確認の期限は木曜の15時に置きます。 それを過ぎた店は例外処理のとおり「未確認」として表に載り、人事部が店長に電話で確かめます。
1番目の催しの日は、モデルより店長の知っていることのほうが確かなことがあります。 同じ会場の催しでも、子ども向けの催しの帰りは家族連れが昼に来て、夜の公演の帰りは遅い時間に少人数で来ます。直すことはためらわず、直した理由を必ず残します。
目標は、240件をならして1件4分です。 印が付くのは一部の日という想定で、それより多い週は、催しの予定表が細かすぎるか、wide の閾値が狭すぎます。
例外に対処する
| 起きること | 対応 |
|---|---|
| POSレジの書き出しが失敗した | 前回の書き出しで出さず、その店は「見込みなし」として店長と人事部に知らせる |
| 天気の電文が取れなかった日がある | その日の天気の列は空のまま。取れなかった日を記録し、学ばせる組でも空として扱う |
| 入店時刻が残っていない伝票 | 会計時刻から店の滞在の中央値を引いて入店時刻とし、その旨の列を付ける |
| 見込みの在席の客数が席数を超えた | 席数で切り詰め、wide の印を付ける |
| 店ごとの換算の表に無い席の種類 | 人数を出さず、その日を low_history にして店長へ |
| 臨時休業や貸し切りの日 | 人数を出さず、店長が手で入れる |
| POSレジや予約台帳の入れ替え | 入れ替えの前後で列の意味が変わる。学ばせ直すまで見込みを止める |
| 店長が確認の期限までに戻さない | 印の付いた枠も p50 のまま渡し、「未確認」の列を付ける |
| 学ばせ直したモデルの外れ方が前の版より悪い | 新しい版を使わず、前の版で見込みを出す。 版の比較を記録する |
上から2行目が、最初の数か月でいちばん起きます。 電文の取得の処理が止まっていても、見込みは天気の列を空にして出てしまいます。取れなかった日の数を毎週の報告に載せ、続けて空の日があれば取得の処理を疑います。
記録を残す
- 実行ごとに、書き出したPOSと予約台帳のファイル名と書き出し日時
- 取得した天気の電文の発表日時と、店ごとに使った地域
- 学ばせたモデルの版(学ばせた期間、特徴の列、
monotonic_cstの有無) - 確かめる期間での外れ方(真ん中と多めの側を、それぞれ枠ごとに)
- 店・日ごとの出力のJSON(
booked、p50、p80、flags) - 店長が直した記録(
manager_overrideの枠・人数・理由) - 日・枠ごとの実際の入店客数と、見込みとの差
外れ方は、真ん中と多めの側を別々に見ます。 多めの側は「10日のうち8日はこれを下回る」水準なので、実際がそれを上回った枠が2割前後に収まっているかを数えます。真ん中だけを当たり外れで測ると、多めの側が当たっているかが分かりません。
直した記録は、モデルを学ばせ直す材料になります。 同じ会場の催しで毎回同じ方向に直しているなら、催しの規模の列の付け方が実態と合っていません。
04実装レベルの3段階
半自動化で、数えと換算は機械になります。 1件15分のうち①と③の大半が消えますが、天気と催しの上乗せは、まだ店長の勘です。 本格構成で、1件4分になります。 本記事の想定はこの段階です。上乗せがモデルに置き換わり、店長は印の付いたところだけを見ます。 差が大きいのは、②の考える時間が、印の付いた枠の確認に変わるからです。 段階を飛ばさないでください。 半自動化を1〜2か月回すと、換算の表と滞在の時間の取り方の誤りが先に見つかります。天気の電文の保存だけは、半自動化と同時に始めておきます。
05工数削減シミュレーション
導入後 240件 × 4分 ÷ 60 = 16 時間/月
自社条件で導入効果を整理したい方へ
このユースケースを自社に当てはめた場合の前提値と削減見込みを、業務ヒアリングをもとに整理します。
06向いている企業・向いていない企業
- 数店舗から数十店舗を運営し、パート・アルバイトのシフトを店長が週ごとに組んでいる飲食店。店長が予約台帳と過去の売上の画面を見比べて、時間帯ごとの人数を勘で決めている場合。雨の日や近くの会場の催しの日に、人が足りない時間帯と余る時間帯が毎週出ている場合。POSレジから会計ごとの入店時刻・人数・会計時刻を書き出せる場合。
- 席数が十数席で、毎日同じ人数で回せる店。開店から半年に満たず、来店の記録がたまっていない店。来店客のほとんどが予約で、予約台帳を見れば人数が決まる店(予約の数え上げだけで足りる)。なお、誰をどの時間帯に入れるかの決定、労働時間と休憩の管理、採用の判断は、この構成では代替できません。
07最小構成で試す方法
- POSレジから、1店の過去3か月分の会計の記録を書き出す
- 表計算ソフトで、曜日ごと・30分ごとの入店客数の平均を出す
- 同じ記録で、時間帯ごとの滞在の時間の中央値を出し、在席の客数に直す
- 翌週の各日について、曜日の平均を見込みとし、ホールの人数に直す
- 同じ週の店長の人数と並べ、翌週に実際の入店客数と比べる
最初に確かめるのは、在席の客数で見たときに、店長の人数と山の位置が合うかです。 入店の平均だけで見ると、昼の山は12時に立ちます。在席に直すと、山は12時半から13時に後ろへずれます。店長が12時半に一番人を入れていたなら、店長はすでに在席で考えていたということです。
| 出てきた内容 | 判断 |
|---|---|
| 曜日の平均でも店長と同じくらい当たった | Python でのモデルに進む。 予約と天気と催しを足す |
| 入店時刻が書き出せなかった | POSレジの設定を先に確かめる。 会計時刻だけでは在席が作れない |
| 雨の日と催しの日だけ大きく外れた | 想定どおり。天気の電文の保存と催しの予定表を始める |
3行目が出たら、構成は有効です。 外れる日が天気と催しの日に集まっているなら、モデルに渡す特徴が足りていないだけです。
08実装時につまずきやすいポイント
| 問題 | 対策 |
|---|---|
| 実際の天気で学ばせ、本番で外れる | 当時の予報で学ばせる。 電文を毎日保存する |
| 予報の範囲の外の日を平年値で埋める | 空のまま渡す。 モデルは欠けた値を扱える |
| 入店の山で人数を決め、片付けの時間帯が足りない | 在席に直してからホールの人数を出す |
| 同じ日の枠が学ばせる側と確かめる側に入る | TimeSeriesSplit の gap で間を空ける |
| 取消を除いて書き出し、宴会の時期を多めに見込む | 取消も含めて書き出す。 予約を受けた日と取り消した日が要る |
| 席数を超える在席が出る | 席数で切り詰める規則を置く |
| 22時以降の枠を18歳未満で埋めてしまう | late の印を付け、店長が名簿と照らす |
| 出し直しで確定したシフトを自動で減らす | 知らせるだけにする。 シフトには触れない |
| POSレジの入れ替えで列の意味が変わる | 入れ替えの前後で学ばせ直す |
| 天気の電文の取得が止まっても気づかない | 取れなかった日の数を毎週の報告に載せる |
上の2行が、この構成の失敗のほとんどです。 どちらも「シフトを決める時点で何が分かっているか」を取り違えています。本番で渡せないものを学ばせると、見込みは確かめる期間では当たって見えるのに、本番で外れます。
7行目と8行目も、同じくらい早く効いてきます。 見込みの出し直しでシフトが動く仕組みと、年齢を見ずに夜の枠を埋める仕組みは、作らないでください。
09セキュリティ・AIガバナンス上の注意点
この構成で扱うデータ: 会計の記録(入店時刻・人数・会計時刻・席の種類)、予約の記録、店長がつける催しの予定表、スタッフの勤務の予定です。予約した客の氏名・電話番号は使いません。
- 予約の記録から個人の項目を外して書き出す … 見込みに要るのは予約日時・人数・席の種類・予約を受けた日・取り消した日だけです。客の氏名や電話番号の列は、書き出しの設定で外します
- 見込みで個人の勤務を決めない … この構成が出すのは時間帯ごとの必要人数までです。誰をどの時間帯に入れるかは、勤務の希望と労働時間を見て店長が決めます
- 22時以降の枠は、年齢を確かめて組む … 満18才に満たない者を午後10時から午前5時までの間に使用してはならないと定められています。モデルは年齢を知らないので、
lateの印で店長に確かめてもらいます - 確定したシフトを自動で変えない … 出し直しの結果は知らせにとどめます。働く人の予定を、見込みで前日に動かさないでください
- 見込みを、店長やスタッフの評価に使わない … 「見込みより少ない人数で回せた」を評価に使うと、換算の表が実態から離れていきます
誤りが起きた場合のリスクは、人が足りずに客を待たせることと、人が余って早上がりをお願いすることの2つです。 前者は多めの側を持たないと起き、後者は印の付いたところを確かめないと起きます。どちらも、2本の見込みと店長の確認で守ります。
10まず何から始めるか
1週目:換算の表を作り、天気の電文の保存を始める
各店の店長と、1人のホールが受け持てる在席の客数、1人の厨房がさばける入店の客数、時間帯ごとの最低人数を決めます。同じ週に、気象庁防災情報XMLの長期のフィードから、府県週間天気予報の電文を毎日保存する処理を動かし始めます。 これが遅れるほど、天気を学ばせられる時期が遅れます。
2週目:POSレジと予約台帳の書き出しを確かめる
過去3か月分の会計の記録に入店時刻と会計時刻があるか、予約の記録に予約を受けた日と取り消した日があるかを確かめます。無ければ販売元に、書き出しの設定を問い合わせます。
3週目:最小構成で在席の山を見る
1店で、曜日・30分ごとの入店の平均と滞在の時間から在席の客数を出し、店長の人数と山の位置を比べます。催しの予定表を、会場ごとに作り始めます。
4週目:Python で30分ごとの数えと人数を出す
入店と在席を30分ごとに数え、予約済みの人数を足し、換算の表で人数を出すところまで作ります。この時点ではモデルを使わず、曜日の平均で見込みます。
2か月目: ポアソン損失と分位点の損失で真ん中と多めを見込み、TimeSeriesSplit で外れ方を測ります。印付けを足し、店長の確認の表を回し始めます。3か月目以降: 保存した天気予報の列を足し、シフト作成の仕組みへの受け渡しを足して、1件15分が何分になったかを実測します。8店すべてで、印の付いたところだけを見て人数が決まり、見込みと実際の差を毎週見られるようになった時点で、この構成は完成です。
11関連ユースケース
12この仕組みを理解するための記事
13技術仕様の確認日・参考情報
| 確認した内容 | 情報源 | 確認日 |
|---|---|---|
HistGradientBoostingRegressor の損失に squared_error・absolute_error・gamma・poisson・quantile があること。poisson は対数のリンクを使い目的変数が0以上であることを求めること。quantile はピンボール損失で、quantile の引数に0から1の分位点を指定すること。欠けた値を分かれ目ごとに左右へ送ることを学ぶこと。categorical_features で区分の列を扱えること。monotonic_cst で単調な向きを指定できること(scikit-learn 1.9.1 の文書) | scikit-learn: HistGradientBoostingRegressor | 2026-10-12 |
TimeSeriesSplit が時間の順の記録を、前の期間で学ばせ後の期間で確かめる分け方であること。n_splits・test_size・gap の指定があり、gap が学ばせる期間の終わりから確かめる期間の前に除く数であること | scikit-learn: TimeSeriesSplit | 2026-10-12 |
気象庁がXML形式の電文をAtom形式のフィードで公開していること。高頻度のフィードは毎分更新で直近10分以上、長期のフィードは毎時更新で数日分の電文を載せること。迅速・確実な入手には気象業務支援センター等を案内していること。1日10GB以上のダウンロードでアクセス元のIPアドレスを遮断すること(あわせて、定時のフィードに府県週間天気予報が載っていることを regular.xml で確認) | 気象庁防災情報XMLフォーマット: PULL型での提供 | 2026-10-12 |
| 使用者は満18才に満たない者を午後10時から午前5時までの間において使用してはならないこと(労働基準法第61条第1項) | e-Gov 法令API: 労働基準法 | 2026-10-12 |
シフトの決定、労働時間と休憩の管理、18歳未満のスタッフの勤務の扱いは、人事部と各店の判断で、必要に応じて社会保険労務士に確かめて決めてください。 本記事は製品と官公庁の公開資料で確認できた範囲だけを扱っています。
実装ステータス:構成例。 公開仕様に基づいて設計した構成であり、当社で実際に構築・検証したものではありません。工数の数値はモデル条件による試算です。
自社の業務に使えるAI活用候補を整理します
このユースケース(UC-1275)についてのご相談はこちらから。
