Regresi parameterisasi dari sudut rotasi

15

Katakanlah saya memiliki gambar panah panah dari atas ke bawah, dan saya ingin memprediksi sudut yang dihasilkan panah ini. Ini akan berada di antara dan derajat, atau antara dan . Masalahnya adalah bahwa target ini melingkar, dan derajat persis sama yang merupakan invarian yang ingin saya sertakan dalam target saya, yang seharusnya membantu generalisasi secara signifikan (ini asumsi saya). Masalahnya adalah bahwa saya tidak melihat cara yang bersih untuk menyelesaikan ini, apakah ada makalah yang mencoba untuk mengatasi masalah ini (atau yang serupa)? Saya punya beberapa ide dengan potensi kerugiannya:360 0 2 π 0 360036002π0360

  • Gunakan aktivasi sigmoid atau tanh, skalakan ke rentang ( dan sertakan properti sirkular dalam fungsi kehilangan. Saya pikir ini akan gagal cukup keras, karena jika di perbatasan (prediksi terburuk) hanya sedikit suara akan mendorong bobot untuk pergi satu atau lain cara. Juga, nilai-nilai yang lebih dekat ke perbatasan 0 dan 2 \ pi akan lebih sulit untuk dijangkau karena nilai pra-aktivasi absolut akan perlu mendekati tak terhingga.0 2 π0,2π)02π

  • Regress ke dua nilai, nilai x dan y dan hitung kerugian berdasarkan sudut yang dibuat oleh kedua nilai ini. Saya pikir yang satu ini memiliki lebih banyak potensi tetapi norma dari vektor ini tidak terbatas, yang dapat menyebabkan ketidakstabilan angka dan dapat menyebabkan meledak atau menjadi 0 selama pelatihan. Ini dapat berpotensi diselesaikan dengan menggunakan beberapa regularizer aneh untuk mencegah norma ini terlalu jauh dari 1.

Pilihan lain akan melakukan sesuatu dengan fungsi sinus dan cosinus tetapi saya merasa seperti kenyataan bahwa beberapa pra-aktivasi memetakan ke output yang sama juga akan membuat optimasi dan generalisasi menjadi sangat sulit.

Jan van der Vegt
sumber
Jujur saya pikir mencoba memprediksi rotasi total akan lebih mudah dan memberi Anda hasil yang lebih baik. Anda dapat memetakan dari mis 3π ke π setelah faktanya jika Anda mau. Mencoba memprediksi sudut pada lingkaran unit setelah perkalian pada dasarnya mencoba memprediksi sisanya setelah dibagi dengan 2π , dan saya tidak bisa melihat bagaimana itu akan lebih mudah daripada memprediksi besarnya keseluruhan dan kemudian mengurangi kelipatan 2π .
tom
1
Opsinya adalah a) langkah samping periodisitas: perkirakan dosa dan cos sudut menggunakan fungsi aktivasi sigmoid. b) memasukkan simetri ke dalam fungsi loss melalui kernel seperti itu . Baca tentang kelompok rotasi dan tesis Taco Cohen tentang belajar kelompok transformasi . Sayangnya saya tidak memiliki pengetahuan tentang teori grup jadi saya tidak bisa membantu lebih banyak.
Emre
@tom Hal tentang pendekatan itu adalah bahwa ada pra-aktivasi tak terbatas yang memetakan ke sudut yang sama sementara mereka tidak memiliki kesamaan. Sedangkan x1 positif selalu mengacu pada sudut antara -1/2 dan 1 \ 2 . Dan Emre, saya akan membahas beberapa teori grup, itu selalu membuat saya tertarik sehingga kombinasi ML dan teori grup akan menarik bagi sayaπππ
Jan van der Vegt

Jawaban:

15

Cara kedua, memprediksi dan benar-benar oke.y = s i n ( α )x=cos(α)y=sin(α)

Ya, norma dari vektor yang diprediksi tidak dijamin mendekati . Tetapi tidak mungkin meledak, terutama jika Anda menggunakan fungsi aktivasi sigmoid (yang dibatasi oleh sifatnya) dan / atau mengatur model Anda dengan baik. Mengapa model Anda memprediksi nilai yang besar, jika semua sampel pelatihan dalam ?1 [ - 1 , 1 ](x,y)1[1,1]

Sisi lain adalah vektor terlalu dekat dengan . Ini kadang-kadang bisa terjadi, dan memang bisa mengakibatkan memprediksi sudut yang salah. Tapi itu mungkin dilihat sebagai manfaat dari model Anda - Anda dapat mempertimbangkan norma sebagai ukuran kepercayaan model Anda. Memang, mendekati norma 0 berarti bahwa model Anda tidak yakin di mana arah yang benar.( 0 , 0 ) ( x , y )(x,y)(0,0)(x,y)

Berikut adalah contoh kecil dalam Python yang menunjukkan bahwa lebih baik untuk memprediksi dosa dan cos, untuk memprediksi sudut secara langsung:

# predicting the angle (in radians)
import numpy as np
from sklearn.neural_network import MLPRegressor
from sklearn.model_selection import cross_val_predict
from sklearn.metrics import r2_score
# generate toy data
np.random.seed(1)
X = np.random.normal(size=(100, 2))
y = np.arctan2(np.dot(X, [1,2]), np.dot(X, [3,0.4]))
# simple prediction
model = MLPRegressor(random_state=42, activation='tanh', max_iter=10000)
y_simple_pred = cross_val_predict(model, X, y)
# transformed prediction
joint = cross_val_predict(model, X, np.column_stack([np.sin(y), np.cos(y)]))
y_trig_pred = np.arctan2(joint[:,0], joint[:,1])
# compare
def align(y_true, y_pred):
    """ Add or remove 2*pi to predicted angle to minimize difference from GT"""
    y_pred = y_pred.copy()
    y_pred[y_true-y_pred >  np.pi] += np.pi*2
    y_pred[y_true-y_pred < -np.pi] -= np.pi*2
    return y_pred
print(r2_score(y, align(y, y_simple_pred))) # R^2 about 0.57
print(r2_score(y, align(y, y_trig_pred)))   # R^2 about 0.99

Anda dapat melanjutkan dan memplot prediksi, untuk melihat bahwa prediksi model sinus-kosinus hampir benar, meskipun mungkin perlu beberapa kalibrasi lebih lanjut:

import matplotlib.pyplot as plt
plt.figure(figsize=(12, 3))
plt.subplot(1,4,1)
plt.scatter(X[:,0], X[:,1], c=y)
plt.title('Data (y=color)'); plt.xlabel('x1'); plt.ylabel('x2')
plt.subplot(1,4,2)
plt.scatter(y_simple_pred, y)
plt.title('Direct model'); plt.xlabel('prediction'); plt.ylabel('actual')
plt.subplot(1,4,3)
plt.scatter(y_trig_pred, y)
plt.title('Sine-cosine model'); plt.xlabel('prediction'); plt.ylabel('actual')
plt.subplot(1,4,4)
plt.scatter(joint[:,0], joint[:,1], s=5)
plt.title('Predicted sin and cos'); plt.xlabel('cos'); plt.ylabel('sin')
plt.tight_layout();

masukkan deskripsi gambar di sini

Perbarui . Seorang insinyur navigasi memperhatikan bahwa model seperti itu akan paling akurat ketika sudutnya mendekati . Memang, dekat 0 ° dan 180 ° sudut hampir linier dalam , dan dekat 90 ° dan 270 ° hampir linear dalam . Dengan demikian, dapat bermanfaat untuk menambahkan dua output lagi , seperti dan , untuk membuat model hampir linier di dekat 45 ° dan 135 ° masing-masing. Namun dalam hal ini, mengembalikan sudut asli tidak begitu jelas.πN2αcos(α)sin(α)z=sin(α+π4)w=cos(α+π4)

Solusi terbaik mungkin untuk mengekstrak koordinat dari kedua representasi (dalam yang kedua, kita perlu memutar untuk mendapatkan ), rata-rata, dan hanya kemudian menghitung .(x,y)(z,w)(x,y)arctan2

David Dale
sumber
Ini sempurna, terima kasih banyak. Saya akan memeriksanya lebih lanjut, saya perlu memperluasnya ke dimensi yang lebih
Jan van der Vegt
2

Bekerja dengan koordinat Cartesian berfungsi dengan baik seperti yang disebutkan di atas. Namun, menurut saya, mengonversi data polar ke Cartesian menciptakan ketergantungan antara koordinat X dan Y yang awalnya tidak ada dalam data. Misalnya, model keputusan jalur robot lebih intuitif dalam koordinat kutub daripada Cartesian. Ketergantungan vektor kecepatan robot dalam koordinat kutub antara sudut dan besarnya bahkan mungkin tidak ada atau berbeda dari ketergantungan pada koordinat Cartesian.

Solusi yang saya temukan untuk terus bekerja dengan koordinat polar adalah membuat fungsi kesalahan khusus untuk menghitung perbedaan sudut menggunakan fungsi angdiff () di MATLAB dan perbedaan besarnya seperti biasa.

Fungsi ini mengembalikan '0' untuk perbedaan antara -pi dan pi. Berikut ini tautan ke halaman dukungan fungsi di situs web Mathworks.

https://www.mathworks.com/help/robotics/ref/angdiff.html

Jika Anda menggunakan aktivasi Sigmoid dan data sudut Anda dinormalisasi antara [0,1] Anda harus mengembalikannya ke rentang [-pi, pi] sebelum menggunakan fungsi angdiff () dan kemudian menormalkan kesalahan kembali ke [0,1] ] kisaran untuk proses backpropagation.

Selain itu, fungsi yang setara dalam Python adalah:

import numpy as np


def angdiff(a, b):
    delta = np.arctan2(np.sin(b-a), np.cos(b-a))
    delta = np.around(delta, 4)  # Since np.sin(pi) result is 1.22e-16
    delta += 0.  # Since np.around return -0.
    return delta


pi = np.pi
a = np.asarray([pi/2, 3*pi/4, 0])
b = np.asarray([pi, pi/2, -pi])

print(angdiff(a, b))
print(angdiff(pi, -pi))
print(angdiff(-pi, pi))

Ini mengembalikan hasil yang sama seperti fungsi MATLAB dan bekerja dengan array juga:

[ 1.5708 -0.7854 -3.1416]
0.0
0.0

Semoga itu bisa membantu.

Stav Bar-Sheshet
sumber