9 votos

¿Cómo se pasa del papel al código?

Mi formación es en biología computacional, aunque soy biólogo de formación. Acabo de empezar mi doctorado, y aunque tengo un gran interés/curiosidad por la Bioestadística Computacional/Epidemiología y el desarrollo de métodos, estoy bastante seguro de que no tendré que desarrollar e implementar nuevos métodos. Pero, como ya he dicho, siento curiosidad por ello.

Más concretamente, sigo sin entender cómo alguien pasa de un trabajo de investigación a, por ejemplo, un R paquete. Me resulta muy difícil, no sólo porque quizás no tengo la comprensión teórica adecuada, sino también porque me resulta especialmente difícil programar métodos estadísticos. Sé codificar, codifico bastante, pero nunca he tenido que implementar un método a partir del documento que lo presenta.

Por poner un ejemplo, hace poco me encontré con este documento y aparentemente alguien lo implementó en R aquí (archivo denominado focus_1.0.0.tar.gz ). Sinceramente, no sabría ni por dónde empezar. ¿Qué tipo de modelo mental hay que tener para poder traducir un marco de este tipo en código?

9voto

John Richardson Puntos 1197

En mi caso, repaso las matemáticas hasta que las entiendo (y puedo identificar los métodos para que el álgebra lineal sea eficiente y estable). A menudo, esto implica volver a escribir las matemáticas en los pasos más pequeños posibles (como un documento LaTeX). Esto suele ser útil como documento de mantenimiento: si vuelves al software dentro de un año, probablemente no recordarás exactamente cómo funciona, y una derivación matemática paso a paso (con anotaciones que expliquen su problemas con él) es una forma rápida de volver a recordarlo. Por lo general, en la programación la principal dificultad no es la codificación (por ejemplo, averiguar cómo decirle al ordenador que haga algo), sino la resolución/comprensión del problema (averiguar exactamente qué es lo que quieres que haga el ordenador).

El siguiente paso es identificar los componentes del algoritmo/método que se pueden probar individualmente. No intentes implementarlo todo de una sola vez. Me gusta bastante la cita de Heinlein "cuando te enfrentes a un problema que no entiendes, haz cualquier parte que entiendas, y luego vuelve a mirarlo".

También es una buena idea leer el código de otras personas y averiguar por qué lo han implementado de la forma en que lo han hecho, y mirar cómo se ha estructurado el código en el caso de paquetes que son utilizados por otras personas. La documentación también es muy importante: si quieres que la gente utilice tu código, tienes que hacerlo lo más fácil posible, así que minimiza las dependencias (por ejemplo, de paquetes de terceros), haz que la interfaz de usuario o la API sea sencilla y coherente con las expectativas, y proporciona una buena documentación. Esto requiere mucho tiempo/esfuerzo que la mayoría de nosotros (incluido yo) no parece poder dedicar ;o)

8voto

microhaus Puntos 151

Para complementar Dikran Marsupial's respuesta, lo siguiente es una articulación de un proceso que utilizo personalmente. Esto es desde la perspectiva de la codificación de algoritmos de aprendizaje automático para la investigación, más que desde una perspectiva de ingeniería de software a nivel de producción.

Principalmente, a menudo he encontrado útil ver el proceso de codificación de un algoritmo de aprendizaje automático desde cero como la instanciación de una idea matemática:

Etapa matemática, pre-algorítmica.

Normalmente, en el aprendizaje automático se utiliza algún tipo de principio matemático o formal para guiar el desarrollo del algoritmo. Puede ser estadístico, por ejemplo, la estimación de máxima probabilidad, y lo más probable es que implique cierto grado de optimización. Por lo general, tendrá que hacer una buena cantidad de trabajo de derivación matemática, hasta que tenga lo que necesita.

Personalmente, he comprobado que se necesita tanto una sólida comprensión de los principios matemáticos que operan aquí, como seguridad absoluta en la corrección de las propias derivaciones. Cualquier confusión o incertidumbre en esta fase se agravará durante la aplicación, cuando uno tiene que preocuparse además de la depuración. Antes de pasar a la siguiente fase, suelo utilizar un paquete de cálculo simbólico o un sistema de álgebra computacional, como Mathematica para comprobar todas mis derivaciones, porque incluso un solo error en esta etapa puede llevar a la situación extremadamente indeseable de tener que buscar errores de derivación y/o depuración.

Algorítmica.

Una vez que he comprobado que mis derivaciones matemáticas son herméticas, sin errores, y que estoy en un nivel en el que puedo especificar y esbozar mi algoritmo en papel, procedo a redactar el pseudocódigo formal. Al igual que el cartel anterior, me parece que obligarse a escribirlo con algorithm2e en LaTeX ayuda en el sentido de que te compromete de una manera que no lo hace el trabajar únicamente con lápiz y papel.

Durante esta fase de redacción del pseudocódigo, empiezo a pensar en cuestiones más relacionadas con la implementación, como adaptar mis derivaciones para que sean lo más vectorizadas posible, es decir, utilizar el menor número de bucles posible, o hacer uso de las técnicas de álgebra lineal en la medida de lo posible. Sin embargo, la preocupación por la implementación se limita a cumplir los consejos básicos y los principios estándar del diseño de algoritmos, pero no a optimizar el código.

La puesta en práctica.

Implementa el pseudocódigo en el código. Cuanto más claro sea tu pseudocódigo y más consistente sea tu notación, más fácil será esto. Algo que probablemente recogerás por ti mismo es que vale mucho la pena, como ha dicho el cartel anterior, asegurarse de que estás escribiendo código modular . Divida la escritura del código en pequeños trozos, que podrá probar individualmente.

En la mayoría de los casos, casi siempre habrá comprobaciones de cordura muy básicas que puedes hacer. Por ejemplo, si su código utiliza gradientes, puede utilizar la diferenciación finita para evaluar si se ha calculado correctamente. Si está implementando un algoritmo EM, su log-verosimilitud aumentará monotónicamente, por lo que cualquier disminución es probablemente un error de codificación, siempre que sus derivaciones sean correctas.

Optimización.

No quiero decir mucho sobre esto, excepto que puede volverse profundo muy rápidamente. En esta fase, se cronometra el código, o se utiliza toda una serie de trucos de álgebra lineal numérica, cálculo de matrices y optimización convexa para exprimir el mayor número posible de mejoras de rendimiento.


Con respecto al uso de herramientas para depurar, no sé mucho sobre las herramientas de depuración en R ya que es algo con lo que recién me estoy familiarizando. Sin embargo, parece que el depurador de RStudio es similar al de pdb en Python.

En el caso de Python, el PyCharm El IDE es un ejemplo en el que he descubierto que las herramientas pueden mejorar enormemente el proceso de depuración. Usted puede caminar a través de cada línea de su código mientras se ejecuta, y paso a paso, paso a cabo, hacer evaluaciones de lado a lado, así como realizar un seguimiento de todos los estados de las variables a la vez. Personalmente, encuentro que los medios visuales para acceder a todos los estados de las variables a la vez son muy superiores a hacer consultas manuales en la línea de comandos utilizando pdb . Aunque me gustaría haberlo sabido antes, insisto en que esto es sólo para consideración, y no tengo ninguna afiliación con JetBrains.

Algunas cosas finales. Como la mayoría de las cosas en la vida, se mejora con la práctica. Hay muchos documentos acompañados de código en paperswithcode.com y también hay un Reto de reproducibilidad del papel ML también.

Por último, si alguna vez te das cuenta de que la depuración está empezando a machacarte un poco el alma, he descubierto que ayuda pensar en los días en que la gente solía usar tarjetas perforadas para implementar algoritmos...

8voto

william e emba Puntos 1

Más concretamente, sigo sin entender cómo alguien pasa de un trabajo de investigación a, por ejemplo, un paquete R.

Como muchas cosas en la vida, a veces lo más difícil es empezar. Mucha gente se prepara para el fracaso pensando que no está lo suficientemente preparada y que va a fracasar, así que se rinde antes de intentarlo.

Dicho esto, hay que saber al menos un poco para empezar. En el contexto de la escritura de paquetes de R basados en un documento, no necesariamente tienes que estar cómodo con las matemáticas o la teoría detrás del tema para empezar. Es posible que el proceso de escribir el paquete te ayude a desarrollar esa comprensión más profunda que eventualmente quieres tener, y que con el tiempo harás cambios en el paquete que reflejen eso. En realidad tengo un paquete R muy robusto y fácil de usar en el que empecé en esta situación. Tengo mucha experiencia en programación, y alguien que conozco tenía esta teoría no trivial que había estado desarrollando pero que no tenía la capacidad de implementar adecuadamente en un paquete, así que me pidió ayuda. Los detalles de la matemática y la teoría eran en su mayoría desconocidos para mí, ya que mi experiencia está en otra parte. Ni siquiera conocía los detalles de la creación de un paquete de R en ese momento. Pero ahora, después de haber pasado por el proceso de creación del paquete, estoy en un punto en el que incluso soy capaz de hacer mis propias contribuciones y avances a la teoría y sus aplicaciones.

Para empezar, lo que hay que hacer es pensar en lo que los usuarios podrían querer en un paquete. ¿Qué presenta el documento que los usuarios potenciales encontrarán útil? Esto requiere al menos una comprensión de alto nivel de lo que el documento está logrando; presumiblemente, usted no estaría tratando de escribir un paquete para un documento si no tuviera idea de lo que el documento se trata. Así que haz una lista de las cosas que querrán los usuarios. Ahora asigne estos elementos a los nombres de las funciones sin preocuparse por los detalles de cómo funcionan internamente. Por ejemplo, puede tener una función que haga un análisis estadístico básico, otra que calcule los intervalos de confianza, otra que calcule las varianzas y quizás otra que haga una visualización básica.

A continuación, ¿cuáles son los insumos más fundamentales que necesita para estas funciones? Puede que haya un montón de pequeñas cosas que sean útiles para la comodidad, el ajuste o la flexibilidad de la función, pero éstas no son importantes todavía. Lo importante es conseguir que la funcionalidad básica funcione; puedes volver y tratar los detalles más finos más tarde para que no te abrumen ahora.

Ahora bien, ¿qué valores devuelven estas funciones? En otras palabras, ¿qué es lo que un usuario espera o encuentra más útil en términos de resultados? Parte de esto puede acabar dando forma o siendo moldeado por el punto anterior sobre las entradas. Si tienes una función que se basa en el resultado de otra, entonces la entrada de una va a ser la salida de otra. Así que, dependiendo de la situación, podrías tener una función que devuelva cosas simples como un número o un vector, y podrías tener otra función que devuelva cosas complejas como una lista o un objeto de clase.

Una vez que tienes una lista de las funciones que quieres, lo que necesitan para las entradas, y lo que producen, ahora tienes lo que necesitas para un paquete de esqueleto básico. Así que empieza a codificar creando un montón de funciones vacías. O tal vez sólo haga que impriman algo. Si nunca has escrito un paquete antes, este es un buen momento para intentar construirlo e instalarlo (localmente en tu ordenador) para asegurarte de que funciona. Mientras trabajas, construye, instala, carga y prueba periódicamente tu paquete para asegurarte de que todo hace lo que crees que hace.

Por último, con la configuración de un paquete esqueleto, ahora puede empezar a profundizar en los detalles matemáticos/teóricos. Tus funciones actúan como guía para ayudarte a desglosar esta tarea para que no intentes abrumarte centrándote en todo a la vez. Céntrate en una sola cosa y trabaja en conseguir los fundamentos de la función. No deberías preocuparte en absoluto por el rendimiento en este punto. Lo mismo ocurre con otras cosas como los casos extraños con los datos de entrada. Deberías centrarte en conseguir que las cosas se implementen de la manera más sencilla posible. Una vez que todo está funcionando y usted tiene una mejor sensación de todo, entonces usted puede revisar.

Con el tiempo, comprenderás mejor qué es lo que quieres conseguir con tu paquete. Puede que te des cuenta de que hay funciones que necesitas y en las que no habías pensado antes, o tal vez haya que combinar un par de funciones. Incluso puede mirar hacia atrás y darse cuenta de que hay una manera completamente diferente de hacer las cosas que es mejor. En el caso de mi paquete, la v0.1.0 (esencialmente una beta) y la v1.0.0 (primera versión de producción) son muy diferentes. Reescribí completamente el paquete desde cero para la v1.0.0. La v0.1.0 fue genial para aprender, no sólo las matemáticas y la teoría, sino cómo se sentían las decisiones de mi marco de trabajo tanto como usuario como mantenedor del paquete. Resulta que era torpe y desagradable para ambos, y valía la pena invertir en una revisión completa. Así que no te preocupes por hacerlo perfecto de inmediato; tómate tu tiempo en la fase de pre-lanzamiento para probar cosas, y preferiblemente da a algunos usuarios potenciales la oportunidad de probarlo y dar su opinión también. No sabes cuántas veces los datos del mundo real de otras personas sacan a la luz casos extraños que no habías considerado...

Algunos consejos adicionales:

  • Deuda técnica: a veces hay situaciones en las que hay múltiples maneras de hacer una tarea. Antes de publicar tu paquete, tienes que pensar con antelación lo que significan estas opciones, porque si te limitas a tomar el camino más fácil, puedes encontrarte con que te cuesta un montón de tiempo más tarde para trabajar. Es mejor ocuparse de esto antes de publicar porque quieres evitar romper cosas para los usuarios una vez que empiecen a usar el paquete. Hay toneladas de artículos sobre la "deuda técnica". Dedica algo de tiempo a leer algunos de ellos.
  • Carga de mantenimiento: Si tiene la intención de crear un paquete de R, comprométase a mantenerlo. Un gran problema que he visto con el software científico es que muchas personas publican software/paquetes como una tarea única para una publicación y luego lo dejan pudrirse, lo que en última instancia cuesta a otros mucho tiempo y energía cuando las cosas van mal con el software para ellos. Esperemos que usted no sea uno de ellos. Para ello, tienes que ser consciente de cómo tus elecciones al diseñar el paquete van a afectar a tus compromisos de tiempo más adelante. Cada pequeña cosa que añades es una cosa más que puede romperse, que necesita ser probada, que requiere soporte para los usuarios, etc., lo que lleva a dedicar más tiempo a mantener tu paquete en lugar de otras cosas que quieres hacer. Por lo tanto, hay que ser juicioso con lo que se añade y con lo que se elimina de la lista de características; si se intenta añadir todas las pequeñas comodidades que la gente quiere, se lamentará más tarde. Lo mismo se aplica a la hora de pensar qué paquetes externos necesitas para tu paquete; cada dependencia que añadas es otra oportunidad para que un cambio que alguien haga en su paquete rompa el tuyo.
  • Para publicar un paquete de R, póngalo en CRAN. No puedo decirte cuántas veces he mirado el repo de GitHub para paquetes que no están en CRAN (con publicaciones revisadas por pares) sólo para encontrar defectos fundamentales que las políticas "molestas" de CRAN habrían evitado. En serio, un paquete que miré cambió múltiples comportamientos predeterminados de R de los que dependen implícitamente muchos otros paquetes comunes. Simplemente cargando el paquete, podía alterar completamente el comportamiento de otros paquetes de una manera que era completamente silenciosa con cambios no obvios en los resultados, y sólo podía arreglarse fácilmente reiniciando R (y no cargando nunca más el paquete). Los comandos que este paquete utilizaba para hacer esto están explícitamente prohibidos en los paquetes de CRAN. Si el autor hubiera intentado publicar en CRAN, lo habrían detectado (o tal vez lo intentaron y decidieron que no valía la pena arreglarlo sólo para estar en CRAN...)
  • Utilizar pruebas automatizadas (para R, véase el paquete 'testthat'). Esto debería ser un requisito para cualquier paquete. Desgraciadamente, el hecho de que un paquete tenga pruebas unitarias no significa que se hagan bien. Puede ser un poco una forma de arte averiguar cómo hacerlo bien.
  • Es necesaria una buena documentación. Y no me refiero sólo a las típicas referencias a las funciones de R. Creo que la mayoría de la gente se beneficia de tener una variedad de tutoriales/viñetas que expliquen los conceptos y den ejemplos. Intentar escribirlos contribuirá realmente a tu propia comprensión de las matemáticas y la teoría, o al menos te dará confianza en lo que has aprendido. Para R, recomiendo mirar el paquete 'pkgdown' para esto; te ayuda a construir un sitio web para tu paquete que está alojado como parte del repo de GitHub de tu paquete.

Algunas palabras de despedida : Si vas a escribir software para científicos, ten en cuenta lo importante que es esta responsabilidad. Si realmente te dedicas y lo haces bien, podrías marcar una gran diferencia en la capacidad de muchas personas para hacer su investigación de manera más eficiente y eficaz. Pero si lo dejas a medias o eres perezoso a la hora de mantenerlo, podrías fastidiar a mucha gente y costarles mucho tiempo, energía y potencialmente dinero. En cualquier caso, tienes el potencial de tener un impacto enorme en la ciencia que normalmente no tendrías si te quedas en tu pequeña burbuja de investigación.

i-Ciencias.com

I-Ciencias es una comunidad de estudiantes y amantes de la ciencia en la que puedes resolver tus problemas y dudas.
Puedes consultar las preguntas de otros usuarios, hacer tus propias preguntas o resolver las de los demás.

Powered by:

X