Soy German Bourdin, tengo 29 años
Como ir de esto:
En general, si estamos experimentando vamos a tener mas o menos las siguientes etapas en nuestro notebook:
Probablemente con varias iteraciones de los últimos pasos hasta que llegamos a un modelo que nos convence
Saliendo del notebook, hay tareas que nos van a seguir interesando hacer con nuestro modelo y tareas que ya no tanto.
In [1]:
from abc import ABCMeta, abstractmethod
class BaseModel(metaclass=ABCMeta):
@abstractmethod
def predict(self, X):
passs
@abstractmethod
def fit(self, X, y):
pass
def dump(self, path):
"""
Dumps the current trained model to path
"""
pass
@classmethod
def load(cls, path):
"""
Load saved model from path.
"""
pass
Hiciste tu predictor pip instalable, de paso te aseguraste:
Va mas allá de lo que voy a llegar a hablar en esta charla, pero primero, les permite ser los primeros usuarios de sus propios modelos.
Segundo, hay muchas cosas para obligarse a pensar en este sentido.
Ya tenemos nuestro predictor instalable, ya esta testeado dumpeamos y cargamos el modelo donde queremos, pero todavía lo estamos usando nosotros solos. Que como solucionamos esto?
Q: Los microservicios son una buena idea para conseguirte dolores de cabeza gratis, por qué no lo integrás todo con tu sistema existente?
A: Hablemos mas de esto despues, pero en general, los sistemas que sirven modelos de machine learning suelen necesitar prestar atencion a cosas muy distintas a por ejemplo un sitio de clasificados tradicional, donde el tiempo que vas a pasar esperando, en general va a ser de latencia de red o de disco y podes hacer cosas con el CPU mientras esperas.
Veamos como reemplazar el modelo en 5 minutos (por uno que ande menos mal)
Live Coding! Nada puede salir mal!
Hay muchas herramientas para construir modelos de Machine Learning, que no son threadsafe, xgboost por ejemplo aclara que el predict de los modelos que construye tiene este problema.
Esto es, no puede ejecutarse en muchos threads al mismo tiempo, y si eso ocurre, no garantizan que funcione o que funcione bien. A la hora de configurar servidores para modelos de ese tipo, hay que tenerlo en cuenta, es una buena idea aclararlo en nuestros READMEs!
Otra cosa que suele pasar con algunas de las herramientas mas comunes, es que generan cosas en memoria que no son serializables o que no pueden compartirse entre distintos procesos. Es el caso de TensorFlow entre otros, donde las sesiones no se pueden compartir y algunas cosas de los modelos no son serializables.
Para evitar problemas con esto, conviene habilitar el "lazy load" en la config de uwsgi. Por defecto uwsgi antes de forkear los workers carga la app en memoria y hace algo de inicializacion, eso ayuda a que el footprint en memoria sea mas chico. Para cosas como tensorflow, queremos que pase exactamente lo opuesto.
Usar threads para aumentar el throughput the una aplicacion web suele ser tentador, tengan en cuenta sin embargo, a la hora de configurar un servidor para modelos de Machine Learning que lo mas probable es que su cuello de botella sea en CPU: ie, sus procesos no pierden tiempo buscando cosas del disco o esperando a la red, estan usando el CPU para hacer calculos. Por como funciona el GIL, agregarle mas threads a esto, lo unico que va a conseguir es aumentar la latencia de todos los threads que estan compartiendo el interprete!
Usen procesos en su configuracion, apunten a no mas de uno por CPU y tuneen desde ahi
No suele ser un tema menor cuanto tiempo tarda en responder un modelo, antes de poner algo en produccion, si van a tener requerimientos sobre esto, hacer benchmarks y load tests del modelo solo y del modelo montado sobre el servidor para al menos entender como se comporta. Tener una idea de con que volumen de requests vamos a empezar a encolar pedidos en lugar de responder enseguida y cuanto vamos a tener que escalar para satisfacer los requerimientos.
De nuevo, hagan experimentos con carga similar a la que esperan de produccion, observen, aunque sea de la forma más rustica la carga y el nivel de uso del CPU. Algunas librerias llaman a rutinas en C que se escapan de las limitaciones de python y usan threads o forkean para usar 2, 4, 8 o TODOS los cpus disponibles. Si esto les pasa, en seguida van a notar que aunque tengan 1 servidor cada 2 CPUs, si hay requests contestandose en paralelo, la performance de todas va a estar degradada. Enterensé lo antes posible de esto, tomen medidas para evitarlo.
Si van a estar cargando modelos grandes en memoria, tengan en cuenta cuanta hay disponible, no quieren irse a swap (o que los mate el OOM)
Esto no se cae tan de maduro, pero, antes de poner algo en produccion, solemos tener idea de como performa contra datos productivos que teniamos ejecutado. Con los datos que estemos prediciendo una vez que vayamos a produccion, a menos que tengamos un loop donde los datos lo mismo se revisen y etiqueten a mano, solamente tenemos la estadística de como suelen estar compuestos.
Si por ejemplo, hicieron un detector de panchos, y en las muestras que tomaron de produccion, el 20% de las imagenes tenian panchos, monitoreen sus resultados, si en testing tenian buenas metricas pero en produccion de repente pasan a detectar que el 80% de las imagenes estan teniendo panchos, o estamos sufriendo una invasion de vendedores de panchos o estamos por algun motivo devolviendo falsos positivos.
Si pueden, monitoreen de forma automatica, si no, al menos colecten los logs y parseen de vez en cuando!
Algo que suele pasar por la naturaleza de los algotimos que hay debajo (aunque no siempre es el caso, midanlo si estan en esta situación) es que hacer una prediccion para 1 dato o para 50 lleva el mismo o casi el mismo tiempo.
Ejemplo: Supongamos que obtener el precio sugerido de una casa me lleva 100ms, y el de 10 casas juntas me lleva 150ms, si predijera en serie 10 pedidos que entran juntos, el primero va a tardar 100ms, el segundo 200, el ultimo 1000ms.
Un posible tweak que se puede meter acá es una ventana que junte requests y cada X ms mande a ejecutar, es complejo de implementar pero ayuda a reducir la latencia media en estos casos. No quiero dar ejemplos puntuales porque es complicado y hay que tunearlo mucho.
During a conversation I had with Peter Norvig, Norvig quoted a friend of him who said:
“Machine Learning development is like the raisins in a raisin bread: 1. You need the bread first 2. It’s just a few tiny raisins but without it you would just have plain bread.”
Marcos Sponton - Don't Buy Machine Learning (2016)
Esta es una cita que me gusta mucho, cuenta la leyenda que se la decia Peter Norvig, al jefe del area de R&D de Machinalis hace algunos años. Y es algo que resuena mucho conmigo y es uno de los disparadores de que venga a hablarles hoy aca.
Desarrollar soluciones de Machine Learning para la industria, es una parte chiquita de llevar soluciones a los usuarios, en particular, hacer solamente la parte de ciencia, nos deja muy lejos de los usuarios, si no lo transformamos en un producto, lo único con lo que nos quedamos son con un monton de notebooks con resultados bonitos que sirven para escribir un blogpost, para publicar un paper, para dar una charla en una pycon o para mostrarle a tus amigos, pero lejos estan de ser algo que los usuarios puedan consumir.