Wprowadzenie
Uważam to pytanie za bardzo interesujące, zakładam, że ktoś opublikował na nim artykuł, ale to mój dzień wolny, więc nie chcę gonić za referencjami.
Możemy więc uznać to za reprezentację / kodowanie danych wyjściowych, co robię w tej odpowiedzi. Nadal myślę, że istnieje lepszy sposób, w którym można po prostu użyć nieco innej funkcji utraty. (Być może suma kwadratowych różnic przy użyciu odejmowania modulo 2 ).π
Ale z rzeczywistą odpowiedzią.
metoda
I proponują, że kąt być przedstawiony w postaci pary, jej wartości sinusa i kosinusa jej.θ
Zatem funkcja kodowania to:
a funkcja dekodowania to: Dla arctan2, które są odwrotnymi stycznymi, zachowując kierunek we wszystkich ćwiartkach)θ ↦ ( grzech( θ ) , cos( θ ) )
( y1, y2)) ↦ arctan2 ( y1, y2))
Teoretycznie możesz równo pracować bezpośrednio z kątami, jeśli twoje narzędzie jest obsługiwane atan2
jako funkcja warstwy (biorąc dokładnie 2 dane wejściowe i generując 1 wynik).
TensorFlow robi to teraz i obsługuje opadanie gradientu , choć nie jest to przeznaczone do tego zastosowania. Badałem za out = atan2(sigmoid(ylogit), sigmoid(xlogit))
pomocą funkcji straty min((pred - out)^2, (pred - out - 2pi)^2)
. Przekonałem się, że trenował o wiele gorzej niż używanie outs = tanh(ylogit), outc = tanh(xlogit))
z funkcją utraty 0.5((sin(pred) - outs)^2 + (cos(pred) - outc)^2
. Wydaje mi się, że można to przypisać nieciągłości gradientuatan2
Moje testy tutaj uruchamiają go jako funkcję przetwarzania wstępnego
Aby to ocenić, zdefiniowałem zadanie:
Biorąc pod uwagę czarno-biały obraz przedstawiający pojedynczą linię na pustym tle Wyprowadzić, pod jakim kątem ta linia jest pod „dodatnią osią x”
Zaimplementowałem funkcję losowego generowania tych obrazów, z liniami pod losowymi kątami (Uwaga: wcześniejsze wersje tego postu używały losowych nachyleń, a nie losowych kątów. Dzięki @Ari Herman za zwrócenie na to uwagi. Teraz jest naprawione). Zbudowałem kilka sieci neuronowych, aby ocenić wydajność na tym zadaniu. Pełne szczegóły implementacji znajdują się w tym notatniku Jupyter . Cały kod jest w Julii , a ja korzystam z biblioteki sieci neuronowej Mocha .
Dla porównania przedstawiam go w stosunku do alternatywnych metod skalowania do 0,1. oraz wkładanie do 500 pojemników i stosowanie softmax soft-label. Nie jestem szczególnie zadowolony z ostatniego i czuję, że muszę go ulepszyć. Dlatego, w przeciwieństwie do innych, testuję go tylko na 1000 iteracji, w porównaniu do dwóch pozostałych, które przeprowadzono na 1000 i na 10 000
Zestaw doświadczalny
Obrazy miały pikseli, a linia przesuwała się na środku i dochodziła do krawędzi. Na obrazie nie było szumu, tylko „czarna” linia na białym tle.101 × 101
Dla każdego szlaku losowo wygenerowano 1000 treningów i 1000 zdjęć testowych.
Sieć oceny miała jedną ukrytą warstwę o szerokości 500. W ukrytej warstwie zastosowano neurony sigmoidalne.
Został przeszkolony przez Stochastic Gradient Decent, ze stałą szybkością uczenia się 0,01 i stałym pędem 0,9.
Nie zastosowano regulacji ani rezygnacji. Nie było też żadnego rodzaju splotu itp. Prosta sieć, która, mam nadzieję, sugeruje, że wyniki te się uogólnią
Dostosowanie tych parametrów w kodzie testowym jest bardzo łatwe i zachęcam ludzi do tego. (i poszukaj błędów w teście).
Wyniki
Moje wyniki są następujące:
| | 500 bins | scaled to 0-1 | Sin/Cos | scaled to 0-1 | Sin/Cos |
| | 1,000 Iter | 1,000 Iter | 1,000 iter | 10,000 Iter | 10,000 iter |
|------------------------|--------------|----------------|--------------|----------------|--------------|
| mean_error | 0.4711263342 | 0.2225284486 | 2.099914718 | 0.1085846429 | 2.1036656318 |
| std(errors) | 1.1881991421 | 0.4878383767 | 1.485967909 | 0.2807570442 | 1.4891605068 |
| minimum(errors) | 1.83E-006 | 1.82E-005 | 9.66E-007 | 1.92E-006 | 5.82E-006 |
| median(errors) | 0.0512168533 | 0.1291033982 | 1.8440767072 | 0.0562908143 | 1.8491085947 |
| maximum(errors) | 6.0749693965 | 4.9283551248 | 6.2593307366 | 3.735884823 | 6.2704853962 |
| accurancy | 0.00% | 0.00% | 0.00% | 0.00% | 0.00% |
| accurancy_to_point001 | 2.10% | 0.30% | 3.70% | 0.80% | 12.80% |
| accurancy_to_point01 | 21.90% | 4.20% | 37.10% | 8.20% | 74.60% |
| accurancy_to_point1 | 59.60% | 35.90% | 98.90% | 72.50% | 99.90% |
Gdy odnoszę się do błędu, jest to wartość bezwzględna różnicy między kątem wyjściowym sieci neuronowej a kątem rzeczywistym. Zatem średni błąd (na przykład) jest średnio ponad 1000 przypadków testowych tej różnicy itp nie jestem pewien, że nie powinno się go poprzez przeskalowanie błąd powiedzmy jest równa na błąd ). π7 π4π4
Przedstawiam również dokładność na różnych poziomach szczegółowości. Dokładność jest częścią przypadków testowych, które uzyskały. Więc accuracy_to_point01
oznacza, że został on liczony jako poprawne, jeśli wyjście było w 0,01 prawdziwego kątem. Żadne z przedstawień nie przyniosło żadnych doskonałych wyników, ale nie jest to wcale zaskakujące, biorąc pod uwagę, jak działa matematyka zmiennoprzecinkowa.
Jeśli spojrzysz na historię tego postu, zobaczysz, że wyniki mają trochę hałasu, nieco inny za każdym razem, gdy go ponownie uruchamiam. Ale ogólny porządek i skala wartości pozostają takie same; co pozwala nam wyciągnąć pewne wnioski.
Dyskusja
Binning z softmaxem działa zdecydowanie najgorzej, ponieważ powiedziałem, że nie jestem pewien, czy coś nie spieprzyłem w implementacji. Działa jednak nieznacznie powyżej wskaźnika zgadywania. Gdyby tylko zgadywał, otrzymalibyśmy średni błądπ
Kodowanie sin / cos działa znacznie lepiej niż skalowane kodowanie 0-1. Poprawa polega na tym, że przy 1000 iteracjach treningowych sin / cos radzi sobie około 3 razy lepiej na większości metryk niż skalowanie przy 10.000 iteracji.
Myślę, że częściowo wiąże się to z poprawą uogólnienia, ponieważ oba miały dość podobny średni błąd kwadratowy na zestawie treningowym, co najmniej raz po uruchomieniu 10 000 iteracji.
Z pewnością istnieje górna granica najlepszej możliwej wydajności w tym zadaniu, biorąc pod uwagę, że Kąt może być mniej więcej dowolną liczbą rzeczywistą, ale nie wszystkie takie anioły wytwarzają różne linie w rozdzielczości pikseli. Ponieważ na przykład oba kąty 45.0 i 45.0000001 są powiązane z tym samym obrazem w tej rozdzielczości, żadna metoda nigdy nie uzyska obu poprawnych poprawności.101 × 101
Wydaje się również prawdopodobne, że w skali absolutnej, aby wyjść poza tę wydajność, potrzebna jest lepsza sieć neuronowa. Zamiast tego bardzo prostego opisanego powyżej w konfiguracji eksperymentalnej.
Wniosek.
Wydaje się, że reprezentacja sin / cos jest zdecydowanie najlepsza z reprezentacji, które badałem tutaj. Ma to sens, ponieważ ma płynną wartość podczas poruszania się po okręgu. Podoba mi się również, że odwrotność można wykonać za pomocą arctan2 , który jest elegancki.
Uważam, że przedstawione zadanie jest wystarczające, aby móc przedstawić rozsądne wyzwanie dla sieci. Chociaż tak naprawdę myślę, że to tylko nauka dopasowywania krzywej do więc być może jest to zbyt łatwe. A może gorzej może sprzyjać sparowanej reprezentacji. Nie sądzę, że tak jest, ale robi się już późno, więc mogłem coś przeoczyć. Zapraszam ponownie do przejrzenia mojego kodu . Zaproponuj ulepszenia lub alternatywne zadania.fa( x ) = y1y2)x