Introducción
Encuentro esta pregunta realmente interesante, supongo que alguien ha publicado un documento sobre ella, pero es mi día libre, así que no quiero ir a buscar referencias.
Entonces podríamos considerarlo como una representación / codificación de la salida, lo que hago en esta respuesta. Sigo pensando que hay una mejor manera, donde puedes usar una función de pérdida ligeramente diferente. (Quizás la suma de las diferencias al cuadrado, utilizando el módulo de resta 2 ).π
Pero en adelante con la respuesta real.
Método
Propongo que un ángulo se represente como un par de valores, su seno y su coseno.θ
Entonces, la función de codificación es:
y la función de decodificación es: Para arctan2 siendo las tangentes inversas, preservando la dirección en todos los cuadrantes)θ ↦ ( pecado( θ ) , cos( θ ) )
( y1, y2) ↦ arctan2 ( y1, y2)
En teoría, podría trabajar de manera equivalente directamente con los ángulos si su herramienta utiliza el soporte atan2
como una función de capa (tomando exactamente 2 entradas y produciendo 1 salida).
TensorFlow hace esto ahora y admite el descenso de gradiente en él , aunque no está destinado para este uso. Investigué el uso out = atan2(sigmoid(ylogit), sigmoid(xlogit))
con una función de pérdida min((pred - out)^2, (pred - out - 2pi)^2)
. Descubrí que entrenaba mucho peor que usar outs = tanh(ylogit), outc = tanh(xlogit))
con una función de pérdida 0.5((sin(pred) - outs)^2 + (cos(pred) - outc)^2
. Lo cual creo que puede atribuirse a que el gradiente es discontinuo paraatan2
Mi prueba aquí lo ejecuta como una función de preprocesamiento
Para evaluar esto, definí una tarea:
Dada una imagen en blanco y negro que representa una sola línea sobre un fondo en blanco, indique en qué ángulo se encuentra esa línea con el "eje x positivo"
Implementé una función para generar aleatoriamente estas imágenes, con líneas en ángulos aleatorios (NB: las versiones anteriores de esta publicación usaban pendientes aleatorias, en lugar de ángulos aleatorios. Gracias a @Ari Herman por señalarlo. Ahora está arreglado). Construí varias redes neuronales para evaluar su desempeño en la tarea. Los detalles completos de la implementación se encuentran en este cuaderno Jupyter . El código está todo en Julia , y uso la biblioteca de red neuronal Mocha .
A modo de comparación, lo presento contra los métodos alternativos de escalado a 0,1. y para poner en 500 contenedores y usar softmax de etiqueta blanda. No estoy particularmente contento con el último, y siento que necesito modificarlo. Es por eso que, a diferencia de los otros, solo lo pruebo por 1,000 iteraciones, frente a los otros dos que se ejecutaron por 1,000 y por 10,000
Configuración experimental
Las imágenes eran píxeles, con la línea en el centro y hacia el borde. No había ruido, etc. en la imagen, solo una línea "negra", sobre un fondo blanco.101 × 101
Para cada recorrido, se generaron 1,000 entrenamientos y 1,000 imágenes de prueba al azar.
La red de evaluación tenía una sola capa oculta de ancho 500. Se usaron neuronas sigmoideas en la capa oculta.
Fue entrenado por Stochastic Gradient Decent, con una tasa de aprendizaje fija de 0.01 y un impulso fijo de 0.9.
No se usó regularización o abandono. Tampoco hubo ningún tipo de convolución, etc. Una red simple, que espero sugiera que estos resultados se generalizarán
Es muy fácil modificar estos parámetros en el código de prueba , y animo a las personas a que lo hagan. (y busca errores en la prueba).
Resultados
Mis resultados son los siguientes:
| | 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% |
Cuando me refiero al error, este es el valor absoluto de la diferencia entre el ángulo de salida de la red neuronal y el ángulo verdadero. Entonces, el error medio (por ejemplo) es el promedio sobre los 1,000 casos de prueba de esta diferencia, etc. No estoy seguro de que no deba volver a escalarlo haciendo un error de decir ser igual a un error de ). π7 π4 4π4 4
También presento la precisión en varios niveles de granularidad. La precisión es la parte de los casos de prueba que se corrigió. Entonces accuracy_to_point01
significa que se contó como correcto si la salida estaba dentro de 0.01 del ángulo verdadero. Ninguna de las representaciones obtuvo resultados perfectos, pero eso no es sorprendente dado el funcionamiento de las matemáticas de coma flotante.
Si echas un vistazo al historial de esta publicación, verás que los resultados tienen un poco de ruido, ligeramente diferente cada vez que lo vuelvo a ejecutar. Pero el orden general y la escala de valores siguen siendo los mismos; lo que nos permite sacar algunas conclusiones.
Discusión
Binning con softmax es, con mucho, el peor, ya que dije que no estoy seguro de no haber estropeado algo en la implementación. Sin embargo, funciona marginalmente por encima de la tasa de conjetura. si solo estuviera adivinando, obtendríamos un error medio deπ
La codificación sin / cos funciona significativamente mejor que la codificación 0-1 escalada. La mejora es en la medida en que con 1,000 iteraciones de entrenamiento, sin / cos se está desempeñando aproximadamente 3 veces mejor en la mayoría de las métricas que la escala en 10,000 iteraciones.
Creo que, en parte, esto está relacionado con la mejora de la generalización, ya que ambos obtuvieron un error cuadrático medio bastante similar en el conjunto de entrenamiento, al menos una vez que se ejecutaron 10,000 iteraciones.
Ciertamente, existe un límite superior para el mejor rendimiento posible en esta tarea, dado que el ángulo puede ser más o menos cualquier número real, pero no todos esos ángeles producen líneas diferentes con una resolución de píxeles. Entonces, dado que, por ejemplo, los ángulos 45.0 y 45.0000001 están vinculados a la misma imagen con esa resolución, ningún método obtendrá ambos perfectamente correctos.101 × 101
También parece probable que en una escala absoluta para ir más allá de este rendimiento, se necesite una mejor red neuronal. En lugar del muy simple descrito anteriormente en la configuración experimental.
Conclusión.
Parece que la representación sin / cos es, con mucho, la mejor de las representaciones que investigué aquí. Esto tiene sentido, ya que tiene un valor uniforme a medida que te mueves alrededor del círculo. También me gusta que lo inverso se pueda hacer con arctan2 , que es elegante.
Creo que la tarea presentada es suficiente en su capacidad para presentar un desafío razonable para la red. Aunque supongo que realmente está aprendiendo a hacer un ajuste de curva para así que tal vez sea demasiado fácil. Y quizás peor, puede estar favoreciendo la representación pareada. No creo que sea así, pero se está haciendo tarde aquí, así que podría haber perdido algo. Te invito nuevamente a que revises mi código . Sugerir mejoras o tareas alternativas.F( x ) = y1y2X