Introdução
Acho essa pergunta realmente interessante, suponho que alguém tenha publicado um trabalho sobre ela, mas é meu dia de folga, então não quero ir atrás de referências.
Portanto, poderíamos considerá-lo como uma representação / codificação da saída, o que faço nesta resposta. Eu continuo pensando que existe uma maneira melhor, onde você pode simplesmente usar uma função de perda ligeiramente diferente. (Talvez soma das diferenças ao quadrado, usando o módulo de subtração 2 ).π
Mas em diante com a resposta real.
Método
Proponho que um ângulo seja representado como um par de valores, seu seno e seu cosseno.θ
Portanto, a função de codificação é:
e a função de decodificação é: Para o arctan2 sendo as tangentes inversas, preservando a direção em todos os quadrantes)θ ↦ ( sin( θ ) , cos( θ ) )
( y1, y2) ↦ arctan2 ( y1, y2)
Em teoria, você poderia trabalhar de forma equivalente diretamente com os ângulos se sua ferramenta usar suporte atan2
como uma função de camada (recebendo exatamente 2 entradas e produzindo 1 saída).
O TensorFlow faz isso agora e oferece suporte à descida gradiente , embora não seja destinado a esse uso. Eu investiguei usando out = atan2(sigmoid(ylogit), sigmoid(xlogit))
uma função de perda min((pred - out)^2, (pred - out - 2pi)^2)
. Descobri que ele treinava muito pior do que usar outs = tanh(ylogit), outc = tanh(xlogit))
com uma função de perda 0.5((sin(pred) - outs)^2 + (cos(pred) - outc)^2
. Que eu acho que pode ser atribuído ao gradiente ser descontínuo poratan2
Meu teste aqui o executa como uma função de pré-processamento
Para avaliar isso, defini uma tarefa:
Dada uma imagem em preto e branco que representa uma única linha em um plano de fundo em branco, indique qual é o ângulo dessa linha no "eixo x positivo"
Eu implementei uma função gerar essas imagens aleatoriamente, com linhas em ângulos aleatórios (NB: as versões anteriores deste post usavam inclinações aleatórias, em vez de ângulos aleatórios. Agradecemos a @Ari Herman por apontar isso. Agora está corrigido). Eu construí várias redes neurais para avaliar o desempenho da tarefa. Os detalhes completos da implementação estão neste caderno Jupyter . O código está todo em Julia e eu uso a biblioteca de rede neural Mocha .
Para comparação, apresento-o contra os métodos alternativos de escala para 0,1. e colocar em 500 escaninhos e usar softmax com etiqueta macia. Não estou particularmente feliz com o último e sinto que preciso ajustá-lo. É por isso que, diferentemente dos outros, eu apenas o testo por 1.000 iterações, contra os outros dois que foram executados por 1.000 e por 10.000
Configuração Experimental
As imagens eram pixels, com a linha voltada para o centro e indo para a borda. Não havia ruído, etc. na imagem, apenas uma linha "preta", sobre fundo branco.101 × 101
Para cada trilha, 1.000 treinamentos e 1.000 imagens de teste foram geradas aleatoriamente.
A rede de avaliação tinha uma única camada oculta de largura 500. Neurônios sigmóides foram usados na camada oculta.
Foi treinado por Decocção de Gradiente Estocástico, com uma taxa de aprendizado fixa de 0,01 e um momento fixo de 0,9.
Não houve regularização ou abandono. Nem houve qualquer tipo de convolução, etc. Uma rede simples, que espero sugira que esses resultados generalizem
É muito fácil ajustar esses parâmetros no código de teste , e eu encorajo as pessoas a fazer isso. (e procure por erros no teste).
Resultados
Meus resultados são os seguintes:
| | 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% |
Onde me refiro ao erro, esse é o valor absoluto da diferença entre o ângulo de saída da rede neural e o ângulo real. Portanto, o erro médio (por exemplo) é a média dos 1.000 casos de teste dessa diferença, etc. Não tenho certeza de que não devo escalá-lo novamente, cometendo um erro de dizer igual para um erro de ). π7 π4π4
Apresento também a precisão em vários níveis de granularidade. A precisão é a parte dos casos de teste que foi corrigida. Isso accuracy_to_point01
significa que foi contado como correto se a saída estivesse dentro de 0,01 do ângulo real. Nenhuma das representações obteve resultados perfeitos, mas isso não surpreende, dado o funcionamento da matemática de ponto flutuante.
Se você der uma olhada no histórico deste post, verá que os resultados têm um pouco de ruído para eles, um pouco diferente a cada vez que o executo. Mas a ordem geral e a escala de valores permanecem as mesmas; permitindo assim tirar algumas conclusões.
Discussão
Binning com softmax tem um desempenho de longe o pior, pois eu disse que não tenho certeza de que não estraguei nada na implementação. Porém, ele apresenta um desempenho ligeiramente acima da taxa de estimativa. se apenas estivéssemos supondo que estaríamos recebendo um erro médio deπ
A codificação sin / cos tem um desempenho significativamente melhor que a codificação em escala 0-1. A melhoria é na medida em que, em 1.000 iterações de treinamento, o sin / cos está apresentando um desempenho três vezes melhor na maioria das métricas do que o dimensionamento em 10.000 iterações.
Eu acho que, em parte, isso está relacionado à melhoria da generalização, pois ambos estavam obtendo um erro quadrático médio bastante semelhante no conjunto de treinamento, pelo menos uma vez que 10.000 iterações foram executadas.
Certamente, existe um limite superior para o melhor desempenho possível nessa tarefa, já que o ângulo pode ser mais ou menos qualquer número real, mas nem todos esses anjos produzem linhas diferentes na resolução de pixels. Portanto, como, por exemplo, os ângulos 45.0 e 45.0000001 estão vinculados à mesma imagem nessa resolução, nenhum método jamais obterá os dois perfeitamente corretos.101 × 101
Também parece provável que, em uma escala absoluta, para ir além desse desempenho, seja necessária uma rede neural melhor. Em vez do muito simples descrito acima na configuração experimental.
Conclusão.
Parece que a representação sin / cos é de longe a melhor das representações que investiguei aqui. Isso faz sentido, pois tem um valor suave à medida que você se move ao redor do círculo. Também gosto que o inverso possa ser feito com o arctan2 , que é elegante.
Acredito que a tarefa apresentada seja suficiente em sua capacidade de apresentar um desafio razoável para a rede. Embora eu ache que realmente esteja apenas aprendendo a fazer o ajuste de curva para , talvez seja muito fácil. E talvez pior ainda, esteja favorecendo a representação emparelhada. Eu não acho que seja, mas está ficando tarde aqui, então talvez eu tenha perdido algo que eu convido você novamente a examinar meu código . Sugira melhorias ou tarefas alternativas.f( x ) = y1y2x