Jak pracować z programującymi nieprogramistami
Jacek Bzdak
Magalena Wójcik
Kim jesteśmy?
Jacek:
Jestem jedyną osobą identyfikującą się jako programista w zespole Data Scientistek/ów, więc chcąc nie chcąc (głównie chcąc) odpowiadam za wszystkie rzeczy związane z jakością projektu i wdrożeniami. Wcześniej pracowałem w fizyce jądrowej z naukowcami i elektronikami.
Przez parę lat uczyłem studentów fizyki rozsądnych praktyk programistycznych.
Kim jesteśmy?
Magda:
Przez lata kodowałam rzeczy webowe, ale odkrylam moje powołanie, kiedy zobaczyłam przedmiot "Narzędzia sztucznej inteligencji". Od tego czasu robię głównie Data Science, co znaczy, że skupiam się na zjawiskach i prawidłowościach w danych, a mniej na jakości kodu.
Jacek umiarkowanie mnie lubi ze względu na tę jakość.
Po co ta prezentacja?
Prezentację kierujemy do programistów i tech leadów, którzy nieuchronnie spotkają się w pracy z Data Scientistami (albo innymi specjalistami domenowymi). Mamy nadzieję ułatwić zorganizowanie takiej współpracy i uniknięcie konfliktów.
Disclaimer
- Podane niżej pomysły sprawdzają się w "młodym" zespole w consultingowym startupie, który realizuje dużo małych projektów i ciągle rekrutuje;
- Relatywnie rzadko robimy projekty Big Data, większość z nich da się zrobić na jednej maszynie (nawet jeśli jest to “duża” instancja);
- Jeśli masz terabajty danych i od lat tworzysz produkt możliwe, że rozwiązania te mogą nie być optymalne;
Plan prezentacji
- Skąd podział na "programistów" i "nieprogramistów" i dlaczego jest ważny?
- Kim jest Data Scientistka?
- Jak wyglądają projekty z zakresu DS?
- Problemy (i ich rozwiązania) na linii Data Scientist - programista
- Doświadczenia z pracą z elektronikami.
- Konkulzja: Jak pracować z programującymi nieprogramistami.
Programista vs nieprogramista
Przyjęliśmy rozróżnienie:
- Programista to osoba, której głównym przedmiotem zainteresowania jest pisanie i dbanie o kod,
- Nieprogramista co prawda pisze kod, ale jest głównie zainteresowany innymi tematami, np. wyniki modelu Machine Learningowego albo działanie urządzenia elektronicznego.
Bad programmers worry about the code. Good programmers worry about data structures and their relationships.
- Linus Torvalds
Data Scientists worry about subject matter,
spurious correlations and data leakages.- Magdalena Wójcik
Dlaczego ten podział jest przydatny
Kim jest Data Scientistka?
Data Scientistka to taka programistka, która jest też statystyczką i analityczką w jednym.
Do jej zadań należy modelowanie problemów, projektowanie ekperymentów, implementacja i interpretacja wyników.
Przebieg projektu Data Science
- Rozmowa z klientem, zebranie wymagań i potrzeb, określenie jaki cel biznesowy ma osiągnąć model Machine Learningowy (ML),
- Analiza i czyszczenie danych, co może zająć nawet 90% czasu całego projektu. W ramach tego kroku powstają tzw. Transformery czyli skrypty przygotowujące dane do postaci odpowiedniej dla modelu ML,
- Trenowanie modelu ML (dopasowanie modelu do danych, wynikiem jest wytrenowany model),
- Interpretacja wyników modelu ML,
- Przekazanie wyników do klienta. Wynik może być gotowym produktem, mikroserwisem Data Science, jak i raportem/prezentacją.
Wyniki projektu Data Science
Dla programisty najważniejszy jest działający kod.
- Kiedy poprosisz programistę o wyniki pracy dostaniesz PR z kodem.
Dla Data Scientistki/ty najważniejsze są dane i wyniki działania modelu, który może produkować je parę dni.
- DS może czasem (zupełnie naturalnie i intuicyjnie) wysłać CSV.
Sztandarowe problemy
w projektach Data Science
i jakie znaleźliśmy rozwiązania
API - zrozumiałe dla obu stron
Problem:
Bezproblemowa współpraca między częścią "data-science" a "programistyczną" (api, webaplikacja).
Rozwiązanie:
Ustalone API zrozumiałe dla wszystkich, przejrzane przez wszystkich i wszyscy uważają, że jest dobre i zdatne do użytku.
Praktyka:
Po pierwszej implementacji trzeba je zmienić, i to jest OK.
API - zrozumiałe dla obu stron
@dataclasses.dataclass(frozen=True)
class DetectorResultInstance:
"""Bazowa klasa na wiersz wyniku działania detektora."""
product_id: int
reported_value: float
correct_value: typing.Optional[float]
# ... 15 innych kolumn (potrzeby biznesowe)
class IDetector:
"""Międzymordzie dla szablonów detektorów."""
@abc.abstractmethod
def execute(
self, detector_input: DetectorInput, context: DetectorContext
) -> typing.Iterator[DetectorResultInstance]:
"""
Uruchamia detektor i zwraca jego wyniki. Operacja ta może trwać
długo i jest wykonywana asynchronicznie w kolejce zadań.
"""
raise NotImplementedError
Przeglądy kodu
Problem:
Przeglądy kodu mogą być uważane przez Data Scientistów/ki za niepotrzebne (o kod się nie liczy, liczą się dane) i być stresujące/irytujące.
Symptomy:
Czas przez jaki wiszą otwarte PR generuje problemy w projekcie (tj. nagminnie ludzie zaczynają branchować się od swoich PR)
Przeglądy kodu
Rozwiązanie:
Dbamy, żeby przeglądy kodu nie były okazją do gate keepingu, raczej do poprawy firmowych praktyk.
Nasza procedura przeglądania kodu:
- Znajdź kilka rzeczy, których poprawa zmieni najwięcej,
- Przekonaj osobę zgłaszającą kod do tego, że proponowane poprawki pomogą, albo: daj się przekonać, że nie ma sensu tego robić, bo nie będzie istotnej zmiany,
- Potraktujcie poprawę kodu jako wspólną odpowiedzialność.
"Silo mentality"
Problem:
Sytuacja, kiedy dwa zespoły (u nas Data Science i Development) pracują zupełnie niezależnie, nie dzielą się informacjami i współpracują wyłącznie na podstawie formalnych procedur.
Rozwiązanie:
Wymuszamy komunikację i naukę przez wspólną pracę nad projektami, zawsze z udziałem programistów i DS.
Wdrożenia
Problem:
Wdrożenie aplikacji wykorzystującej model ML dla klienta zwykle leży daleko poza kompetencjami Data Scientista.
Razem z modelem często trzeba wdrożyć też wiele bibliotek w konkretnych wersjach, od których zależą modele ML. Wytrenowane modele są z reguły plikami w nieprzezroczystym formacie i często nie są łatwo portowalne między wersjami biblioteki.
Wdrożenia
Rozwiązanie:
Wytrenowane modele i zależności pakujemy do dockera, to gwarantuje że wersje się nie rozjadą.
Do zarządzania zależnościami używamy pip-tools.
Pip-tools
$ cat requirements.in
Flask
$ pip-compile requirements.in
$ cat requirements.txt
click==6.7 # via flask
flask==0.12.2
itsdangerous==0.24 # via flask
jinja2==2.9.6 # via flask
markupsafe==1.0 # via jinja2
werkzeug==0.12.2 # via flask
Jupyter notebook
Problem:
Do analiz i eksperymentów Data Scientiści korzystają z notebooków, które wewnętrznie są plikami JSON z zserializowanymi wykresami i wynikami obliczeń.
Nie nadają się do wdrożeń, przechowywania w repo, diffowania.
Code review
Jupyter notebook
Rozwiązanie:
Korzystamy z biblioteki jupytext, która równolegle z zapisem pliku ipynb zapisuje też py.
W repo przechowujemy tylko pliki py, chyba, że mamy w notatniku ważne wyniki obliczeń.
Code review
Kod eksperymentalny
Problem:
W projektach Data Science powstaje sporo kodu eksperymentalnego.
Przed startem projektu nie wiadomo:
- Co dokładnie klient ma w danych,
- Co wpływa na wynik, na co zwracać uwagę,
- Jaki model będzie najlepszy do tego problemu.
Pierwsza faza projektu zaczyna się od EDA (Eksploracyjna Analiza Danych).
Choć często prototypy nie ograniczają się od EDA.
Kod eksperymentalny
Rozwiązanie:
- Większość prototypowego kodu trafia do repozytorium,
- Wiemy że ma on niższy standard,
- Generalnie prototypy trzymamy w notebookach jupytera,
- Kod pythona (pliki *.py) zawierają kod produkcyjny.
Testowanie kodu
Problem:
Programista chce przetestować 100% projektu. Data Scientist wie, że to nie ma sensu. Przykładowo, modele najczęściej przychodzą zaimplementowane w bibliotekach i ekstremalnie rzadko są źródłem błędów.
Rozwiązanie:
Warto testować:
- Proces przetwarzania danych (Transformery),
- Warto zrobić "smoke testy" całego projektu.
Praca na instancjach
Problem:
Modele i dane często zajmują dziesiątki GB i nie mieszczą się na lokalnych maszynach. Data Scientiści mogą jednocześnie nie czuć się na tyle komfortowo z Linuxem, żeby pracować wygodnie na zdalnej instancji.
Rozwiązanie:
- Nie pracowanie na instancjach. Wygenerowanie małego datasetu, który mieści się w RAMie lokalnie.
- Zapakowanie pojektu do dockera i stawianie środowiska przez docker-compose up.
Jak pracować z elektronikami
Elektronicy myślą głównie tym jakie urządzenie wyprodukują, ile będzie kosztować w produkcji i jak spełnić wszystkie wymogi stawiane urządzeniu.
- Daniel Kowalski
Protokół komunikacji to API
- Urządzenia mogą mieć specyficzne wymogi;
- Czasem to maksymalny rozmiar ramki;
- Czasem to maksymalny pobór mocy;
- Czasem maksymalny rozmiar kodu wykonywalnego;
- ...
- Trzeba słuchać!
- Warto myśleć o rozszerzalności protokołu, ale ważne jest żeby działał dobrze tu i teraz;
- Koniecznie trzeba wersjonować protokół;
- Trudno jest zrobić go dobrze za pierwszym razem;
Przykład problematycznego protokołu (w projekcie)
- Firma wybrała MODBUS TCP do kontaktu z czujnikami;
- Modbus jest bardzo przyjemnym i fajnym protokołem;
- Natomiast zakłada istnienie mastera który odpytuje urządzenia;
- Z wyboru protokołu wynika że urządzenie działające na GSM miało otwarte gniazdo TCP do serwera non stop;
- Urządzenie działało na baterii;
Wszystko co możliwe należy zaimplementować poza urządzeniem
- Wszystko co się da należy zaimplementować po stronie "programistycznej" (web-serwis, kontroler);
- Update centralnego systemu jest tańszy i łatwiejszy niż update firmware sprzętu zakopanego pod ziemią;
- Tworzenie tej części też jest IMO tańsze;
- Warto pamiętać że nie wszystko da się tak wyeksportować;
Jak pracować z programującymi nie programistami
- Należy trzy razy posłuchać, a dopiero później zacząć działać;
- Za nieintuicyjnymi rozwiązaniami z reguły stoją dobre argumenty;
- Patrz punkt 1;
- Przy odrobinie dobrej woli z obu stron można spotkać się po środku;
Q & A
- jacek@logicai.io
- magda@logicai.io
- https://logicai.io/
- https://slides.com/magdalenawojcik/jak-pracowac/
Jak pracować
By Magdalena Wójcik
Jak pracować
PyCon PL 2019
- 1,701