Przegląd
Ten przewodnik przedstawia mechanizmy definiowania operacji niestandardowych (ops), jąder i gradientów w TensorFlow.js. Ma na celu przedstawienie przeglądu głównych koncepcji i wskaźników do kodu, które demonstrują te koncepcje w działaniu.
Dla kogo jest ten przewodnik?
Jest to dość zaawansowany przewodnik, który dotyka niektórych elementów wewnętrznych TensorFlow.js, może być szczególnie przydatny dla następujących grup osób:
- Zaawansowani użytkownicy TensorFlow.js zainteresowani dostosowywaniem zachowania różnych operacji matematycznych (np. badacze nadpisujący istniejące implementacje gradientów lub użytkownicy, którzy muszą załatać brakującą funkcjonalność w bibliotece)
- Użytkownicy budujący biblioteki, które rozszerzają TensorFlow.js (np. biblioteka ogólnej algebry liniowej zbudowana na prymitywach TensorFlow.js lub nowy backend TensorFlow.js).
- Użytkownicy zainteresowani dodawaniem nowych operacji do tensorflow.js, którzy chcą uzyskać ogólny przegląd działania tych mechanizmów.
To nie jest przewodnik po ogólnym użyciu TensorFlow.js, ponieważ dotyczy wewnętrznych mechanizmów implementacji. Nie musisz rozumieć tych mechanizmów, aby korzystać z TensorFlow.js
Aby jak najlepiej wykorzystać ten przewodnik, musisz mieć swobodę (lub chcieć spróbować) czytać kod źródłowy TensorFlow.js.
Terminologia
W tym przewodniku kilka kluczowych terminów jest przydatnych do opisania z góry.
Operacje (Ops) — operacja matematyczna na jednym lub większej liczbie tensorów, która w wyniku daje co najmniej jeden tensor. Operatorzy są kodem „wysokiego poziomu” i mogą używać innych operatorów do definiowania swojej logiki.
Kernel — Konkretna implementacja zoptymalizowana pod kątem określonych możliwości sprzętowych/platformowych. Jądra są „niskopoziomowe” i specyficzne dla backendu. Niektóre operacje mają mapowanie jeden-do-jednego z operacji na jądro, podczas gdy inne korzystają z wielu jąder.
Gradient / GradFunc — Definicja „wstecznego trybu” operacji /jądra , która oblicza pochodną tej funkcji w odniesieniu do niektórych danych wejściowych. Gradienty są kodem „wysokiego poziomu” (nie są specyficzne dla backendu) i mogą wywoływać inne operacje lub jądra.
Rejestr jądra — odwzorowanie krotki (nazwa jądra, nazwa zaplecza) na implementację jądra.
Gradient Registry — Odwzorowanie nazwy jądra na implementację gradientu .
Organizacja kodu
Operacje i Gradienty są zdefiniowane w tfjs-core .
Jądra są specyficzne dla backendu i są zdefiniowane w odpowiednich folderach backendu (np . tfjs-backend-cpu ).
Niestandardowe operacje, jądra i gradienty nie muszą być definiowane w tych pakietach. Ale często będą używać podobnych symboli w ich implementacji.
Wdrażanie niestandardowych operacji
Jednym ze sposobów myślenia o niestandardowym op jest po prostu funkcja JavaScript, która zwraca jakieś wyjście tensorowe, często z tensorami jako danymi wejściowymi.
- Niektóre operacje mogą być całkowicie zdefiniowane w kategoriach istniejących operacji i powinny po prostu importować i wywoływać te funkcje bezpośrednio. Oto przykład .
- Implementacja operacji może również zostać rozesłana do konkretnych jąder zaplecza. Odbywa się to za pośrednictwem
Engine.runKernel
i zostanie opisane dalej w sekcji „Implementowanie niestandardowych jąder”. Oto przykład .
Wdrażanie niestandardowych jąder
Implementacje jądra specyficzne dla backendu umożliwiają zoptymalizowaną implementację logiki dla danej operacji. Jądra są wywoływane przez ops wywołujące tf.engine().runKernel()
. Implementacje jądra są definiowane przez cztery rzeczy
- Nazwa jądra.
- Backend, w którym jest zaimplementowane jądro.
- Dane wejściowe: argumenty tensorowe funkcji jądra.
- Atrybuty: nietensorowe argumenty funkcji jądra.
Oto przykład implementacji jądra . Konwencje używane do implementacji są specyficzne dla backendu i najlepiej je zrozumieć, patrząc na implementację i dokumentację każdego konkretnego backendu.
Generalnie jądra działają na poziomie niższym niż tensory i zamiast tego bezpośrednio odczytują i zapisują do pamięci, która zostanie ostatecznie opakowana w tensory przez tfjs-core.
Po zaimplementowaniu jądra można je zarejestrować w TensorFlow.js za pomocą funkcji registerKernel
z tfjs-core. Możesz zarejestrować jądro dla każdego backendu, w którym ma ono działać. Po zarejestrowaniu jądro może zostać wywołane za pomocą tf.engine().runKernel(...)
, a TensorFlow.js upewni się, że zostanie wysłane do implementacji w aktualnie aktywny backend.
Wdrażanie niestandardowych gradientów
Gradienty są ogólnie zdefiniowane dla danego jądra (identyfikowane przez tę samą nazwę jądra, która została użyta w wywołaniu tf.engine().runKernel(...)
). Dzięki temu tfjs-core może używać rejestru do wyszukiwania definicji gradientów dla dowolnego jądra w czasie wykonywania.
Implementowanie niestandardowych gradientów jest przydatne w przypadku:
- Dodawanie definicji gradientu, która może nie być obecna w bibliotece
- Zastępowanie istniejącej definicji gradientu w celu dostosowania obliczania gradientu dla danego jądra.
Możesz zobaczyć przykłady implementacji gradientów tutaj .
Po zaimplementowaniu gradientu dla danego wywołania można go zarejestrować w TensorFlow.js za pomocą funkcji registerGradient
z tfjs-core.
Innym podejściem do implementacji niestandardowych gradientów, które pomija rejestr gradientów (a tym samym pozwala na obliczanie gradientów dla dowolnych funkcji w dowolny sposób, jest użycie tf.customGrad .
Oto przykład operacji w bibliotece przy użyciu customGrad
,Przegląd
Ten przewodnik przedstawia mechanizmy definiowania operacji niestandardowych (ops), jąder i gradientów w TensorFlow.js. Ma na celu przedstawienie przeglądu głównych koncepcji i wskaźników do kodu, które demonstrują te koncepcje w działaniu.
Dla kogo jest ten przewodnik?
Jest to dość zaawansowany przewodnik, który dotyka niektórych elementów wewnętrznych TensorFlow.js, może być szczególnie przydatny dla następujących grup osób:
- Zaawansowani użytkownicy TensorFlow.js zainteresowani dostosowywaniem zachowania różnych operacji matematycznych (np. badacze nadpisujący istniejące implementacje gradientów lub użytkownicy, którzy muszą załatać brakującą funkcjonalność w bibliotece)
- Użytkownicy budujący biblioteki, które rozszerzają TensorFlow.js (np. biblioteka ogólnej algebry liniowej zbudowana na prymitywach TensorFlow.js lub nowy backend TensorFlow.js).
- Użytkownicy zainteresowani dodawaniem nowych operacji do tensorflow.js, którzy chcą uzyskać ogólny przegląd działania tych mechanizmów.
To nie jest przewodnik po ogólnym użyciu TensorFlow.js, ponieważ dotyczy wewnętrznych mechanizmów implementacji. Nie musisz rozumieć tych mechanizmów, aby korzystać z TensorFlow.js
Aby jak najlepiej wykorzystać ten przewodnik, musisz mieć swobodę (lub chcieć spróbować) czytać kod źródłowy TensorFlow.js.
Terminologia
W tym przewodniku kilka kluczowych terminów jest przydatnych do opisania z góry.
Operacje (Ops) — operacja matematyczna na jednym lub większej liczbie tensorów, która w wyniku daje co najmniej jeden tensor. Operatorzy są kodem „wysokiego poziomu” i mogą używać innych operatorów do definiowania swojej logiki.
Kernel — Konkretna implementacja zoptymalizowana pod kątem określonych możliwości sprzętowych/platformowych. Jądra są „niskopoziomowe” i specyficzne dla backendu. Niektóre operacje mają mapowanie jeden-do-jednego z operacji na jądro, podczas gdy inne korzystają z wielu jąder.
Gradient / GradFunc — Definicja „wstecznego trybu” operacji /jądra , która oblicza pochodną tej funkcji w odniesieniu do niektórych danych wejściowych. Gradienty są kodem „wysokiego poziomu” (nie są specyficzne dla backendu) i mogą wywoływać inne operacje lub jądra.
Rejestr jądra — odwzorowanie krotki (nazwa jądra, nazwa zaplecza) na implementację jądra.
Gradient Registry — Odwzorowanie nazwy jądra na implementację gradientu .
Organizacja kodu
Operacje i Gradienty są zdefiniowane w tfjs-core .
Jądra są specyficzne dla backendu i są zdefiniowane w odpowiednich folderach backendu (np . tfjs-backend-cpu ).
Niestandardowe operacje, jądra i gradienty nie muszą być definiowane w tych pakietach. Ale często będą używać podobnych symboli w ich implementacji.
Wdrażanie niestandardowych operacji
Jednym ze sposobów myślenia o niestandardowym op jest po prostu funkcja JavaScript, która zwraca jakieś wyjście tensorowe, często z tensorami jako danymi wejściowymi.
- Niektóre operacje mogą być całkowicie zdefiniowane w kategoriach istniejących operacji i powinny po prostu importować i wywoływać te funkcje bezpośrednio. Oto przykład .
- Implementacja operacji może również zostać rozesłana do konkretnych jąder zaplecza. Odbywa się to za pośrednictwem
Engine.runKernel
i zostanie opisane dalej w sekcji „Implementowanie niestandardowych jąder”. Oto przykład .
Wdrażanie niestandardowych jąder
Implementacje jądra specyficzne dla backendu umożliwiają zoptymalizowaną implementację logiki dla danej operacji. Jądra są wywoływane przez ops wywołujące tf.engine().runKernel()
. Implementacje jądra są definiowane przez cztery rzeczy
- Nazwa jądra.
- Backend, w którym jest zaimplementowane jądro.
- Dane wejściowe: argumenty tensorowe funkcji jądra.
- Atrybuty: nietensorowe argumenty funkcji jądra.
Oto przykład implementacji jądra . Konwencje używane do implementacji są specyficzne dla backendu i najlepiej je zrozumieć, patrząc na implementację i dokumentację każdego konkretnego backendu.
Generalnie jądra działają na poziomie niższym niż tensory i zamiast tego bezpośrednio odczytują i zapisują do pamięci, która zostanie ostatecznie opakowana w tensory przez tfjs-core.
Po zaimplementowaniu jądra można je zarejestrować w TensorFlow.js za pomocą funkcji registerKernel
z tfjs-core. Możesz zarejestrować jądro dla każdego backendu, w którym ma ono działać. Po zarejestrowaniu jądro może zostać wywołane za pomocą tf.engine().runKernel(...)
, a TensorFlow.js upewni się, że zostanie wysłane do implementacji w aktualnie aktywny backend.
Wdrażanie niestandardowych gradientów
Gradienty są ogólnie zdefiniowane dla danego jądra (identyfikowane przez tę samą nazwę jądra, która została użyta w wywołaniu tf.engine().runKernel(...)
). Dzięki temu tfjs-core może używać rejestru do wyszukiwania definicji gradientów dla dowolnego jądra w czasie wykonywania.
Implementowanie niestandardowych gradientów jest przydatne w przypadku:
- Dodawanie definicji gradientu, która może nie być obecna w bibliotece
- Zastępowanie istniejącej definicji gradientu w celu dostosowania obliczania gradientu dla danego jądra.
Możesz zobaczyć przykłady implementacji gradientów tutaj .
Po zaimplementowaniu gradientu dla danego wywołania można go zarejestrować w TensorFlow.js za pomocą funkcji registerGradient
z tfjs-core.
Innym podejściem do implementacji niestandardowych gradientów, które pomija rejestr gradientów (a tym samym pozwala na obliczanie gradientów dla dowolnych funkcji w dowolny sposób, jest użycie tf.customGrad .
Oto przykład operacji w bibliotece przy użyciu customGrad