Uczenie maszynowe · prognozowanie i optymalizacja zapasów · handel spożywczy

Popyt za pustą półką

Gdy świeży produkt się wyprzeda, sprzedaż przestaje mówić prawdę. Szacujemy popyt kryjący się za pustą półką – wraz z przedziałem – i zamieniamy go na jutrzejsze zamówienie.

Wypróbuj na żywo

01

Przegląd

FreshRetailNet-50K to publiczny zbiór danych o sprzedaży świeżej żywności: 50 000 szeregów produktowych z 898 sklepów, ze sprzedażą godzinową i godzinowym znacznikiem dostępności produktu. W danych 19,7% godzin sprzedaży to godziny braku towaru, a w 44% par produkt–dzień towar kończy się co najmniej raz. W tych godzinach dane nie rejestrują prawie żadnej sprzedaży. Prognoza wytrenowana na zarejestrowanej sprzedaży traktuje więc pustą półkę jak spokojną godzinę i zaniża prognozy.

Szacujemy popyt ukryty przez każdy brak towaru – jako liczbę z przedziałem 80%. Model uczy się wyłącznie na godzinach, dla których znamy odpowiedź. Bierzemy dzień, w którym towaru nie zabrakło, nakładamy na niego wzorzec braków z rzeczywistego dnia z wyprzedaniem, ukrywamy te godziny i uczymy model je odtwarzać. Korzysta przy tym z pozostałej części dnia, własnej historii produktu, rabatu, kalendarza, pogody i kategorii. W 167 sklepach, których model nigdy nie widział, przedział obejmował prawdziwą wartość w 80,0% przypadków. Nasza pierwsza wersja obejmowała ją tylko w 71% przypadków w dniach, w których towar naprawdę się wyprzedał, bo takie dni są bardziej ruchliwe, niż sugerują godziny z towarem na półce. Trening na godzinach z towarem w dniach z wyprzedaniem podniósł wynik tego testu z powrotem do 79,5%. Oba testy ukrywają godziny, których sprzedaż faktycznie zaobserwowaliśmy. Popytu po wyprzedaniu nie da się nigdy zaobserwować, więc jego przedział opiera się na jednym założeniu: że zachowuje się on jak godziny, które możemy przetestować. W całym zbiorze danych nasze centralne oszacowanie dodaje 17% do zarejestrowanej sprzedaży.

Następnie prognozy probabilistyczne zamieniają odtworzony popyt na zamówienia. Przetestowaliśmy je na wydzielonym tygodniu testowym zbioru danych. Polityka uzupełniania zapasów co wieczór zamawia do poziomu kwantyla prognozy, pomniejszonego o zapas, który wciąż da się sprzedać. Zapas starzeje się według zasady „pierwsze przyszło, pierwsze wyszło”, a jego okres przydatności to założenie – 1, 2 lub 3 dni – które można ustawić. Symulowane odtworzenie tygodnia testowego pokazuje zmarnowany towar i utraconą sprzedaż w porównaniu z zamawianiem na podstawie zarejestrowanej sprzedaży. W tym odtworzeniu utracona sprzedaż kosztuje dwa razy tyle co zmarnowana jednostka, a popyt obejmuje nasze oszacowanie. Zamawianie na podstawie naszego oszacowania obniżyło wtedy łączny koszt marnotrawstwa i utraconej sprzedaży o 4,6%, 5,6% i 10,9% przy okresie przydatności 1, 2 i 3 dni. Gdyby braki towaru nie ukrywały żadnego popytu, obie polityki różniłyby się najwyżej o 1,3%. Zysk zależy też od tego, czy utracona sprzedaż kosztuje więcej niż marnotrawstwo. Jeśli utracona sprzedaż kosztuje tyle samo co zmarnowana jednostka lub mniej, a braki towaru nie ukrywały popytu, zamawianie na podstawie naszego oszacowania kosztuje o 1 do 5% więcej. Przy koszcie utraconej sprzedaży równym 1 wynik dla 3 dni maleje z -10,9% do -0,2%. Przy okresie przydatności 2 lub 3 dni obie polityki zamawiają na górnej granicy naszego zakresu prognoz (P95), więc te dwa wyniki są najmniej pewne. Sprzedaż podana jest we własnych, przeskalowanych jednostkach zbioru danych. Każda odtworzona liczba to nasze oszacowanie, nigdy obserwacja. Półka w filmie to render 3D, który odtwarza dzień jednego produktu na podstawie danych.

02

Co zbudowaliśmy

  • Odtwarzanie popytu ucenzurowanego. Modele kwantylowe LightGBM od P10 do P90, trenowane przez ukrywanie godzin, w których towar był dostępny. Są kalibrowane kontrolami split-conformal na osobnych sklepach i walidowane na 167 wydzielonych sklepach testowych. Jedna z tych kontroli wykorzystuje wyłącznie rzeczywiste dni z wyprzedaniem.
  • Prognozy probabilistyczne na wydzielony tydzień testowy, sporządzane z jednodniowym wyprzedzeniem. Trenujemy je na zarejestrowanej sprzedaży oraz na odtworzonym popycie i oceniamy za pomocą CRPS, pinball loss, WAPE i obciążenia (bias) względem prostych modeli bazowych.
  • Polityka uzupełniania do poziomu (order-up-to) śledząca wiek zapasu. Sprzedaje według zasady „pierwsze przyszło, pierwsze wyszło” przy założonym okresie przydatności od 1 do 3 dni. Kwantyl zamówienia jest dobierany na dwóch tygodniach backtestu dla każdego stosunku kosztów, a następnie zamrażany na wydzielony tydzień testowy.
  • Symulowane odtworzenie, które zaczyna się od pustej półki. Wyznacza dwa skrajne warianty wyniku: jeden z popytem obejmującym nasze oszacowanie, drugi wyłącznie z zarejestrowaną sprzedażą.
  • Interaktywne odtworzenie 2 000 szeregów, wszystkich z wydzielonych sklepów testowych. W przeglądarce można wybrać sklep i produkt, pokazać zarejestrowany lub oszacowany popyt, przesuwać koszt utraconej sprzedaży względem zmarnowanej jednostki i zmieniać okres przydatności. Polityka jest na żywo ponownie dostrajana i odtwarzana.
03

Technologie

Python, pandas, NumPy, LightGBM (regresja kwantylowa), kalibracja split-conformal · three.js (renderowanie oparte na fizyce, akumulowana głębia ostrości i miękkie cienie) · czysty JavaScript dla optymalizatora w przeglądarce

04

W liczbach

Wszystkie liczby pochodzą z naszych własnych przebiegów na FreshRetailNet-50K (Dingdong-Inc, CC BY 4.0). Sprzedaż podana jest w przeskalowanych jednostkach zbioru danych.

WartośćJak to zmierzyliśmy
19,7% godzin sprzedaży bez towaru

Godzinowe znaczniki dostępności, 06:00–22:00, dla wszystkich 50 000 szeregów w ciągu 97 dni. W 44,0% par produkt–dzień produkt skończył się co najmniej raz. W 4,0% brakowało go przez cały dzień.

80,0% pokrycia przedziału (cel 80%)

Łącznie 444 393 pary produkt–dzień ze 167 wydzielonych sklepów testowych. Na godziny z towarem nałożono rzeczywisty wzorzec braków, ukryto te godziny i sprawdzono, czy prawdziwa suma mieści się w naszym przedziale P10–P90.

79,5% pokrycia w rzeczywistych dniach z wyprzedaniem

Łącznie 222 549 wydzielonych dni, w których towar skończył się o 12:00 lub później. Ukryliśmy 3 godziny z towarem kończące się 2 godziny przed brakiem. Bez treningu na dniach z wyprzedaniem pokrycie wynosiło 71,4%, a oszacowanie było zaniżone o 39%.

40% błędu na ukrytych godzinach (WAPE dla P50)

Te same 444 393 dni. Przyjęcie wartości z tych samych godzin w niedawnych dniach z towarem daje 49% błędu. Skalowanie godzin z towarem według typowego profilu godzinowego daje 60%.

+17% popytu ukrytego przez braki towaru

Suma naszych oszacowań P50 ze wszystkich 2,1 mln par produkt–dzień z brakiem towaru w porównaniu z całkowitą zarejestrowaną sprzedażą. Suma median zaniża sumę całkowitą. Sumowanie dziennych składowych P10 i P90 daje zamiast tego 8% i 30%.

CRPS niższe o 2,4% na wydzielonym tygodniu testowym

Prognoza trenowana na odtworzonym popycie wobec tego samego modelu trenowanego na zarejestrowanej sprzedaży: 0,192 wobec 0,197 (skalowane CRPS, im niżej, tym lepiej). Ocena na 206 562 wydzielonych parach produkt–dzień, w których towaru nie zabrakło, a więc zarejestrowana sprzedaż równa się popytowi. Model bazowy oparty na kwantylach empirycznych z 28 dni uzyskuje 0,240.

42% wobec 56% zbyt niskich prognoz w dniach z wyprzedaniem

Odsetek wydzielonych dni z wyprzedaniem, w których mediana prognozy jest niższa od faktycznie zarejestrowanej sprzedaży, a ta stanowi dolną granicę popytu w tym dniu. Prognoza na odtworzonym popycie wobec prognozy na zarejestrowanej sprzedaży. Ta kontrola premiuje prognozowanie w górę, więc należy ją czytać razem z obciążeniem w dniach z towarem: +3,1% wobec -6,4%.

-4,6% / -5,6% / -10,9% kosztu w odtworzeniu przy okresie przydatności 1 / 2 / 3 dni

Koszt = (2 × utracona sprzedaż + zmarnowane jednostki) na jednostkę popytu, w wydzielonym tygodniu testowym dla wszystkich 50 000 szeregów, począwszy od pustej półki. Popyt w odtworzeniu = zarejestrowana sprzedaż plus nasze oszacowanie P50 dla godzin braku towaru. Porównujemy zamawianie na podstawie naszego oszacowania z zamawianiem na podstawie zarejestrowanej sprzedaży. Kwantyl zamówienia każdej polityki dobrano na dwóch poprzedzających tygodniach. Losowanie ukrytego popytu z naszego przedziału (200 losowań) daje zamiast tego -4,0% / -7,2% / -12,6%.

W granicach 1,3%, jeśli nic nie było ukryte

To samo odtworzenie z zarejestrowaną sprzedażą jako jedynym popytem: +1,3% / -0,3% / +0,8%.

Od -5% do +5% przy koszcie utraconej sprzedaży od 0,5 do 1 zmarnowanej jednostki

To samo odtworzenie, inne stosunki kosztów. Z naszym oszacowaniem jako popytem: od -5,5% do -0,2%. Wyłącznie z zarejestrowaną sprzedażą: od +0,8% do +5,4%, więc zamawianie na podstawie naszego oszacowania kosztuje więcej, gdy nic nie było ukryte, a utracona sprzedaż jest tania. Przy okresie przydatności 2 i 3 dni obie dostrojone polityki stoją na pułapie P95 naszego zakresu prognoz.

20,4% wobec 23,0% marnotrawstwa przy 1-dniowym okresie przydatności

Odtworzenie jak wyżej. Zamawianie na podstawie naszego oszacowania marnuje 20,4% popytu i traci 7,2%. Zamawianie na podstawie zarejestrowanej sprzedaży marnuje 23,0% i traci 6,7%. Zamawianie wczorajszej sprzedaży pomnożonej przez dostrojony współczynnik kosztuje 0,600 na jednostkę popytu, wobec 0,347 w przypadku naszej metody.

42 ms na ponowne dostrojenie i odtworzenie w przeglądarce

Mediana z 10 przebiegów w Chromium w trybie headless na naszym laptopie z M1 Pro. Każdy przebieg dostraja 37 kwantyli zamówienia × 2 polityki na 14 dniach backtestu, a następnie odtwarza 7-dniowy tydzień dla 2 000 szeregów. Na telefonach będzie wolniej.

05

Kadry

06

Wypróbuj na żywo

Interaktywny widok działający w przeglądarce. Nic się nie wczytuje, dopóki nie zostanie otwarty.

Wypróbuj na żywo Pobierz 3,3 MB

Podobny problem do rozwiązania?

Wystarczy opisać, co trzeba obliczyć, zasymulować lub rozstrzygnąć, a odpowiemy, jak byśmy do tego podeszli.

Napisz do nas AI i uczenie maszynowe