Jedna konwencja nazewnictwa, która odmieni twój kod ML
Shape suffixes — drobna konwencja, która zmienia wszystko
Przyznać się: debugowanie kodu sieci neuronowych to i tak wystarczająco trudne zadanie. A jeśli do tego dochodzą tajemnicze nazwy zmiennych? Siedziałeś pewnie nie raz przed outputs i zastanawiałeś się: batch na początku czy na końcu? Gdzie jest wymiar sekwencji? Ktoś go chyba przestawiał?
Jest lepsze rozwiązanie. I brzmi zaskakująco prosto.
Czym są shape suffixes?
To literowe kody dodawane do nazw zmiennych tensorowych. Mówią wprost, jakie wymiary dany tensor zawiera. Zamiast outputs piszesz outputs_bc. Zamiast activations — activations_bcn.
Na pierwszy rzut oka wygląda to na dodatkową robotę. поверь mi — jest odwrotnie. To jedna z najbardziej praktycznych konwencji, jakie możesz wprowadzić w projektach machine learning.
Alfabet wymiarów
Oto standardowy zestaw oznaczeń:
- b — wymiar batcha
- p — pozycja lub indeks w sekwencji
- n — wymiar neuronów (zazwyczaj wynik mnożenia przez macierz wag)
- c — wymiar kanałów
- h — wysokość (dla tensorów obrazu)
- w — szerokość (dla tensorów obrazu)
- d — głębokość (dla danych 3D)
- k — wymiar kernela
W praktyce wygląda to tak: logits_bc mówi ci, że masz do czynienia z batchem logitów z wymiarem klas — idealnie do zadań klasyfikacji. positional_embeddings_bpn? Kolejno: batch, pozycja, neurony.
Dlaczego ta konwencja się opłaca
Kontekst od razu. Widzisz probs_bc i wiesz, że to prawdopodobieństwa pogrupowane przez batch i klasę. Żadnego zgadywania, żadnego szukania w dokumentacji.
Wbudowane wykrywanie błędów. Tu robi się ciekawie. Piszesz:
outputs_bc = torch.matmul(activations_bcn, weights_bcn)
I natychmiast widzisz problem. Macierz wag powinna być weights_nc, żeby prawidłowo pomnożyć się z activations_bcn. Nazwy zmiennych same stają się statycznym sprawdzaczem kształtów tensorów.
Dokumentacja, która się nie starzeje. Komentarze rdzewieją. Nikt ich nie aktualizuje po refaktoryzacji. Ale embeddings_bpn zostaje aktualny tak długo, jak długo trzymasz się konwencji — bo nazwa zmiennej to dokumentacja.
Lżejsze code review. Recenzenci wychwytują niezgodności wymiarów w pull requestach bez uruchamiania kodu czy śledzenia wywołań funkcji. To oszczędza czas wszystkim i pozwala łapać bugi wcześniej.
Wzorce, które warto wdrożyć
Przy konkatenacjach i stackach: Łączysz tensory? Zmień suffix, żeby odzwierciedlał nową strukturę. Stackujesz dwa features_bc wzdłuż nowego wymiaru? Teraz masz features_bck albo features_bkc — zależy, który axis wybrałeś.
Przy redukcjach: Sumujesz po wymiarze pozycji i inputs_bp zamienia się w inputs_b. Bierzesz argmax przez kanały i logits_bc staje się logits_b. Suffix kurczy się razem z tensorem.
Przy bardziej złożonych tensorach: Możesz łączyć kody: attention_bpp dla wyników uwagi między parami pozycji, albo gradients_bpn dla gradientów uporządkowanych przez batch, pozycję i neurony.
Jak to wdrożyć i nie zwariować
Kluczowa jest konsekwencja. Wybierz konwencję, stosuj ją wszędzie — przy wejściach, wyjściach i każdym pośrednim tensorze. Tak, nawet przy tym jednolinijkowym tmp_debug_123, które tworzysz na szybko podczas debugowania.
Twoja przyszła wersja ci podziękuje. Team też.
Jeśli budujesz aplikacje ML i zależy ci na czystym, utrzymywalnym kodzie, który skaluje się z zespołem — te drobne konwencje składają się na ogromne zyski produktywności. To dokładnie ten sam mechanizm, który stoi za dobrym nazewnictwem w całym software developmentzie: pisz kod tak, żeby czytał się jak dokumentacja.
Daj temu tydzień w następnym projekcie. Podejrzewam, że potem będziesz się zastanawiać, jak wcześniej programowałeś bez tego.