Jak zbudować prosty system rekomendacji dla sklepu online

0
8
Rate this post

Nawigacja:

Po co w ogóle system rekomendacji i kiedy ma sens

Najważniejsze cele biznesowe prostego systemu rekomendacji

System rekomendacji sklepu internetowego ma jeden nadrzędny cel: zwiększyć przychód bez drastycznego zwiększania kosztów pozyskania ruchu. Zamiast wydawać więcej na reklamy, można wycisnąć więcej z klientów, którzy już są na stronie. Nawet bardzo proste rekomendacje produktowe potrafią wygenerować dodatkowe zamówienia oraz podnieść wartość koszyka.

W praktyce najczęstsze cele to:

  • Podniesienie wartości koszyka – dokładanie do koszyka produktów uzupełniających („często kupowane razem”, akcesoria, większe opakowania).
  • Cross-sell i up-sell – sugerowanie alternatyw nieco droższych lub produktów z powiązanych kategorii, które naturalnie pasują do głównego zakupu.
  • Utrzymanie klienta na stronie – rekomendacje „podobne produkty” czy „zobacz też” zmniejszają ryzyko, że klient po obejrzeniu jednego niedopasowanego produktu po prostu zamknie kartę.
  • Lepsza ekspozycja oferty – nowe lub niszowe produkty częściej pojawiają się w rekomendacjach, nawet jeśli nie trafiły jeszcze na stronę główną czy do newslettera.

System rekomendacji dla sklepu online nie musi od razu być skomplikowaną, drogą infrastrukturą z modelami deep learning. Dla większości małych i średnich sklepów efekt „80% wartości za 20% wysiłku” daje się osiągnąć dzięki prostym mechanizmom typu „często kupowane razem” i filtrom opartym na cechach produktów.

Kiedy sklep jest już „gotowy” na rekomendacje

Nie każdy sklep musi od pierwszego dnia inwestować w personalizację oferty online. Proste kryteria pomagają ocenić, czy to już ten moment, kiedy system rekomendacji ma sens:

  • Liczba produktów – powyżej około kilkudziesięciu produktów rekomendacje „podobne” i „często kupowane razem” zaczynają mieć sens. Poniżej tej liczby klient i tak łatwo przejrzy całą ofertę.
  • Liczba zamówień – jeśli sklep generuje choć kilka–kilkanaście zamówień dziennie, zaczynają się pojawiać sensowne dane transakcyjne do rekomendacji produktów. Przy 1–2 zamówieniach tygodniowo lepszy efekt może dać ręczne układanie sekcji polecanych.
  • Struktura asortymentu – system rekomendacji w e-commerce największy sens ma tam, gdzie istnieją naturalne powiązania: elektronika (akcesoria), kosmetyki (zestawy), odzież (komplety), suplementy (cykle). W sklepie z jednym typem produktu cały system może ograniczyć się do prostych bestsellerów.
  • Ruch – jeśli na stronie jest stały, choćby umiarkowany ruch, nawet proste bloki „Zobacz także” mogą przekierowywać użytkowników do kolejnych produktów zamiast pozwalać im zamknąć kartę.

Dobrym sygnałem, że to moment na system rekomendacji, jest sytuacja, w której asortyment rośnie szybciej niż możliwość ręcznego zarządzania ekspozycją. Gdy zaczyna brakować czasu na ręczne ustawianie cross-selli przy każdym produkcie, automatyczne rekomendacje są naturalnym kolejnym krokiem.

Realne zyski z prostego systemu vs brak rekomendacji

Efekt biznesowy oczywiście zależy od branży, ruchu i obecnego UX sklepu, ale praktyka pokazuje kilka uogólnień:

  • Proste rekomendacje popularnościowe (bestsellery) – poprawiają ekspozycję najczęściej kupowanych produktów i przyspieszają decyzję zakupową; efekt na przychód bywa zauważalny głównie na stronie głównej i w listingu kategorii.
  • „Często kupowane razem” – nawet bardzo prosty mechanizm współwystępowania może podnieść wartość koszyka, bo „podpowiada” klientowi oczywiste dodatki, o których sam w danej chwili nie pomyślał.
  • Produkty podobne – zmniejszają sytuacje, gdy klient trafia na produkt nie do końca pasujący, ale brakuje mu alternatywy na wyciągnięcie ręki i rezygnuje.

Klucz w tym, aby zacząć od najtańszego rozwiązania, które można wdrożyć w kilka dni, a nie w kilka miesięcy. Zwykle rozsądnym startem jest sekcja typu „często kupowane razem” oparta na danych transakcyjnych oraz prosty blok „podobne produkty” w oparciu o kategorie i cechy.

Minimalne wymagania organizacyjne i operacyjne

Prosty system rekomendacji sklepu internetowego nie wymaga od razu dedykowanego zespołu data science. Potrzebne są jednak minimalne zasoby:

  • Osoba odpowiedzialna – ktoś, kto:
    • raz zbuduje pierwszą wersję (często programista lub techniczny marketer),
    • będzie pilnował harmonogramu aktualizacji rekomendacji,
    • będzie reagował, gdy coś się „wysypie” (np. rekomendacje przestaną się odświeżać).
  • Czas na utrzymanie – nawet przy bardzo prostym systemie trzeba:
    • odświeżać dane co jakiś czas (np. raz dziennie lub raz na tydzień),
    • kontrolować jakość rekomendacji (czy nie pojawiają się absurdy),
    • dbać o poprawność danych wejściowych (identyfikatory, kategorie).
  • Proste zasoby techniczne – skrypt (Python, PHP, inny język), który:
    • zbierze dane (np. z bazy lub CSV),
    • policzy rekomendacje,
    • wgra wyniki do bazy lub pliku cache, z którego skorzysta frontend sklepu.

Zazwyczaj na start wystarczy kilka godzin pracy programisty, który zna bazę sklepu, i prosty harmonogram (np. cron na serwerze), aby co noc aktualizować plik z rekomendacjami.

Jakie dane są potrzebne do prostego systemu rekomendacji

Podstawowe źródła danych w typowym sklepie online

System rekomendacji w e-commerce opiera się na tym, co użytkownicy robią w sklepie i jakie cechy mają produkty. Najprostszy zestaw danych obejmuje:

  • Dane transakcyjne (order lines) – każda linia zamówienia: identyfikator klienta lub zamówienia, identyfikator produktu, ilość, czas zakupu, cena.
  • Logi przeglądania – informacje o tym, kto (lub która sesja) oglądał jaki produkt i kiedy. Nawet jeśli nie ma zakupu, kliknięcie/oglądanie to cenna informacja.
  • Dane o produktach (katalog) – ID produktu, nazwa, kategoria, tagi, marka, atrybuty (rozmiar, kolor, materiał), cena, dostępność, status (aktywny/wycofany).

Im więcej z tych źródeł uda się zintegrować, tym lepszy system rekomendacji można zbudować, ale do prostego startu wystarczy tak naprawdę historia zamówień i podstawowy katalog produktów.

Absolutne minimum danych do prostych rekomendacji

Do najprostszych rekomendacji produktowych potrzebne jest minimum informacji. Bez nich trudno w ogóle mówić o sensownym systemie:

  • Identyfikator klienta lub sesji – stały ID użytkownika (zalogowanego) lub ID anonimowej sesji, który pozwala połączyć jego zachowania (oglądania, zakupy).
  • Identyfikator produktu – najlepiej stabilne ID wewnętrzne (np. numer w bazie), a nie zmienna nazwa lub SKU, które może się zmienić.
  • Czas interakcji – data/godzina zakupu lub oglądania. Umożliwia filtrowanie po ostatnich miesiącach i uniknięcie używania bardzo starych danych.

Dla prostego systemu „często kupowane razem” wystarczy historia zamówień: ID zamówienia, produkt, czas. Dla prostych rekomendacji personalizowanych (collaborative filtering) przydaje się już identyfikator użytkownika, ale da się zacząć także na poziomie sesji.

Dane „fajnie mieć”, które później mocno pomagają

Poza absolutnym minimum, duże przyspieszenie dają dodatkowe atrybuty:

  • Kategoria produktu – pozwala ograniczać rekomendacje do tej samej lub powiązanej kategorii (np. nie polecać opon klientowi kupującemu kosmetyki).
  • Tagi i cechy – kolor, rozmiar, typ, przeznaczenie; to podstawa prostego content-based filtering krok po kroku.
  • Marka – często klienci mają preferencję marek; można polecać produkty tej samej lub zbliżonej marki.
  • Cena – dzięki niej można uniknąć polecania produktu kilkukrotnie droższego niż główny zakup, jeśli celem nie jest agresywny up-sell.
  • Popularność / liczba sprzedaży – umożliwia filtrowanie z list rekomendacji produktów, które się w praktyce nie sprzedają.

Te dane zwykle już są w systemie sklepu – wystarczy po nie sięgnąć przy eksporcie. Później pozwalają tanio zbudować rekomendacje podobnych produktów na bazie cech, bez konieczności zaawansowanego machine learning.

Skąd wziąć dane w WooCommerce, Shopify, Presta i własnych rozwiązaniach

W większości popularnych silników e-commerce dane można pozyskać bez dodatkowych licencji:

  • WooCommerce – dane siedzą w tabelach WordPressa i WooCommerce (np. wp_posts dla produktów, wp_woocommerce_order_items dla linii zamówień). Często najprościej wykonać zapytanie SQL lub skorzystać z REST API i wyeksportować CSV.
  • Shopify – ma rozbudowane API: Orders API (do zamówień), Products API (do katalogu). Można wykonać eksport do CSV bezpośrednio z panelu lub przez prosty skrypt w Pythonie korzystający z API.
  • PrestaShop – podobnie jak WooCommerce, dane są w bazie MySQL; dostępne są też moduły eksportujące zamówienia i produkty do CSV.
  • Własne rozwiązania – najczęściej konieczny jest bezpośredni dostęp do bazy danych lub przygotowanie endpointu API, który zwróci historię zamówień i katalog produktów.

Na początek całkowicie wystarczy ręczny eksport CSV raz dziennie lub raz na kilka dni. Automatyzacja ETL może poczekać, dopóki system rekomendacji nie udowodni swojej wartości.

Jakość danych: typowe problemy, które psują rekomendacje

Jakość danych to jeden z głównych czynników, który różni działający system rekomendacji od takiego, który generuje śmieci. Typowe problemy to:

  • Duplikaty produktów – ten sam fizyczny produkt występujący pod różnymi ID (np. po imporcie z hurtowni) rozmywa statystyki współkupiowania.
  • Brakujące identyfikatory użytkowników – część zamówień bez powiązania z klientem (zakupy gościnne), brak spójnego ID między logami a zamówieniami.
  • Rozjechane nazwy i kategorie – ten sam typ produktu w różnych kategoriach, literówki, płynna polityka nazewnictwa utrudniają proste grupowanie.
  • Testowe zamówienia i zwroty – jeśli nie zostaną odfiltrowane, potrafią wygenerować kuriozalne rekomendacje (np. kilkadziesiąt sztuk tego samego produktu kupionych w jednym zamówieniu).

Lepiej poświęcić godzinę na proste czyszczenie danych (usunięcie testów, ujednolicenie kilku najpopularniejszych kategorii) niż później walczyć z rekomendacjami, które są z gruntu nieracjonalne.

Kobieta zamawia zegarek online przy użyciu laptopa i smartfona
Źródło: Pexels | Autor: Antoni Shkraba Studio

Najprostsze typy rekomendacji – co naprawdę da się zrobić „tanio”

Rekomendacje oparte na popularności: bestsellery globalne i w kategorii

Rekomendacje popularnościowe to najtańszy i najszybszy do wdrożenia typ rekomendacji. Zwykle wystarczy policzyć liczbę sprzedanych sztuk lub liczbę zamówień z danym produktem w zadanym okresie (np. ostatnie 30–90 dni) i posortować listę malejąco.

Przykładowe zastosowania:

  • Strona główna – lista bestsellerów globalnych (najczęściej sprzedawane produkty w całym sklepie).
  • Strona kategorii – lista bestsellerów w obrębie konkretnej kategorii, co pomaga w szybkiej orientacji w dużym asortymencie.
  • Blok „klienci wybierają najczęściej” – dodany w różnych miejscach sklepu (np. pod koszykiem, w newsletterze).

Zaletą tego podejścia jest brak potrzeby śledzenia użytkownika. Wystarczy historia zamówień. Wadą – brak personalizacji: każdy widzi to samo. Mimo to, w wielu sklepach same bestsellery generują solidny, łatwy do zmierzenia efekt.

„Często kupowane razem” na podstawie współwystępowania w koszykach

Rekomendacje oparte na współwystępowaniu produktów w jednym zamówieniu to kolejny tani sposób na sensowny cross-sell. Dla każdego produktu szuka się innych produktów, które najczęściej pojawiały się w tych samych zamówieniach.

Intuicja jest prosta: jeśli klienci z realnych zakupów wielokrotnie łączyli produkt A z produktem B, jest duża szansa, że nowy klient też będzie zainteresowany taką kombinacją. Technicznie rzecz biorąc buduje się macierz współwystępowania (co-occurrence), gdzie każda para produktów ma przypisaną liczbę wspólnych zamówień.

Prosta logika biznesowa nad „często kupowane razem”

Surowa macierz współwystępowania rzadko nadaje się do wyświetlenia 1:1. Kilka regulacji zwiększa sensowność rekomendacji i ogranicza sytuacje, w których system sugeruje bzdury.

Najważniejsze progi i filtry:

  • Minimalna liczba wspólnych zamówień – np. para produktów musi współwystąpić w co najmniej 3–5 zamówieniach, żeby trafić na listę rekomendacji. Dzięki temu odpadają przypadkowe „zlepki” z pojedynczych koszyków.
  • Okno czasowe – liczenie współwystępowania tylko z ostatnich miesięcy (np. 3–6), żeby nie opierać się na relacjach, które już nie są aktualne (zmienił się asortyment, sezon, dostępność).
  • Filtr dostępności – rekomendacje generuje się tylko dla produktów, które są aktualnie dostępne; analogicznie, na listach „często kupowane razem” nie pokazuje się produktów wycofanych lub niedostępnych.
  • Limit na produkt – np. maksymalnie 5–10 rekomendacji dla jednego produktu, żeby nie przeładować interfejsu i ograniczyć koszty przechowywania/ładowania.

Częsty problem to pary produktów, które są jednokierunkowo sensowne. Klient kupujący drukarkę może dostać tusze i papier, ale klient kupujący papier raczej nie powinien jako pierwszej rekomendacji widzieć drukarki za kilkaset złotych. Można to rozwiązać w prosty sposób:

  • liczyć udział pary: wspólne zamówienia / wszystkie zamówienia z danym produktem,
  • ustawić osobne progi dla roli „głównego” produktu (wyświetlany na stronie) i „dodanego” (z listy).

W praktyce sprowadza się to do dwóch kolumn w tabeli z rekomendacjami (np. support_xy i confidence_x_to_y) i jednego prostego filtra w SQL.

Techniczne przechowywanie i serwowanie rekomendacji współkupiowania

Gotowe listy współkupiowania trzeba gdzieś trzymać i szybko odczytywać na froncie. Nie zawsze opłaca się od razu wchodzić w osobną bazę NoSQL.

Parę prostych wariantów:

  • Mały sklep, kilkaset–kilka tysięcy produktów – plik JSON lub CSV z mapowaniem product_id → lista rekomendacji wrzucony do statycznego storage (np. katalog na serwerze WWW, S3). Front lub backend ładuje plik do pamięci przy starcie aplikacji.
  • Średni sklep, kilkanaście tysięcy produktów – tabela w relacyjnej bazie danych, np. product_reco_cooc(product_id, reco_product_id, score) z indeksem po product_id. Backend zwraca REStem gotową listę.
  • Duże obciążenie – ten sam schemat plus warstwa cache (Redis, Memcached), ale to już poziom, na którym system ma udowodniony zwrot i można spokojnie uzasadnić dodatkową infrastrukturę.

Na start często wystarcza regeneracja pliku raz na noc. Nawet przy kilku megabajtach JSON-a zwykły serwer poradzi sobie bez problemu. Optymalizacje typu kompresja, delta update czy osobne API pod rekomendacje mają sens dopiero, gdy rekomendacje realnie zaczynają być używane i widać, że ruch rośnie.

Wybór podejścia: content-based vs collaborative filtering

Różnice w praktyce, a nie w teorii

Oba podejścia są często opisywane akademicko, natomiast w sklepie online liczą się trzy rzeczy: dane, czas wdrożenia i utrzymanie.

  • Collaborative filtering – korzysta z tego, co robią użytkownicy: co oglądają, kupują, oceniają. Wymaga sensownej historii zachowań, im więcej, tym lepiej. Silnie personalizuje wyniki („ludzie podobni do Ciebie kupili…”).
  • Content-based – korzysta z cech produktów, opisów, tagów, kategorii. W uproszczeniu: szuka podobnych produktów po atrybutach. Nie potrzebuje wielu zamówień, wystarczy dobrze opisany katalog.

W praktyce dla małego/średniego sklepu wygodne jest podejście mieszane: tam, gdzie są dane behawioralne – collaborative filtering, w pozostałych miejscach – content-based lub popularność.

Kiedy pierwsze wdrożyć collaborative filtering

Collaborative filtering ma sens, gdy spełnione są przynajmniej dwa warunki:

  • jest już kilkaset–kilka tysięcy zamówień z ostatnich miesięcy,
  • istnieje stabilny sposób identyfikacji użytkowników lub sesji (cookies, loginy).

Przy mniejszej liczbie danych system personalizowany potrafi zachowywać się losowo – widzi po prostu za mało wzorców. W takiej sytuacji lepiej postawić na proste „często kupowane razem” i bestsellery, a collaborative filtering włączyć później, gdy baza urośnie.

Kiedy content-based da większy efekt

Content-based wygrywa tam, gdzie:

  • katalog jest bogaty w atrybuty (rozmiar, kolor, przeznaczenie, marka, materiał),
  • w sklepie pojawia się dużo nowych produktów (np. moda, książki, asortyment sezonowy),
  • duża część ruchu to nowi użytkownicy bez historii, którym nie ma jeszcze co personalizować.

Przykład: sklep z odzieżą wprowadza co tydzień dziesiątki nowych modeli. Collaborative filtering potrzebuje czasu, żeby zebrać statystyki. Content-based jest w stanie od razu pokazać „podobne spodnie” na bazie koloru, fasonu i przedziału cenowego.

Strategia „najpierw tanie, potem sprytne”

Dobrze sprawdza się sekwencja:

  1. Wdrożyć bestsellery + często kupowane razem.
  2. Dodać content-based podobne produkty w kluczowych kategoriach.
  3. Dopiero po kilku tygodniach/miesiącach, gdy są pierwsze liczby, wdrażać prostego collaborative filtering dla zalogowanych lub powracających użytkowników.

Taki układ pozwala szybko zobaczyć pierwsze efekty (często już same bloki cross-sell zwracają inwestycję w dzień czy dwa pracy programisty), a bardziej złożone elementy dopinać na spokojnie.

Przygotowanie danych krok po kroku (ETL bez fajerwerków)

Minimalny pipeline: od bazy sklepu do pliku z rekomendacjami

Nawet prosty system rekomendacji potrzebuje jakiegoś mini-ETL. Nie musi to być od razu Airflow czy rozbudowane narzędzie BI. Wystarczy skrypt w Pythonie, PHP lub nawet SQL + cron.

Najprostszy pipeline:

  1. Ekstrakcja (E) – pobranie danych z bazy (zamówienia, produkty) lub API i zapisanie do plików tymczasowych (CSV/JSON) lub tabel tymczasowych.
  2. Transformacja (T) – odfiltrowanie śmieci (testy, anulowane, zwroty), grupowanie, liczenie agregatów (sprzedaż, współwystępowanie), wyliczenie cech do content-based.
  3. Ładowanie (L) – zapis gotowych rekomendacji do docelowej struktury: plik, tabela w DB, cache.

Najważniejsze, by proces dało się uruchomić z jednego polecenia (ręcznie lub z crona) i żeby nie wymagał klikania w pięciu panelach. Każda ręczna ingerencja szybko stanie się wąskim gardłem.

Przykładowy prosty ETL w praktyce

Przykładowa sekwencja działań dla sklepu na MySQL z własnym backendem:

  1. Skrypt (np. Python) łączy się z bazą i pobiera:
    • linie zamówień z ostatnich 6 miesięcy (tylko statusy „zrealizowane” lub analogiczne),
    • aktywny katalog produktów (z kategorią, ceną, marką itd.).
  2. Skrypt czyści dane:
    • usuwa zamówienia testowe na podstawie prostych reguł (email, NIP, fraza w adresie),
    • wycina produkty nieaktywne, wycofane, z zerową dostępnością, jeśli nie chcemy ich pokazywać.
  3. Skrypt liczy:
    • liczbę sprzedanych sztuk / zamówień na produkt (bestsellery),
    • macierz współwystępowania (często kupowane razem),
    • wektory cech dla produktów (prosty content-based).
  4. Wyniki zapisuje do:
    • tabeli product_reco_popularity,
    • tabeli product_reco_cooc,
    • tabeli product_features_vector (lub pliku z wektorami).
  5. Backend sklepu udostępnia prosty endpoint: np. /api/reco?product_id=123&type=cooc, który czyta z tych tabel i zwraca listę produktów.

Cały proces można spokojnie zrealizować przy użyciu jednego prostego skryptu na kilkadziesiąt–kilkaset linii kodu, bez dodatkowych usług w infrastrukturze.

Harmonogram odświeżania: częstotliwość vs koszt

Odświeżanie rekomendacji raz na minutę nie ma sensu w większości sklepów. Rzadko też opłaca się robić to raz w tygodniu. Zwykle wystarcza złoty środek:

  • raz na dobę w nocy – standard na początek; jeśli skrypt liczący działa kilkanaście minut, nikomu to nie przeszkodzi,
  • raz na kilka godzin – jeśli sklep ma duży ruch i częste zmiany asortymentu/cen, a rekomendacje mają reagować szybciej.

Można przyjąć prostą zasadę: jeśli rekomendacje mają istotny wpływ na przychód, stopniowo zwiększać częstotliwość odświeżania i dopiero wtedy inwestować w wydajniejsze mechanizmy (inkrementalne przeliczenia, osobne serwery itp.).

Laptop na niebieskiej sofie otoczony torbami i pudełkami zakupowymi
Źródło: Pexels | Autor: Cup of Couple

Prosty system „często kupowane razem” (co-occurrence) od A do Z

Liczenie współwystępowania krok po kroku na SQL

Najprostsza wersja bez dodatkowych narzędzi może się oprzeć w całości na SQL. Przy założeniu, że mamy tabelę linii zamówień w formacie:

  • order_lines(order_id, product_id, quantity, created_at)

przykładowe zapytanie (w pseudo-SQL) do liczenia współwystępowania par:

INSERT INTO product_reco_cooc (product_id, reco_product_id, score)
SELECT
  ol1.product_id       AS product_id,
  ol2.product_id       AS reco_product_id,
  COUNT(DISTINCT ol1.order_id) AS score
FROM order_lines ol1
JOIN order_lines ol2
  ON ol1.order_id = ol2.order_id
 AND ol1.product_id < ol2.product_id
WHERE ol1.created_at >= NOW() - INTERVAL 6 MONTH
GROUP BY ol1.product_id, ol2.product_id
HAVING COUNT(DISTINCT ol1.order_id) >= 3;

Później można dorobić z tego wersję symetryczną (para A–B i B–A), jeśli system ma działać dwukierunkowo:

INSERT INTO product_reco_cooc_final (product_id, reco_product_id, score)
SELECT product_id, reco_product_id, score FROM product_reco_cooc
UNION ALL
SELECT reco_product_id AS product_id, product_id AS reco_product_id, score
FROM product_reco_cooc;

Na koniec warto dorzucić ranking i limit na produkt, np. trzymać tylko 10 najlepszych rekomendacji na każdy produkt, żeby tabela nie rosła bez kontroli.

Gdzie w sklepie wykorzystać „często kupowane razem”

Ten typ rekomendacji sprawdza się najlepiej w kilku konkretnych miejscach:

  • Strona produktu – sekcja „często kupowane razem” lub „zestaw idealny”. Zwykle 3–5 produktów wystarczy, żeby nie rozpraszać.
  • Koszyk – gdy system widzi 1–2 produkty w koszyku, może zaproponować dodatki na bazie ich współwystępowania, np. kabel HDMI do telewizora, etui do telefonu.
  • Strona potwierdzenia zakupu – rekomendacje do kolejnego zakupu (bez nachalnego wciskania, raczej inspiracja).

Technicznie frontend po prostu wywołuje endpoint rekomendacyjny z product_id produktów obecnych na stronie/koszyku i łączy wyniki w jeden blok, np. biorąc produkty z najwyższą sumą „score”.

Prosty model collaborative filtering na start (bez wielkiej infrastruktury)

Matrix factorization „na małym ogniu”

Najbardziej znane podejście do collaborative filtering to reprezentowanie interakcji użytkownik–produkt w postaci macierzy i jej rozbicie na dwa mniejsze zestawy wektorów (użytkownicy i produkty). W praktyce nie trzeba do tego klastrów Big Data.

Przy niewielkim sklepie można:

  • zbudować macierz implicit feedback (np. „1”, jeśli użytkownik kupił produkt w ostatnich 12 miesiącach),
  • skorzystać z biblioteki takiej jak implicit w Pythonie (LightFM, ALS itp.),
  • trenować model raz na dobę na zwykłym serwerze aplikacyjnym lub osobnym małym VPS.

Proces w skrócie:

Przygotowanie danych do collaborative filtering krok po kroku

Żeby jakikolwiek model collaborative filtering miał sens, trzeba najpierw przygotować porządną tabelę interakcji użytkownik–produkt. Bez tego nawet najlepsza biblioteka będzie „wróżyć z fusów”.

Praktyczny minimalny zestaw pól:

  • user_id – identyfikator użytkownika (lub techniczny identyfikator sesji dla niezalogowanych),
  • product_id – identyfikator produktu,
  • event_type – typ zdarzenia, np. view, add_to_cart, purchase,
  • event_time – znacznik czasu, żeby można było przyciąć dane do ostatnich 6–12 miesięcy,
  • value – siła interakcji, najprostsze: 1 dla zakupu, 0.2 dla kliknięcia itd.

Jeżeli nie ma systemu eventów, można zacząć tylko od zakupów:

  • user_product_events(user_id, product_id, event_type='purchase', value=1, event_time)

Na początek wystarczy zrzut z tabeli zamówień/linii zamówień zagregowany do poziomu „użytkownik–produkt”:

INSERT INTO user_product_events (user_id, product_id, event_type, value, event_time)
SELECT
  o.user_id,
  ol.product_id,
  'purchase' AS event_type,
  1          AS value,
  MAX(o.created_at) AS event_time
FROM orders o
JOIN order_lines ol ON ol.order_id = o.id
WHERE o.status = 'completed'
  AND o.created_at >= NOW() - INTERVAL 12 MONTH
GROUP BY o.user_id, ol.product_id;

Później można dołożyć kolejne typy zdarzeń (odsłony, koszyk), przeskalować je na wartości numeryczne i traktować jako feedback „implicit”.

Uczenie prostego modelu implicit ALS w Pythonie

Jeśli stosujemy bibliotekę implicit, pipeline uczenia w uproszczeniu wygląda tak:

  1. Wyciągnięcie tabeli user_product_events do Pythona (np. przez pandas).
  2. Mapowanie user_id i product_id na kolejne liczby całkowite (indeksy macierzy).
  3. Budowa macierzy rzadkiej (np. csr_matrix) z wartościami interakcji.
  4. Trenowanie modelu ALS.
  5. Zapis wektorów produktów (i użytkowników, jeśli potrzebne) do bazy lub pliku.

Przykładowy „szkielet” kodu (mocno uproszczony, bez obsługi błędów):

import pandas as pd
from scipy.sparse import coo_matrix
import implicit

# 1. Wczytanie danych z DB do DataFrame (tu: CSV jako przykład)
df = pd.read_csv("user_product_events.csv")

# 2. Mapowanie ID na indeksy
user_ids = df["user_id"].astype("category")
item_ids = df["product_id"].astype("category")

df["user_idx"] = user_ids.cat.codes
df["item_idx"] = item_ids.cat.codes

# 3. Budowa macierzy user-item
ratings = coo_matrix(
    (df["value"].astype(float),
     (df["user_idx"], df["item_idx"]))
)

# 4. Trenowanie modelu implicit ALS
model = implicit.als.AlternatingLeastSquares(
    factors=64,       # wymiar wektorów
    regularization=0.01,
    iterations=20
)
# biblioteka implicit zakłada macierz item-user, stąd transpozycja
model.fit(ratings.T)

# 5. Zapis wektorów produktów
item_factors = model.item_factors  # macierz [n_items x factors]

# odwrotne mapowanie indeks - product_id
idx_to_product = dict(enumerate(item_ids.cat.categories))

# dalszy krok: zapis do DB lub pliku

Dla średniej wielkości sklepu taki kod spokojnie da się uruchomić raz na dobę na zwykłym serwerze z kilkoma gigabajtami RAM. Główne „pokrętła” (liczba czynników, liczba iteracji) można dobrać empirycznie, obserwując czas uczenia vs jakość propozycji.

Jak serwować rekomendacje z modelu collaborative filtering

Sam model to dopiero połowa drogi. Trzeba jeszcze szybko i tanio udostępniać jego wyniki backendowi sklepu. Są dwa proste warianty.

Wariant 1: offline’owe precomputy

Najtańsze podejście to wygenerowanie list rekomendacji „na zapas”. Podczas nocnego joba:

  1. Dla każdego aktywnego użytkownika wyliczyć np. 20 najlepszych produktów.
  2. Dla każdego produktu obliczyć listę podobnych produktów (item–item) na podstawie wektorów.
  3. Zapisać wyniki do tabeli, np.:
    • user_reco_cf(user_id, reco_product_id, score),
    • product_sim_cf(product_id, reco_product_id, score).

Endpoint API sprowadza się wówczas do prostego SELECT-a po user_id lub product_id. To rozwiązanie jest tanie obliczeniowo w ciągu dnia i odporne na skoki ruchu.

Wariant 2: on‑the‑fly z trzymaniem modelu w pamięci

Jeśli katalog nie jest ogromny, można wczytać wektory do pamięci serwera (np. w procesie Pythona) i generować rekomendacje na bieżąco. W takim scenariuszu:

  • w nocy trenowany jest model i zapisywane są jego parametry (wektory),
  • backend przy starcie procesu ładuje te wektory (np. z pliku NumPy lub tabeli),
  • przy zapytaniu o rekomendacje bierze aktualną historię użytkownika (np. ostatnie 20 zakupów) i prosi model o listę produktów.

To rozwiązanie daje więcej elastyczności (można np. mocniej ważyć świeższe zakupy), ale wymaga nieco większej dyscypliny przy wdrażaniu kodu (pilnowanie pamięci, wersji modelu itd.).

Typowe pułapki przy collaborative filtering w małym sklepie

Przy małych i średnich katalogach kilka rzeczy potrafi „zabić” efekt przy minimalnym ruchu:

  • Słaba identyfikacja użytkownika – jeśli user_id jest nadawany losowo i gubi się przy każdym czyszczeniu cookies, model nie ma z czego się uczyć. Pomaga prosty mechanizm „pół-konta” (np. przypisywanie historii do maila po zakupie).
  • Za mało sygnałów – same zakupy to często zbyt mało, zwłaszcza przy droższych produktach. Włączenie odsłon i „dodanego do koszyka” zazwyczaj kilka razy zwiększa liczbę interakcji.
  • Brak filtrów biznesowych – model zaproponuje produkt niedostępny lub wycofany, jeśli nie dostanie jasnych ograniczeń. Zawsze po stronie backendu warto dorzucić filtr na dostępność, widoczność, marżę.
  • Przewaga „wiecznych bestsellerów” – w małych sklepach kilka produktów dominuje statystyki. Konieczne bywa lekkie „spłaszczanie” popularności (np. przez logarytmowanie liczby interakcji lub mieszanie z content-based).

Rekomendacje podobnych produktów na bazie cech (content-based)

Jak zbudować wektory cech produktu bez ML‑owego laboratorium

Content-based w wersji budżetowej nie musi polegać na skomplikowanych embeddingach NLP. Da się zacząć od prostych wektorów zbudowanych z tego, co już jest w bazie.

Typowe źródła cech:

  • kategoria i podkategoria,
  • marka,
  • cena (przedziały cenowe),
  • cechy techniczne (np. rozmiar ekranu, typ materiału, kolor),
  • tagi, jeśli są stosowane w CMS‑ie,
  • tytuł/opis – chociażby najprostsze TF‑IDF na słowach.

Wersja „bez fajerwerków” to ręczne zakodowanie atrybutów do wektora binarnego/liczbowego. Przykładowo:

  • każda kategoria i marka zamieniona na wektor „one‑hot”,
  • cena zamieniona na kilka cech typu price_bin_1, price_bin_2 (np. 0–50, 50–150, 150+),
  • kolor jako osobna cecha binarna (czerwony, czarny, biały itd.).

Na to można dołożyć prosty wektor tekstowy, zbudowany z najważniejszych słów w tytule/opisie. Python + scikit-learn (klasa TfidfVectorizer) spokojnie wystarczą.

Prosty pipeline content-based w SQL + Pythonie

Najbardziej praktyczne podejście to przygotowanie danych w SQL, a wektory i podobieństwa policzyć w Pythona:

  1. W SQL generowana jest tabela product_features_raw:
    • z kolumnami typu category_slug, brand, price, color, title, description,
    • filtrowana do aktywnych, dostępnych produktów.
  2. Skrypt w Pythonie wczytuje tę tabelę, zamienia cechy kategoryczne na one‑hot (np. OneHotEncoder), cenę na „bin”, a tekst na wektory TF‑IDF.
  3. Wszystkie wektory są łączone w jeden duży wektor cech i normalizowane.
  4. Liczone jest podobieństwo kosinusowe między produktami w obrębie tej samej kategorii lub kilku zbliżonych (żeby nie porównywać butów z telewizorami).
  5. Dla każdego produktu wybierane jest np. 20 najbardziej podobnych i zapisywane do tabeli product_sim_content.

Przykładowy fragment kodu (uproszczony):

import pandas as pd
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.preprocessing import OneHotEncoder, StandardScaler, normalize
from sklearn.metrics.pairwise import cosine_similarity
import numpy as np

df = pd.read_csv("product_features_raw.csv")

# 1. Cechy kategoryczne
cat_features = df[["category_slug", "brand", "color"]].fillna("NA")
ohe = OneHotEncoder(handle_unknown="ignore", sparse=True)
X_cat = ohe.fit_transform(cat_features)

# 2. Cechy numeryczne (np. cena w logarytmie)
df["price_log"] = np.log1p(df["price"].fillna(0))
scaler = StandardScaler()
X_num = scaler.fit_transform(df[["price_log"]])

# 3. Cechy tekstowe (tytuł + opis)
text = (df["title"].fillna("") + " " + df["description"].fillna("")).str.lower()
tfidf = TfidfVectorizer(
    max_features=5000,
    min_df=3
)
X_text = tfidf.fit_transform(text)

# 4. Połączenie w jeden wektor
from scipy.sparse import hstack
X = hstack([X_cat, X_num, X_text])
X = normalize(X)

# 5. Liczenie podobieństw dla grupy (np. per kategoria)
similarity = cosine_similarity(X)

Pełna macierz podobieństw dla tysięcy produktów może być ciężka, więc często lepiej liczyć ją blokami (np. po kategoriach) albo zamiast pełnej macierzy stosować przybliżone wyszukiwanie najbliższych sąsiadów (faiss, annoy). Ale na początek w wielu sklepach wystarczy ładowanie kategorii pojedynczo.

Jak filtrować i wybierać podobne produkty

Surowa lista „najbardziej podobnych” produktów często zawiera:

  • produkty z innej półki cenowej (np. podobne, ale 3× droższe),
  • inne warianty tego samego produktu (np. ten sam model, inny kolor),
  • produkty wycofane lub praktycznie niesprzedające się.

Żeby rekomendacje nie irytowały, przy generowaniu list warto dorzucić kilka prostych filtrów biznesowych:

  • odrzucanie duplikatów wariantów (zależnie od tego, jak działa katalog – czasem to zaleta, czasem wada),
  • filtrowanie po dostępności (stan magazynowy > 0),
  • ograniczenie różnicy ceny, np. tylko produkty w przedziale 0.7–1.3 ceny produktu bazowego,
  • minimalny poziom sprzedaży lub liczba odsłon (żeby unikać „martwych dusz” w katalogu).

Technicznie może to być po prostu dodatkowy JOIN i WHERE przy ładowaniu wyników z tabeli z podobieństwami.

Gdzie content-based ma największy zwrot z pracy

Najwięcej korzyści z prostych podobnych produktów pojawia się zwykle w trzech miejscach:

  • Strona produktu – sekcja „podobne produkty” przydaje się, gdy klient ogląda coś, co mu „prawie” pasuje. Jeśli szybko zobaczy bliskie alternatywy, jest większa szansa, że zostanie w sklepie zamiast wracać do Google.
  • Lista kategorii/filtrów – gdy użytkownik wybierze filtr, który daje bardzo mało wyników, można mu dorzucić na dole kilka podobnych produktów z sąsiednich kategorii/parametrów.
  • Strony 404 / produkt niedostępny – w sytuacji „ślepej uliczki” content-based jest tanim sposobem na odzyskanie części ruchu (zamiast pustej strony komunikat i blok „zobacz podobne”).

Zwykle wystarczy wdrożenie jednej, spójnej logiki podobieństwa i ponowne użycie jej w kilku miejscach frontu, zamiast budować osobny system dla każdej podstrony.

Łączenie content-based z bestsellerami i co-occurrence

Najlepsze efekty przy rozsądnym nakładzie pracy pojawiają się przy prostym miksie różnych typów rekomendacji. Nie trzeba żadnego skomplikowanego rankera – wystarczy kilka reguł biznesowych.

Przykładowa strategia „mieszana” dla strony produktu:

Poprzedni artykułJak przygotować mieszkanie do sprzedaży, żeby szybciej znaleźć kupca i nie obniżać ceny
Następny artykułARM w laptopach: czy x86 ma się bać?
Emilia Nowak
Emilia Nowak przygotowuje poradniki i recenzje sprzętu oraz usług online, dbając o rzetelne porównania i jasne kryteria. Testuje urządzenia w typowych scenariuszach: wydajność, kultura pracy, czas działania, kompatybilność i wsparcie aktualizacjami, a wyniki opisuje z podaniem warunków pomiaru. Interesuje ją także legalność oprogramowania i bezpieczeństwo danych użytkownika, dlatego w recenzjach zwraca uwagę na polityki producentów i ustawienia prywatności. Pisze przystępnie, ale bez pomijania istotnych szczegółów.