W 4soft nie wszystko kręci się wokół biznesu. W momentach gdy nasi członkowie zespołu nie są zaangażowani w pracę z klientem, chętnie szukamy nowych wyzwań. Ważne: muszą dostarczać nam sporo dobrej zabawy, a jednocześnie stymulować nasz rozwój!
Tym sposobem, pod koniec maja, zdecydowaliśmy zająć się projektem z obszaru Data Science. Na Kaggle znaleźliśmy zestawy danych z NFL Big Data Competition 2022 – konkursu, w którym narzędzia Data Science wykorzystuje się do wyciągania wniosków na temat futbolu amerykańskiego. Zanim jednak przeszliśmy do budowy aplikacji, musieliśmy dogłębnie zrozumieć i przeanalizować dostępne dane.
W Polsce futbol amerykański jest dość „egzotycznym” sportem. Z tego względu, na początku musieliśmy poświęcić sporo czasu na opanowanie podstawowych zasad gry. Poznawszy jej podstawy, mogliśmy zagłębić się w posiadany zestaw danych.
Zestawy zawierały w sobie dane dotyczące trzech sezonów NFL między 2018 a 2020 roku. Mieliśmy do dyspozycji dane rejestrujące pozycję, prędkość oraz przyspieszenie piłki oraz wszystkich zawodników na boisku. Informacje te były rejestrowane co 100 milisekund przez cały czas trwania gry.
Dysponowaliśmy również danymi odnośnie zawodników, w tym krótką charakterystyką każdego, kto brał udział w rozgrywkach w którymkolwiek z trzech sezonów. Oprócz tego, do naszej dyspozycji były również dane dotyczące meczów – a konkretniej, składu drużyn zaangażowanych w każdą rozgrywkę i czasu jej trwania. Dodatkowo korzystaliśmy z danych akcji podzielonych na dwa zbiory podsumowujące różne aspekty gry.
Gdy nadszedł odpowiedni moment, wyklarowaliśmy cele naszego projektu. Nasza aplikacja miała za zadanie pełnić następujące funkcje:
– symulacja live streamingu gier NFL
– animacja trwających rozgrywek z wykorzystaniem danych śledzących
– przewidywanie zwycięzców każdej gry
– wyświetlanie statystyk graczy i zespołów
Do stworzenia aplikacji wykorzystaliśmy liczne technologie. Oto lista najważniejszych z nich wraz z krótkimi opisami.
Przeważająca część kodu naszej aplikacji powstała w Pythonie, który jest wiodącym językiem programowania w obszarach Data Science i Machine Learning. Większość pozostałych bibliotek i frameworków, z których korzystamy, zostało napisane w Pythonie lub też powstało na nim ich API. Głównym wyjątkiem jest JavaScript, z którego skorzystaliśmy celem budowy aplikacji po stronie serwera.
Biblioteka pandas jest popularna i dobrze znana do analizy danych w Pythonie. Początkowo planowaliśmy wykorzystać ją na potrzeby przetwarzania danych, jednak w międzyczasie usłyszeliśmy o Polars – nowej bibliotece służącej do tych samych celów, napisanej w Rust i mającej API w Pythonie.
Okazało się, że Polars wyprzedza Pandas w przedbiegach, przynajmniej w kontekście tempa niektórych operacji. Mimo niewątpliwej zalety, jaką jest szybkość, biblioteka ta jest jednak nowa i przez to niekompatybilna z wieloma innymi ważnymi bibliotekami. Korzystamy z niej kiedy tylko to możliwe, przerzucając się na z powrotem na Pandas kiedy obiekty Polars nie są obsługiwane.
Kolejną technologią, którą wykorzystaliśmy na potrzeby przetwarzania danych, była Apache Spark. Wykorzystaliśmy bibliotekę Apache Spark Structured Streaming, która gwarantuje szybkie, skalowalne i odporne na błędy przetwarzanie przepływu danych. Operacje przeprowadzane na danych są definiowane w taki sam sposób jak w przypadku Spark SQL. Po zdefiniowaniu danych wejściowych i wyjściowych i rozpoczęciu zapytania, Spark automatycznie wykrywa i przetwarza wszystkie nowe dane.
Do śledzenia najnowszych aktualizacji zbiorów danych używamy DVC – Data Version Control. Nazwa mówi sama za siebie: DVC to ogólnodostępny system kontroli wersji dla projektów angażujących Data Science i Machine Learning. Wersjonuje on duże pliki zawierające dane lub modele, buforując ich snapshoty i przechowując metadane (włączając w to linki) w repozytoriach Git.
Graficzny interfejs użytkownika stworzyliśmy, korzystające ze Streamlit. To framework, służący do budowania szybkich aplikacji webowych w Pythonie. Streamlit jest kolejnym narzędziem, które powinien znać każdy Data Scientist, świetnie sprawdzającym się na potrzeby wizualizacji danych.
Aby stworzyć wizualizację boiska, skorzystaliśmy z Pillow, biblioteki przetwarzającej obrazy. Wykresy stworzyliśmy z wykorzystaniem Plotly.
Na potrzeby wdrożenia i uczenia modeli korzystaliśmy z scikit-learn, XGBoost i Keras. Scikit-learn to prawdopodobnie najlepiej znana pythonowa biblioteka dla Machine Learning. Ma ona sporo narzędzi i modeli. XGBoost wdraża algorytmy uczenia maszynowego z wykorzystaniem Gradient Boosting. Keras to interfejs wysokiego poziomu dla TensorFlow, platformy Deep Learning. Do śledzenia parametrów i wyników wykorzystaliśmy MLflow.
Aplikację stworzyliśmy w trzech repozytoriach. Każde z nich było odrębnym komponentem.
– Główne – w ramach tego repozytorium przetwarzamy zestawy danych i kalkulacje modeli przewidujące wyniki gry
– Serwer– z tego repozytorium transmitowane są dane śledzące;
– Klient – to repozytorium otrzymuje dane, przetwarza je i wyświetla użytkownikowi końcowemu.
Schemat, który zamieściliśmy poniżej, pokazuje zależności między komponentami. Mniejsze okienka w ramach tych komponentów reprezentują główne funkcjonalności. Każdej z nich przypisano wykorzystywane technologie.
Główny komponent pełni dwie podstawowe funkcje:
Dane, które otrzymaliśmy, były wysokiej jakości. Niemniej jednak niektóre funkcjonalności wymagały ich przygotowania w określonej formie. Na przykład, plik z danymi śledzącymi musi być sortowany chronologicznie, tak aby, czytany wers po wersie, symulował streaming gier na żywo.
Okazało się również, że brakowało nam danych odnośnie wyników gry. Musieliśmy więc znaleźć je w innym zestawie danych i włączyć w ten, którym dysponowaliśmy. Kroki przygotowawcze przeprowadziliśmy na głównym repozytorium z wykorzystaniem Polars. Cały proces przetwarzania danych ustrukturyzowaliśmy w pipeline za pośrednictwem DVC. System kontroli wersji umożliwił nam też przenoszenie danych między głównym repozytorium a pozostałymi.
Nasz model ma za zadanie przewidzieć zwycięzcę w oparciu o informacje na temat drużyn, ich średnich wyników z ostatnich dwóch, czterech i szesnastu gier, różnic w ratingu Elo, również w kontekście rozgrywających.
Na potrzeby jego stworzenia przetestowaliśmy takie modele jak Support Vector Machine, Random Forest, K-Nearest Neighbor, Logistic Regression, Gradient-boosted Decision Trees, Multi-layer Perceptron i Fully Connected Neural Network.
Aby znaleźć najlepsze modele i ich hiperparametry, sprawdziliśmy je krzyżowo. Najlepsze rezultaty osiągnęła sieć neuronowa, a także regresja liniowa i Support Vector Machine. Koniec końców, zdecydowaliśmy się na pierwszą z nich. Nasz model ma trzy ukryte warstwy, z których każda składa się z 256 neuronów oraz dropoutu i funkcji aktywacji. Outputem jest neuron z sigmoidalną funkcją aktywacji, oznaczający prawdopodobieństwo zwycięstwa danej drużyny. Dokładność modelu wynosi = 0.655 a brier score = 0.217.
Strona serwerowa służy w tym przypadku do symulowania prawdziwego strumienia danych gromadzonych w trakcie gier NFL. Powstała w JavaScript i działa na Node.JS. Do komunikacji wykorzystuje bibliotekę Socket.io.
Serwer przesyła dane śledzące oraz informacje o wynikach konkretnych akcji i meczów. Podsumowania akcji i wyniki meczów są przechowywane w pamięci, a dane śledzące są sczytywane z pliku wers po wersie. Z każdego ekstrahowane są sygnatury czasowe, playid i gameid. Serwer wykorzystuje te pierwsze do kalkulowania ilości czasu, który musi upłynąć, zanim kolejne wersy danych śledzących będą mogły zostać przesłane.
Podsumowania akcji są przesyłane kiedy serwer wykryje przerwanie ciągłości danych śledzących. Wyniki meczów są przesyłane po upływie przynajmniej 4.5 godziny (zgodnie z danymi śledzącymi) od pierwszej zarejestrowanej akcji meczu.
Komponent kliencki może być z grubsza podzielony na graficzny interfejs użytkownika oraz procesy w tle uwzględniające przetwarzanie przesyłanych danych.
Model predykcyjny, wchodzący w skład głównego repozytorium, przenieśliśmy do komponentu klienta za pośrednictwem DVC. Wykorzystany został tam do przewidywania rezultatów nadchodzących rozgrywek. Ponadto, wyniki streamowanych gier zostały uwzględnione w historycznych danych, a model był aktualizowany po każdym kolejnym tygodniu rozgrywek.
Dane śledzące są przydzielane do konkretnych meczów na potrzeby wizualizacji i przechowywane w odpowiadających im obiektach. W dodatku, dane ze wszystkich meczów są dodawane do listy, a później zapisywane do pliku, co powoduje opróżnienie listy.
Dane śledzące i podsumowania akcji są zapisywane na dysku i automatycznie przetwarzane przez Spark. Na bazie przesyłanych danych aktualizowany jest również rating Elo. Gdy wszystkie mecze w danym tygodniu zostaną rozegrane, model jest uczony z wykorzystaniem pozyskanych z nich danych.
Napisany w Streamlit, GUI zawiera strony poświęcone czterem funkcjonalnościom.
Na załączonym obrazku możesz zobaczyć wizualną reprezentację boiska, graczy oraz piłki. Dzięki wysokiej częstotliwości śledzenia informacji (10 klatek na sekundę), animacja jest wystarczająco płynna. Poza odzwierciedlaniem gry “na żywo”, strona wyświetla również nazwiska graczy oraz przewidywania wyniku meczu.
Wszystkie kalkulacje modelu są zachowywane. Kolejna ze stron wyświetla predykcje wyników, co umożliwia weryfikację dokładności modelu.
Informacje dotyczące wszystkich rozpoczętych gier są wyświetlane w tabeli. Każdemu wersowi odpowiada konkretny kolor – żółty (nieukończone gry), zielony (trafnie przewidziany wynik), pomarańczowy (źle przewidziany wynik).
Aplikacja umożliwia sprawdzenie, w jaki sposób dana drużyna radziła sobie w danym sezonie. Użytkownik może też porównać wyniki z innymi drużynami.
Dla każdej gry kalkulowany jest zestaw prostych statystyk. Uwzględniają one m.in. całkowity dystans pokonywany przez każdego zawodnika oraz liczbę udanych strzałów.
Ukończenie prac nad projektem zajęło nam dwa miesiące. Po realizacji przyszedł czas na refleksję nad tym, co udało nam się osiągnąć.
W toku projektu udało nam się zbudować w pełni funkcjonalną aplikację. Co najważniejsze, prace nad nią były świetną okazją, aby pogłębić ekspertyzę i umiejętności naszego zespołu w ciekawy i dostarczający sporej ilości rozrywki sposób. Jednym z najbardziej satysfakcjonujących aspektów Data Science jest możliwość dowiedzenia się czegoś więcej na temat zagadnienia, które poddajemy analizie czy predykcji. Po kilku tygodniach z nosem w statystykach na temat futbolu amerykańskiego i NFL, sami zaczęliśmy z niecierpliwością wyczekiwać kolejnego Super Bowl! Jednocześnie wzbogaciliśmy nasz tech stack o nowe technologie, jak Polars czy DVC.
Proces tworzenia aplikacji świetnie przełożył się na budowanie zespołowych więzi. Dla niektórych członków naszego zespołu była to pierwsza okazja, aby ze sobą współpracować.
Jeśli chodzi o samą aplikację, udało nam się stworzyć solidną bazę, którą można będzie poszerzać o kolejne funkcjonalności. Możemy na przykład wprowadzić opcję kalkulacji statystyk wyników rozgrywek różnych drużyn i zawodników w danym sezonie. Uczenie maszynowe możemy wykorzystać nie tylko do przewidywania zwycięzców meczów, ale również wyników konkretnych zagrań.
Cieszy nas perspektywa wypróbowania wszystkich tych opcji w przyszłości. A teraz czas na nowe wyzwania!