From 858bf4130093d9f819b6107c5245f4ceae680d41 Mon Sep 17 00:00:00 2001 From: Aaron Jimenez Date: Sun, 14 Apr 2024 10:41:06 -0800 Subject: [PATCH 1/6] add pipeline_webserver to es/ --- docs/source/es/_toctree.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/source/es/_toctree.yml b/docs/source/es/_toctree.yml index 4506dbd06f96b9..cf1ae39c03077e 100644 --- a/docs/source/es/_toctree.yml +++ b/docs/source/es/_toctree.yml @@ -100,4 +100,6 @@ title: BERTología - local: perplexity title: Perplejidad de los modelos de longitud fija + - local: pipeline_webserver + title: Flujo de trabajo para la inferencia de los servidores web title: Guías conceptuales From ba47d98adec2fd3e6698ed11c38e178ccc21d807 Mon Sep 17 00:00:00 2001 From: Aaron Jimenez Date: Sun, 14 Apr 2024 11:07:31 -0800 Subject: [PATCH 2/6] add pipeline_webserver to es/, translate first section --- docs/source/es/pipeline_webserver.md | 155 +++++++++++++++++++++++++++ 1 file changed, 155 insertions(+) create mode 100644 docs/source/es/pipeline_webserver.md diff --git a/docs/source/es/pipeline_webserver.md b/docs/source/es/pipeline_webserver.md new file mode 100644 index 00000000000000..4c3b50b34307b9 --- /dev/null +++ b/docs/source/es/pipeline_webserver.md @@ -0,0 +1,155 @@ + + +# Uso de un flujo de trabajo para un servidor web + + +Crear un motor de inferencia es un tema complejo, y la "mejor" solución probablemente dependerá de tu espacio de problemas. ¿Estás en CPU o en GPU? ¿Quieres la latencia más baja, el rendimiento más alto, soporte para muchos modelos o simplemente optimizar altamente un modelo específico? Hay muchas formas de abordar este tema, así que lo que vamos a presentar es un buen valor predeterminado para comenzar, que no necesariamente será la solución más óptima para ti. + + + +Lo fundamental para entender es que podemos usar un iterador, tal como [en un conjunto de datos](pipeline_tutorial), ya que un servidor web es básicamente un sistema que espera solicitudes y las trata a medida que llegan. + +Por lo general, los servidores web están multiplexados (multihilo, asíncrono, etc.) para manejar varias solicitudes simultáneamente. Por otro lado, los flujos de trabajo (y principalmente los modelos subyacentes) no son realmente ideales para el paralelismo; consumen mucha RAM, por lo que es mejor darles todos los recursos disponibles cuando se están ejecutando o es un trabajo intensivo en cómputo. + +Vamos a resolver esto haciendo que el servidor web maneje la carga ligera de recibir y enviar solicitudes, y que un único hilo maneje el trabajo real. Este ejemplo va a utilizar `starlette`. El marco de trabajo no es realmente importante, pero es posible que debas ajustar o cambiar el código si estás utilizando otro para lograr el mismo efecto. + +Create `server.py`: + +```py +from starlette.applications import Starlette +from starlette.responses import JSONResponse +from starlette.routing import Route +from transformers import pipeline +import asyncio + + +async def homepage(request): + payload = await request.body() + string = payload.decode("utf-8") + response_q = asyncio.Queue() + await request.app.model_queue.put((string, response_q)) + output = await response_q.get() + return JSONResponse(output) + + +async def server_loop(q): + pipe = pipeline(model="google-bert/bert-base-uncased") + while True: + (string, response_q) = await q.get() + out = pipe(string) + await response_q.put(out) + + +app = Starlette( + routes=[ + Route("/", homepage, methods=["POST"]), + ], +) + + +@app.on_event("startup") +async def startup_event(): + q = asyncio.Queue() + app.model_queue = q + asyncio.create_task(server_loop(q)) +``` + +Now you can start it with: +```bash +uvicorn server:app +``` + +And you can query it: +```bash +curl -X POST -d "test [MASK]" http://localhost:8000/ +#[{"score":0.7742936015129089,"token":1012,"token_str":".","sequence":"test."},...] +``` + +And there you go, now you have a good idea of how to create a webserver! + +What is really important is that we load the model only **once**, so there are no copies +of the model on the webserver. This way, no unnecessary RAM is being used. +Then the queuing mechanism allows you to do fancy stuff like maybe accumulating a few +items before inferring to use dynamic batching: + + + +The code sample below is intentionally written like pseudo-code for readability. +Do not run this without checking if it makes sense for your system resources! + + + +```py +(string, rq) = await q.get() +strings = [] +queues = [] +while True: + try: + (string, rq) = await asyncio.wait_for(q.get(), timeout=0.001) # 1ms + except asyncio.exceptions.TimeoutError: + break + strings.append(string) + queues.append(rq) +strings +outs = pipe(strings, batch_size=len(strings)) +for rq, out in zip(queues, outs): + await rq.put(out) +``` + +Again, the proposed code is optimized for readability, not for being the best code. +First of all, there's no batch size limit which is usually not a +great idea. Next, the timeout is reset on every queue fetch, meaning you could +wait much more than 1ms before running the inference (delaying the first request +by that much). + +It would be better to have a single 1ms deadline. + +This will always wait for 1ms even if the queue is empty, which might not be the +best since you probably want to start doing inference if there's nothing in the queue. +But maybe it does make sense if batching is really crucial for your use case. +Again, there's really no one best solution. + + +## Few things you might want to consider + +### Error checking + +There's a lot that can go wrong in production: out of memory, out of space, +loading the model might fail, the query might be wrong, the query might be +correct but still fail to run because of a model misconfiguration, and so on. + +Generally, it's good if the server outputs the errors to the user, so +adding a lot of `try..except` statements to show those errors is a good +idea. But keep in mind it may also be a security risk to reveal all those errors depending +on your security context. + +### Circuit breaking + +Webservers usually look better when they do circuit breaking. It means they +return proper errors when they're overloaded instead of just waiting for the query indefinitely. Return a 503 error instead of waiting for a super long time or a 504 after a long time. + +This is relatively easy to implement in the proposed code since there is a single queue. +Looking at the queue size is a basic way to start returning errors before your +webserver fails under load. + +### Blocking the main thread + +Currently PyTorch is not async aware, and computation will block the main +thread while running. That means it would be better if PyTorch was forced to run +on its own thread/process. This wasn't done here because the code is a lot more +complex (mostly because threads and async and queues don't play nice together). +But ultimately it does the same thing. + +This would be important if the inference of single items were long (> 1s) because +in this case, it means every query during inference would have to wait for 1s before +even receiving an error. + +### Dynamic batching + +In general, batching is not necessarily an improvement over passing 1 item at +a time (see [batching details](./main_classes/pipelines#pipeline-batching) for more information). But it can be very effective +when used in the correct setting. In the API, there is no dynamic +batching by default (too much opportunity for a slowdown). But for BLOOM inference - +which is a very large model - dynamic batching is **essential** to provide a decent experience for everyone. From ee84babf5fdab6e596b8d5780a3080c51feb6427 Mon Sep 17 00:00:00 2001 From: Aaron Jimenez Date: Sun, 14 Apr 2024 11:28:56 -0800 Subject: [PATCH 3/6] add comment for checking link --- docs/source/es/pipeline_webserver.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/source/es/pipeline_webserver.md b/docs/source/es/pipeline_webserver.md index 4c3b50b34307b9..868c1ace889080 100644 --- a/docs/source/es/pipeline_webserver.md +++ b/docs/source/es/pipeline_webserver.md @@ -11,6 +11,12 @@ Crear un motor de inferencia es un tema complejo, y la "mejor" solución probabl Lo fundamental para entender es que podemos usar un iterador, tal como [en un conjunto de datos](pipeline_tutorial), ya que un servidor web es básicamente un sistema que espera solicitudes y las trata a medida que llegan. + + Por lo general, los servidores web están multiplexados (multihilo, asíncrono, etc.) para manejar varias solicitudes simultáneamente. Por otro lado, los flujos de trabajo (y principalmente los modelos subyacentes) no son realmente ideales para el paralelismo; consumen mucha RAM, por lo que es mejor darles todos los recursos disponibles cuando se están ejecutando o es un trabajo intensivo en cómputo. Vamos a resolver esto haciendo que el servidor web maneje la carga ligera de recibir y enviar solicitudes, y que un único hilo maneje el trabajo real. Este ejemplo va a utilizar `starlette`. El marco de trabajo no es realmente importante, pero es posible que debas ajustar o cambiar el código si estás utilizando otro para lograr el mismo efecto. From 489588db5604e1d8ab3995025890ca1d373306be Mon Sep 17 00:00:00 2001 From: Aaron Jimenez Date: Sun, 14 Apr 2024 12:54:47 -0800 Subject: [PATCH 4/6] translate pipeline_webserver --- docs/source/es/pipeline_webserver.md | 73 +++++++++------------------- 1 file changed, 23 insertions(+), 50 deletions(-) diff --git a/docs/source/es/pipeline_webserver.md b/docs/source/es/pipeline_webserver.md index 868c1ace889080..ffa7591a802245 100644 --- a/docs/source/es/pipeline_webserver.md +++ b/docs/source/es/pipeline_webserver.md @@ -21,7 +21,7 @@ Por lo general, los servidores web están multiplexados (multihilo, asíncrono, Vamos a resolver esto haciendo que el servidor web maneje la carga ligera de recibir y enviar solicitudes, y que un único hilo maneje el trabajo real. Este ejemplo va a utilizar `starlette`. El marco de trabajo no es realmente importante, pero es posible que debas ajustar o cambiar el código si estás utilizando otro para lograr el mismo efecto. -Create `server.py`: +Crear `server.py`: ```py from starlette.applications import Starlette @@ -62,28 +62,25 @@ async def startup_event(): asyncio.create_task(server_loop(q)) ``` -Now you can start it with: +Ahora puedes empezar con: ```bash uvicorn server:app ``` -And you can query it: +Y puedes consultarlo con: ```bash curl -X POST -d "test [MASK]" http://localhost:8000/ #[{"score":0.7742936015129089,"token":1012,"token_str":".","sequence":"test."},...] ``` -And there you go, now you have a good idea of how to create a webserver! +¡Y listo, ahora tienes una buena idea de cómo crear un servidor web! -What is really important is that we load the model only **once**, so there are no copies -of the model on the webserver. This way, no unnecessary RAM is being used. -Then the queuing mechanism allows you to do fancy stuff like maybe accumulating a few -items before inferring to use dynamic batching: +Lo realmente importante es cargar el modelo solo **una vez**, de modo que no haya copias del modelo en el servidor web. De esta manera, no se utiliza RAM innecesariamente. Luego, el mecanismo de queuing te permite hacer cosas sofisticadas como acumular algunos elementos antes de inferir para usar el agrupamiento dinámico: -The code sample below is intentionally written like pseudo-code for readability. -Do not run this without checking if it makes sense for your system resources! +El ejemplo de código a continuación está escrito intencionalmente como seudocódigo para facilitar la lectura. +¡No lo ejecutes sin verificar si tiene sentido para los recursos de tu sistema! @@ -104,58 +101,34 @@ for rq, out in zip(queues, outs): await rq.put(out) ``` -Again, the proposed code is optimized for readability, not for being the best code. -First of all, there's no batch size limit which is usually not a -great idea. Next, the timeout is reset on every queue fetch, meaning you could -wait much more than 1ms before running the inference (delaying the first request -by that much). +Nuevamente, el código propuesto está optimizado para la legibilidad, no para ser el mejor código. +En primer lugar, no hay límite de tamaño de lote, lo cual generalmente no es una buena idea. Luego, el tiempo de espera se restablece en cada obtención de la cola, lo que significa que podrías esperar mucho más de 1ms antes de ejecutar la inferencia (retrasando la primera solicitud en esa cantidad). -It would be better to have a single 1ms deadline. +Sería mejor tener un único plazo de 1ms. -This will always wait for 1ms even if the queue is empty, which might not be the -best since you probably want to start doing inference if there's nothing in the queue. -But maybe it does make sense if batching is really crucial for your use case. -Again, there's really no one best solution. +Esto siempre esperará 1ms incluso si la cola está vacía, lo que podría no ser lo mejor ya que probablemente quieras comenzar a hacer inferencias si no hay nada en la cola. Pero tal vez tenga sentido si el agrupamiento es realmente crucial para tu caso de uso. Nuevamente, no hay una solución única y mejor. -## Few things you might want to consider +## Algunas cosas que podrías considerar -### Error checking +### Comprobación de errores -There's a lot that can go wrong in production: out of memory, out of space, -loading the model might fail, the query might be wrong, the query might be -correct but still fail to run because of a model misconfiguration, and so on. +Hay muchas cosas que pueden salir mal en producción: falta de memoria, falta de espacio, cargar el modelo podría fallar, la consulta podría ser incorrecta, la consulta podría ser correcta pero aún así fallar debido a una mala configuración del modelo, y así sucesivamente. -Generally, it's good if the server outputs the errors to the user, so -adding a lot of `try..except` statements to show those errors is a good -idea. But keep in mind it may also be a security risk to reveal all those errors depending -on your security context. +Generalmente, es bueno que el servidor muestre los errores al usuario, por lo que agregar muchos bloques `try..except` para mostrar esos errores es una buena idea. Pero ten en cuenta que también puede ser un riesgo de seguridad revelar todos esos errores dependiendo de tu contexto de seguridad. -### Circuit breaking +### Interrupción de circuito -Webservers usually look better when they do circuit breaking. It means they -return proper errors when they're overloaded instead of just waiting for the query indefinitely. Return a 503 error instead of waiting for a super long time or a 504 after a long time. +Los servidores web suelen verse mejor cuando hacen interrupciones de circuitos. Significa que devuelven errores adecuados cuando están sobrecargados en lugar de simplemente esperar la consulta indefinidamente. Devolver un error 503 en lugar de esperar un tiempo muy largo o un error 504 después de mucho tiempo. -This is relatively easy to implement in the proposed code since there is a single queue. -Looking at the queue size is a basic way to start returning errors before your -webserver fails under load. +Esto es relativamente fácil de implementar en el código propuesto ya que hay una sola cola. Mirar el tamaño de la cola es una forma básica de empezar a devolver errores antes de que tu servidor web falle bajo carga. -### Blocking the main thread +### Bloqueo del hilo principal -Currently PyTorch is not async aware, and computation will block the main -thread while running. That means it would be better if PyTorch was forced to run -on its own thread/process. This wasn't done here because the code is a lot more -complex (mostly because threads and async and queues don't play nice together). -But ultimately it does the same thing. +Actualmente, PyTorch no es consciente de la asincronía, y el cálculo bloqueará el hilo principal mientras se ejecuta. Esto significa que sería mejor si PyTorch se viera obligado a ejecutarse en su propio hilo/proceso. Esto no se hizo aquí porque el código es mucho más complejo (principalmente porque los hilos, la asincronía y las colas no se llevan bien juntos). Pero en última instancia, hace lo mismo. -This would be important if the inference of single items were long (> 1s) because -in this case, it means every query during inference would have to wait for 1s before -even receiving an error. +Esto sería importante si la inferencia de elementos individuales fuera larga (> 1s) porque en este caso, significa que cada consulta durante la inferencia tendría que esperar 1s antes de recibir incluso un error. -### Dynamic batching +### Procesamiento por lotes dinámico -In general, batching is not necessarily an improvement over passing 1 item at -a time (see [batching details](./main_classes/pipelines#pipeline-batching) for more information). But it can be very effective -when used in the correct setting. In the API, there is no dynamic -batching by default (too much opportunity for a slowdown). But for BLOOM inference - -which is a very large model - dynamic batching is **essential** to provide a decent experience for everyone. +En general, el procesamiento por lotes no es necesariamente una mejora respecto a pasar 1 elemento a la vez (ver [procesamiento por lotes](https://huggingface.co/docs/transformers/main_classes/pipelines#pipeline-batching) para más información). Pero puede ser muy efectivo cuando se usa en el entorno correcto. En la API, no hay procesamiento por lotes dinámico por defecto (demasiada oportunidad para una desaceleración). Pero para la inferencia de BLOOM - que es un modelo muy grande - el procesamiento por lotes dinámico es **esencial** para proporcionar una experiencia decente para todos. From f082fb3c3ccf119490b1ac546135c04d276acfb5 Mon Sep 17 00:00:00 2001 From: Aaron Jimenez Date: Mon, 15 Apr 2024 09:25:53 -0800 Subject: [PATCH 5/6] edit pipeline_webserver --- docs/source/es/pipeline_webserver.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/source/es/pipeline_webserver.md b/docs/source/es/pipeline_webserver.md index ffa7591a802245..0fa471a142b4e7 100644 --- a/docs/source/es/pipeline_webserver.md +++ b/docs/source/es/pipeline_webserver.md @@ -5,16 +5,16 @@ rendered properly in your Markdown viewer. # Uso de un flujo de trabajo para un servidor web -Crear un motor de inferencia es un tema complejo, y la "mejor" solución probablemente dependerá de tu espacio de problemas. ¿Estás en CPU o en GPU? ¿Quieres la latencia más baja, el rendimiento más alto, soporte para muchos modelos o simplemente optimizar altamente un modelo específico? Hay muchas formas de abordar este tema, así que lo que vamos a presentar es un buen valor predeterminado para comenzar, que no necesariamente será la solución más óptima para ti. +Crear un motor de inferencia es un tema complejo, y la "mejor" solución probablemente dependerá de tu caso de uso. ¿Estás en CPU o en GPU? ¿Quieres la latencia más baja, el rendimiento más alto, soporte para muchos modelos o simplemente optimizar altamente un modelo específico? Hay muchas formas de abordar este tema, así que lo que vamos a presentar es un buen valor predeterminado para comenzar, que no necesariamente será la solución más óptima para ti. -Lo fundamental para entender es que podemos usar un iterador, tal como [en un conjunto de datos](pipeline_tutorial), ya que un servidor web es básicamente un sistema que espera solicitudes y las trata a medida que llegan. +Lo fundamental para entender es que podemos usar un iterador, tal como [en un conjunto de datos](https://huggingface.co/docs/transformers/pipeline_tutorial#using-pipelines-on-a-dataset), ya que un servidor web es básicamente un sistema que espera solicitudes y las trata a medida que llegan. Por lo general, los servidores web están multiplexados (multihilo, asíncrono, etc.) para manejar varias solicitudes simultáneamente. Por otro lado, los flujos de trabajo (y principalmente los modelos subyacentes) no son realmente ideales para el paralelismo; consumen mucha RAM, por lo que es mejor darles todos los recursos disponibles cuando se están ejecutando o es un trabajo intensivo en cómputo. @@ -75,7 +75,7 @@ curl -X POST -d "test [MASK]" http://localhost:8000/ ¡Y listo, ahora tienes una buena idea de cómo crear un servidor web! -Lo realmente importante es cargar el modelo solo **una vez**, de modo que no haya copias del modelo en el servidor web. De esta manera, no se utiliza RAM innecesariamente. Luego, el mecanismo de queuing te permite hacer cosas sofisticadas como acumular algunos elementos antes de inferir para usar el agrupamiento dinámico: +Lo realmente importante es cargar el modelo solo **una vez**, de modo que no haya copias del modelo en el servidor web. De esta manera, no se utiliza RAM innecesariamente. Luego, el mecanismo de queuing (colas) te permite hacer cosas sofisticadas como acumular algunos elementos antes de inferir para usar el agrupamiento dinámico: From fd285569cf83fb3070cbd6a6eef099877857e8e6 Mon Sep 17 00:00:00 2001 From: Aaron Jimenez Date: Thu, 25 Apr 2024 14:24:19 -0800 Subject: [PATCH 6/6] fix typo --- docs/source/es/pipeline_webserver.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/es/pipeline_webserver.md b/docs/source/es/pipeline_webserver.md index 0fa471a142b4e7..e77e620f58b78b 100644 --- a/docs/source/es/pipeline_webserver.md +++ b/docs/source/es/pipeline_webserver.md @@ -79,7 +79,7 @@ Lo realmente importante es cargar el modelo solo **una vez**, de modo que no hay -El ejemplo de código a continuación está escrito intencionalmente como seudocódigo para facilitar la lectura. +El ejemplo de código a continuación está escrito intencionalmente como pseudocódigo para facilitar la lectura. ¡No lo ejecutes sin verificar si tiene sentido para los recursos de tu sistema!