Jeżeli grałeś kiedyś w jakąś grę komputerową, to pewnie zdarzyło Ci się czasem „oszukać przeznaczenie”. Doszedłeś do jakiegoś miejsca, zapisałeś grę, podjąłeś decyzję co dalej i przegrałeś. Po przegranej dostałeś możliwość wczytania gry i spróbowania ponownie zrealizować swój pomysł. Albo zmieniłeś zdanie i zrobiłeś coś całkiem innego, co pozwoliło Ci kontynuować grę. W taki sposób, mimo iż popełniłeś błąd, dostałeś możliwość cofnięcia się „w czasie” i podjęcia innej decyzji. Oszukałeś więc niejako system, żeby dalej grać. I taką analogię zawsze przedstawiam, gdy omawiam komuś niezaznajomionego z systemami kontroli wersji, system Git.
Problem
Powyższa analogia przedstawia jeden z podstawowych scenariuszy programistycznych. W scenariuszu tym, mamy nasz super dobrze działający program, z którego jesteśmy dumni. Chcielibyśmy jednak dodać do niego jakąś poprawkę, a może nową funkcjonalność. Kodujemy ją sobie spokojnie, i przy kolejnym uruchomieniu, nic nie działa. Wpadamy więc w małą panikę i używając naszego edytora, cofamy się krok po kroku aż do momentu, z którego wydaje nam się, że zaczęliśmy. Jeśli zmian było mało, to jest szansa, że trafiliśmy i nasz program znów działa poprawnie. Wyciągamy wnioski i kopiujemy nasz działający program do katalogu „super_program_działa_styczen”. Mamy już bezpieczną działającą kopię, więc możemy wrócić do eksperymentowania. Później przeprowadzamy wiele takich eksperymentów i nasz projekt zawiera katalogi „super_program_szybszy_ale_z_bledem”, „super_program_nowe_ui”, „super_program_wysłany_do_klienta_marzec” i tak dalej.
Czy jest może jakiś sprytniejszy sposób, żeby to ogarnąć?
System kontroli wersji
Rozwiązaniem tego problemu są systemy kontroli wersji. Pozwalają one na bezpieczne zapisywanie wersji całego projektu. Działa to mniej więcej tak, że jeśli chcemy zrobić jakiś eksperyment, to zakładamy nową gałąź. I wszystkie zmiany zapisywane są na tej gałęzi. W każdej chwili możemy sobie wrócić do głównej bazowej gałęzi, która nie ma naszych eksperymentów. Możemy też sobie założyć nowa gałąź z nowym eksperymentem. A gdy eksperyment okaże się udany, to możemy z powrotem go podłączyć do naszej fajnej, dobrze działającej głównej gałęzi.
Brzmi to strasznie, bo średnio da się to wytłumaczyć bez przykładowego rysunku. Zobaczmy więc taki rysunek:
Powyższy diagram obrazuje jeden ze sposobów pracy z systemem Git. Najważniejsza informacja jest taka, że mamy tutaj pięć rodzajów gałęzi. Gałęzie te są umowne dla grupy programistów i prawdopodobnie jeśli rozwijasz swój projekt samodzielnie, to wystarczą Ci gałęzie „master” i „feature”. „Master” to główna gałąź, która zawsze powinna działać. Jeśli jesteś zadowolony ze swojej pracy, to robisz „commit” (takie globalne zapisywanie stanu projektu) i opcjonalnie nadajesz mu sensowną nazwę (np. 1.0). Od tej pory zawsze możesz tu wrócić. Eksperymenty z kolei prowadzisz na gałęziach feature. Jeśli w Twoim projekcie pracuje dużo osób, to dobrze jest się umówić, żeby wszyscy dokładali swoje udane eksperymenty do gałęzi „develop”. Wtedy jeśli szykujecie się do wydania, taka gałąź może się odłączyć do „release”, gdzie kod będzie intensywnie testowany i nie będą dokładane nowe eksperymenty. Może też się zdarzyć, że wszystko już trafiło na gałąź master, a okazało się, że mamy gdzieś jakąś kompromitującą literówkę. Wtedy też możemy zrobić „hotfix” bez zbędnego marnowania czasu.
Profity
Zdaję sobie sprawę, że wciąż pewnie nie brzmi to zbyt przejrzyście. I pewnie długo nie będzie. Ale Git stał się już standardem w bardzo wielu firmach, które wytarzają oprogramowanie, a systemy uczenia maszynowego do niego należą. Chcąc nie chcąc, prędzej lub później na niego natrafisz, bo:
- Pozwala łączyć pracę wielu programistów.
- Każda kopia kodu w systemie Git zawiera pełną historię — możesz więc pracować offline.
- Zawiera każdą zmianę kodu — można więc się cofnąć o całe lata programowania danego projektu.
- Jest szybki.
- Nie ogranicza się tylko do trzymania kodu programów. Możesz tam z powodzeniem trzymać wszystkie pliki, które są zwykłym tekstem.
Oczywiście, zadziała tutaj zasada: śmieci na wejściu — śmieci na wyjściu, musimy więc wykazać się pewnymi chęciami:
i umiejętnościami:
Git a uczenie maszynowe
Git powstał po to, aby przechowywać i łączyć historię kodu źródłowego, czyli zwykłych plików tekstowych. Potrafi wyśledzić zmiany w takich plikach, więc całe lata kodowania nie muszą zawierać się w dużej objętości na dysku. Git nie potrafi natomiast wyłapać zmian w plikach binarnych. Czyli, jeśli mając obrazek, zmienimy w nim jeden piksel, to Git nie zauważy zmiany jednego piksela, tylko całego obrazka. Zmiana ta spowoduje więc, że Git zapisze w swojej historii cały nowy obrazek. Jak możemy się domyśleć, jeżeli w naszych eksperymentach uczenia maszynowego zmieniamy dane, które są np. obrazkami, to bardzo szybko nasza historia stanie się zbyt ciężka, żeby była praktyczna.
W tym miejscu na ratunek może przyjść system DVC, który rozszerza system Git. DVC to skrót od „data version control” co oznacza system kontroli danych. Powstał po to, żeby łatwo nam było trzymać dane osobno od kodu, ale jednocześnie śledzić i zapisywać ich transformacje, jak i wyniki modeli. W ten sposób dalej mamy pełną historię kodu, a danym pozwalamy „puchnąć” w specjalnie do tego wydzielonym dodatkowym podsystemie. Jeśli chcemy więc szybko wrócić do projektu na nowym gołym systemie, to pobieramy kod z systemu Git i aktualnie nas interesujące dane z systemu DVC. Sam jeszcze z tego nie korzystałem, ale przymierzam się do tego tematu i postaram się go opisać w osobnym artykule.
Git — Podsumowanie
Nie jestem pewien, czy przypadkiem Cię bardziej nie zniechęciłem, niż zachęciłem do zapoznania się z systemem Git. Sądzę jednak, że nie da się bez niego (lub czegoś podobnego) obejść, jeśli tworzy się oprogramowanie w zespole. Trzeba bowiem jakoś łączyć ze sobą różne kawałki kodu, które są rozwijane równolegle przez różne osoby. Trzeba również przechowywać historię swojego kodu, żeby w każdej chwili móc się wrócić, jeśli strasznie się coś popsuło. Co ciekawe, jeżeli zapoznasz się z takimi systemami, szybko zaczniesz traktować je jako niezbędne i pracę bez nich będziesz traktować jako barbarzyństwo i proszenie się o kłopoty. Jestem pewien, że tak będzie ;-).