Regressão da parametrização do ângulo de rotação

15

Digamos que tenho uma imagem de cima para baixo de uma seta e quero prever o ângulo que essa seta faz. Isso seria entre e 360 graus, ou entre 0 e 2 π . O problema é que esse alvo é circular, 0 e 360 graus são exatamente os mesmos, o que é uma invariância que eu gostaria de incorporar ao meu alvo, o que deve ajudar a generalizar significativamente (essa é minha suposição). O problema é que não vejo uma maneira clara de resolver isso. Existem documentos que tentam resolver esse problema (ou similares)? Eu tenho algumas idéias com suas possíveis desvantagens:0 03600 02π0 0360

  • Use uma ativação sigmóide ou tanh, dimensione-a para a faixa ( e incorpore a propriedade circular na função de perda. Eu acho que isso falhará bastante, porque se estiver na fronteira (pior previsão), apenas um pouquinho de ruído empurrará os pesos para seguir um caminho ou outro. Além disso, os valores mais próximos da borda de 0 e 2 π serão mais difíceis de alcançar porque o valor absoluto de pré-ativação precisará estar próximo do infinito.0 0,2π)0 02π

  • Regredir para dois valores, um e y de valores e calcular a perda com base no ângulo estes dois valores fazem. Eu acho que esse tem mais potencial, mas a norma desse vetor é ilimitada, o que pode levar à instabilidade numérica e a explosões ou a 0 durante o treinamento. Isso pode ser resolvido usando um regularizador estranho para impedir que essa norma fique muito longe de 1.xy

Outras opções estariam fazendo algo com funções seno e cosseno, mas sinto que o fato de várias pré-ativações serem mapeadas para a mesma saída também tornará a otimização e generalizações muito difíceis.

Jan van der Vegt
fonte
Honestamente, acho que tentar prever a rotação total será mais fácil e obter melhores resultados. Você pode mapear de, por exemplo, a π após o fato, se desejar. Tentar prever o ângulo no círculo unitário após as multiplicações é essencialmente tentar prever o restante após dividir por 2 π , e não consigo ver como isso seria mais fácil do que prever a magnitude geral e subtrair múltiplos de 2 π . 3ππ2π2π
Tom
11
As opções são: a) evitar a periodicidade: estimar o pecado e o cos do ângulo usando uma função de ativação sigmóide. b) incorporar a simetria na função de perda através de um kernel como esse . Leia sobre grupos de rotação e a tese de Taco Cohen sobre a aprendizagem de grupos de transformação . Infelizmente, não conheço a teoria dos grupos e não posso ajudar muito mais.
Emre
@ tom A questão dessa abordagem é que existem pré-ativações infinitas que são mapeadas no mesmo ângulo enquanto elas não têm nada em comum. Enquanto um x1 positivo sempre se refere a um ângulo entre -1/2 e 1 \ 2 π . E Emre, vou trabalhar o meu caminho através de alguma teoria do grupo, que sempre me interessou de modo a combinação de ML e teoria dos grupos vai apelar para meππ
Jan van der Vegt

Respostas:

15

A segunda maneira, prever e y = s i n ( α ) é totalmente aceitável.x=cos(α)y=sEun(α)

Sim, não é garantido que a norma do vetor previsto esteja próxima de 1 . Mas não é provável que ocorra uma explosão, especialmente se você usar funções de ativação sigmóide (que são limitadas pela natureza) e / ou regularizar bem seu modelo. Por que seu modelo prevê um valor alto, se todas as amostras de treinamento estão em [ - 1 , 1 ] ?(x,y)1 1[-1 1,1 1]

Outro lado é o vetor muito próximo de ( 0 , 0 ) . Isso às vezes pode acontecer e pode realmente resultar na previsão de ângulos errados. Mas isso pode ser visto como um benefício do seu modelo - você pode considerar a norma de ( x , y ) como uma medida de confiança do seu modelo. De fato, uma norma próxima a 0 significa que seu modelo não tem certeza de onde está a direção certa.(x,y)(0 0,0 0)(x,y)

Aqui está um pequeno exemplo em Python, que mostra que é melhor prever o pecado e cos, que prever o ângulo diretamente:

# 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

Você pode continuar e plotar as previsões, para ver que as previsões do modelo seno-cosseno estão quase corretas, embora possam precisar de mais calibração:

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();

insira a descrição da imagem aqui

πN2αporque(α)pecado(α)z=pecado(α+π4)W=porque(α+π4)

(x,y)(z,W)(x,y)arctan2

David Dale
fonte
Isso é perfeito, muito obrigado. Eu vou olhar para ele mais, eu vou necessidade de estendê-lo para mais dimensões
Jan van der Vegt
2

Trabalhar com coordenadas cartesianas funciona bem como mencionado acima. No entanto, na minha opinião, converter dados polares em cartesiano cria dependências entre as coordenadas X e Y que não estavam originalmente presentes nos dados. Por exemplo, o modelo de decisão de caminho de um robô é mais intuitivo em coordenadas polares do que cartesiano. A dependência do vetor de velocidade do robô nas coordenadas polares entre o ângulo e a magnitude pode até não existir ou ser diferente da dependência das coordenadas cartesianas.

Uma solução alternativa que descobri para continuar trabalhando com coordenadas polares é criar uma função de erro personalizada para calcular a diferença de ângulo usando a função angdiff () no MATLAB e a diferença de magnitude, como de costume.

Esta função retorna '0' para a diferença entre -pi e pi. Aqui está um link para a página de suporte de funções no site Mathworks.

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

Se você estiver usando a ativação do Sigmoid e seus dados de ângulos forem normalizados entre [0,1], você deve retorná-los ao intervalo [-pi, pi] antes de usar a função angdiff () e normalizar o erro de volta para [0,1 ] para o processo de retropropagação.

Além disso, a função equivalente no Python seria:

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))

Isso retorna resultados semelhantes à função MATLAB e também funciona com matrizes:

[ 1.5708 -0.7854 -3.1416]
0.0
0.0

Espero que ajude.

Stav Bar-Sheshet
fonte