2010-02-10 22 views
24

Estoy escribiendo un programa de aprendizaje automático bastante complicado para mi tesis en visión artificial. Está funcionando bastante bien, pero necesito seguir probando cosas nuevas y agregando nuevas funcionalidades. Esto es problemático porque a veces introduzco errores cuando estoy ampliando el código o tratando de simplificar un algoritmo.Unit Testing Machine Learning Code

Claramente, lo correcto es agregar pruebas unitarias, pero no está claro cómo hacerlo. Muchos componentes de mi programa producen una respuesta algo subjetiva, y no puedo automatizar los controles de cordura.

Por ejemplo, tenía un código que aproximaba una curva con una curva de menor resolución, por lo que podía hacer un trabajo computacionalmente intensivo en la curva de menor resolución. Accidentalmente introduje un error en este código, y solo lo encontré mediante una minuciosa búsqueda cuando mis resultados de todo mi programa empeoraron un poco.

Pero, cuando traté de escribir un test de unidad para él, no estaba claro qué debería hacer. Si hago una curva simple que tiene una versión claramente de menor resolución, entonces realmente no estoy probando todo lo que podría salir mal. Si hago una curva simple y luego perturbo ligeramente los puntos, mi código comienza a producir respuestas diferentes, a pesar de que esta pieza particular de código realmente parece funcionar bien ahora.

Respuesta

8

Puede que no aprecie la ironía, pero básicamente lo que tiene allí es código heredado: un pedazo de software sin ninguna prueba unitaria. Naturalmente, no sabes por dónde empezar. Por lo tanto, puede resultar útil leer sobre el manejo del código heredado.

El pensamiento definitivo en esto es el libro de Michael Feather, Trabajando eficazmente con Legacy Code. Solía ​​haber un resumen útil de eso en el sitio ObjectMentor, pero lamentablemente el sitio web ha seguido el camino de la empresa. Sin embargo, WELC ha dejado un legado en reseñas y otros artículos. Check them out (or just buy the book), aunque las lecciones clave son las que S.Lott y tvanfosson cubren en sus respuestas.

+0

Este es realmente el consejo más útil. Toda mi depuración exitosa ha resultado del uso manual de técnicas como esta. Pero este PDF brinda buenos consejos para automatizar el proceso. Su enlace PDF no funcionó para mí, pero un simple google lo ubicó. – forefinger

+0

@forefinger - He arreglado el enlace. Pero me alegro de que hayas encontrado el artículo, y me pareció útil. – APC

+2

El autor de ese PDF tiene una excelente publicación sobre el mismo tema: http://www.amazon.com/Working-Effectively-Legacy-Michael-Feathers/dp/0131177052 – TrueWill

11

Sin ver su código, es difícil de decir, pero sospecho que está intentando escribir pruebas en un nivel demasiado alto. Es posible que desee pensar en dividir sus métodos en componentes más pequeños que sean deterministas y probarlos. Luego pruebe los métodos que utilizan estos métodos proporcionando implementaciones simuladas que devuelven valores predecibles de los métodos subyacentes (que probablemente se encuentren en un objeto diferente). Luego puede escribir pruebas que cubran el dominio de varios métodos, asegurándose de que tiene cobertura de la gama completa de resultados posibles. Para los métodos pequeños, lo hace al proporcionar valores que representan el dominio de las entradas. Para los métodos que dependen de estos, proporcionando implementaciones simuladas que devuelven el rango de resultados de las dependencias.

+0

Este consejo fue útil. En el caso de ejemplo, estoy haciendo la aproximación a través de un programa dinámico. Esto se puede descomponer en varios componentes que son deterministas: 1. calcular el error de una pieza particular de la aproximación. Puedo hacer esto a mano para alguna curva particular. 2. Asegurarse de que la función objetivo general sea correcta. De nuevo, puedo hacerlo a mano. 3. Asegúrese de que el programa dinámico sea correcto. (Aquí es donde estaba el error.) – forefinger

+1

Si sé que la función objetivo general es correcta, puedo probar esto alimentando curvas simplemente descomponibles que han sido perturbadas. Siempre que la respuesta me dé puntajes mejores que la respuesta que pensé que quería, el programa dinámico probablemente esté funcionando correctamente. – forefinger

11

"entonces realmente no estoy probando todo lo que podría salir mal".

Correcto.

El trabajo de pruebas de unidad es no para probar todo lo que podía salir mal.

El trabajo de las pruebas unitarias es probar que lo que tiene hace derecho cosa, dadas las entradas específicas y los resultados específicos esperados. La parte importante aquí es el específico visible, los requisitos externos son satisfechos por casos de prueba específicos. No es que todas las cosas posibles que podrían salir mal se hayan evitado de alguna manera.

Nada puede probar todo lo que podría salir mal. Puede escribir una prueba, pero le será difícil escribir pruebas para todo.

Elija sabiamente sus casos de prueba.

Además, el trabajo de las pruebas unitarias es probar que cada pequeña parte de la aplicación general hace lo correcto, de forma aislada.

Su "código que aproximaba una curva con una curva de menor resolución", por ejemplo, probablemente tiene varias piezas pequeñas que pueden probarse como unidades separadas. Aisladamente. El todo integrado también podría probarse para asegurarse de que funciona.

Su "trabajo computacionalmente intensivo en la curva de baja resolución", por ejemplo, probablemente tenga varias piezas pequeñas que se pueden probar como unidades separadas. Aisladamente.

Ese punto de prueba unitario es crear unidades pequeñas y correctas que luego se ensamblan.

+0

Esto parece un consejo razonable, pero no ayuda tanto con mis problemas específicos como con algunas de las otras respuestas. – forefinger

+0

"problemas específicos"? No es fácil de entender, ya que su pregunta no parecía incluir ningún problema específico. Siéntase libre de actualizar su pregunta si desea más información. –

1

Generalmente, para las medidas estadísticas construiría en un épsilon su respuesta. ES DECIR. la diferencia cuadrada media de sus puntos sería < 0.01 o algo así. Otra opción es ejecutar varias veces y si falla "con demasiada frecuencia", entonces usted tiene un problema.

6

Las pruebas de su unidad necesitan emplear algún tipo de factor de fumo , ya sea aceptando aproximaciones o usando algún tipo de comprobaciones probabilísticas.

Por ejemplo, si tiene alguna función que devuelve un resultado de coma flotante, es casi imposible escribir una prueba que funcione correctamente en todas las plataformas. Sus cheques necesitarían realizar la aproximación.

TEST_ALMOST_EQ(result, 4.0); 

encima TEST_ALMOST_EQ podría comprobar que result es entre 3,9 y 4,1 (por ejemplo).

Alternativamente, si los algoritmos de aprendizaje de la máquina son probabilísticos, sus pruebas deberán acomodarse tomando el promedio de varias ejecuciones y esperando que estén dentro de cierto rango.

x = 0; 
for (100 times) { 
    x += result_probabilistic_test(); 
} 

avg = x/100; 
TEST_RANGE(avg, 10.0, 15.0); 

Por supuesto, las pruebas son no determinista, por lo que tendrá que ajustarlos de manera que se puede obtener pruebas no escamosa, con una alta probabilidad. (Por ejemplo, aumentar el número de intentos o aumentar el rango de error).

También puede usar los simulacros para esto (por ejemplo, un generador de números aleatorios falso para sus algoritmos probabilísticos), y generalmente ayudan a determinar determinísticamente rutas de código específicas, pero son un gran esfuerzo para mantener. Idealmente, usaría una combinación de prueba difusa y se burlaría.

HTH.

+0

Este es un buen consejo, pero en realidad no resuelve mis problemas, porque muchas de las cosas que necesito verificar son discretas, por lo que no se puede calcular el error y no se pueden promediar de manera significativa. – forefinger

0
  1. Obtener un conjunto de datos de prueba adecuado (tal vez un subconjunto de lo que su uso por lo general)
  2. calcular algunas métrica en este conjunto de datos (por ejemplo,la exactitud)
  3. Anote el valor obtenido (validación cruzada)
  4. Esto debería dar una indicación de lo que para establecer el umbral para

Por supuesto, si puede ser que al realizar cambios en el código de la el rendimiento en el conjunto de datos aumentará un poco, pero si alguna vez disminuye en gran medida, esto sería una indicación de que algo va mal.