0tws9exwrdfzyvhk3.jpeg

Paso 1: crear API

FastAPI hace que sea muy fácil tomar un script de Python existente y convertirlo en una API con algunas líneas de código adicionales. Esto es lo que parece.

Primero importaremos algunas bibliotecas útiles.

from fastapi import FastAPI
import polars as pl
from sentence_transformers import SentenceTransformer
from sklearn.metrics import DistanceMetric
import numpy as np
from app.functions import returnSearchResultIndexes

A continuación, definiremos los componentes básicos de nuestra función de búsqueda semántica. Es decir, el modelo de incrustación de texto, las incrustaciones de título y transcripción de los videos que queremos buscar y una métrica de distancia para evaluar qué videos son más relevantes para la consulta de un usuario. Si desea profundizar más en la búsqueda semántica, lo hablé en un artículo pasado.

# define model info
model_name = 'all-MiniLM-L6-v2'

# load model
model = SentenceTransformer(model_name)

# load video index
df = pl.scan_parquet('app/data/video-index.parquet')

# create distance metric object
dist_name = 'manhattan'
dist = DistanceMetric.get_metric(dist_name)

Ahora, definimos nuestras operaciones API. Aquí crearemos 3 solicitudes GET. El primero se muestra en el bloque de código a continuación.

# create FastAPI object
app = FastAPI()

# API operations
@app.get("/")
def health_check():
return {'health_check': 'OK'}

En el bloque anterior, inicializamos una nueva aplicación FastAPI usando el API rápida() clase y luego crear un punto final de «verificación de estado».

Para hacer esto, definimos una función de Python que no recibe entradas pero que devuelve un diccionario con la clave «health_check» y un valor de «OK». Para convertir esta función en un punto final API, simplemente le agregamos un decorador y especificamos la ruta de nuestro punto final. Aquí usamos la raíz, es decir, “/».

Veamos otro ejemplo. Aquí tendremos un punto final llamado información que devuelve más información sobre la API.

@app.get("/info")
def info():
return {'name': 'yt-search', 'description': "Search API for Shaw Talebi's YouTube videos."}

Podemos ver que este punto final es muy similar al control de salud. Este, sin embargo, vive en el punto final «/info».

Finalmente, creemos el punto final de búsqueda, que, dada una consulta del usuario, devolverá los títulos y los ID de los videos más relevantes.

@app.get("/search")
def search(query: str):
idx_result = returnSearchResultIndexes(query, df, model, dist)
return df.select(['title', 'video_id']).collect()[idx_result].to_dict(as_series=False)

Para este punto final, requerimos una entrada: la consulta del usuario. Luego, esto se pasa a otra función de Python definida en otro script que hace todos los cálculos detrás de la búsqueda. Si bien no entraré en detalles aquí, el lector curioso puede ver el código en GitHub o el tutorial del código de la función de búsqueda en YouTube.

Dado que esta función solo devuelve los números de fila de los resultados de la búsqueda en el df marco de datos, necesitamos usar esta salida para tomar los títulos y las ID de videos que nos interesan y luego devolverlos como un diccionario de Python. Es importante que Todas las salidas de nuestros puntos finales API serán diccionarios. porque se adhieren al formato JSON estándar para API.

Observe que los bloques de código anteriores hacían referencia a dos archivos externos: aplicación/funciones.py y aplicación/datos/video-index.parquetlo que implica la siguiente estructura de directorios.

La estructura de directorios de la API FastAPI. Imagen del autor.

Para ejecutar esta API localmente, podemos navegar hasta el directorio raíz y ejecutar el siguiente comando.

uvicorn app.main:app --host 0.0.0.0 --port 8080

uvicornio es un Biblioteca Python que nos permite ejecutar aplicaciones web como el que creamos usando FastAPI. Este comando ejecutará la API localmente en http://0.0.0.0:8080. La razón por la que utilizamos este host y puerto será evidente más adelante cuando implementemos Google Cloud Run.

Paso 2: crear una imagen de Docker

¡Sí, nuestra API se ejecuta localmente! Demos el siguiente paso para ejecutarlo en la nube.

Hacemos esto creando una imagen de Docker para nuestra API. Esto requerirá que hagamos 3 archivos adicionales: archivo acoplable, requisitos.txty aplicación/__init__.py. Así es como debería verse nuestro directorio.

La estructura de directorios para la creación de imágenes de Docker. Imagen del autor (sin juego de palabras).

El archivo acoplable contiene las instrucciones paso a paso para ejecutar nuestra imagen de Docker. requisitos.txt especifica las bibliotecas de Python (con versiones) necesarias para ejecutar nuestra API. Finalmente, aplicación/__init__.py señala el aplicación carpeta como un paquete de Python, lo que garantiza que Python pueda encontrar e importar correctamente nuestro código API cuando se ejecuta en el contenedor.

Esto es lo que el interior del archivo acoplable parece.

# start from python base image
FROM python:3.10-slim

# change working directory
WORKDIR /code

# add requirements file to image
COPY ./requirements.txt /code/requirements.txt

# install python libraries
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt

# add python code
COPY ./app/ /code/app/

# specify default commands
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8080"]

La primera línea arranca nuestra imagen encima de una imagen existente que tiene Python 3.10 instalado. A continuación, cambiamos nuestro directorio de trabajo del raíz a /código.

Luego copiamos el requisitos.txt archivo de nuestro código base a la imagen de Docker. Una vez implementado esto, podemos instalar nuestros requisitos usando pip.

A continuación, copiamos todo el código de nuestra API.

Nota: copiamos e instalamos los paquetes de Python antes que nada porque esto nos permite almacenar en caché la instalación de los requisitos. Esto es útil porque evita esperar unos minutos para que se instalen las dependencias cuando ejecutamos nuestra imagen de Docker rápidamente durante el desarrollo.

Finalmente, especificamos el comando predeterminado, que es el mismo que ejecutamos localmente para ejecutar nuestra API.

Paso 3: implementar en Google Cloud

En este punto, podemos construir nuestra imagen de Docker y enviarla al Centro acoplable, que podemos implementar fácilmente en muchos servicios de nube diferentes. Sin embargo, seguiré una estrategia alternativa aquí.

En su lugar, enviaré todo nuestro código a GitHub, desde donde podremos implementar directamente el contenedor en Google Cloud Run. Esto tiene dos beneficios clave.

Primeroevita resolver discrepancias en la arquitectura del sistema entre su sistema local y lo que usa Google Cloud Run (esto fue un problema específicamente para mí porque Mac se ejecuta en ARM64). Segundola implementación desde un repositorio de GitHub permite una implementación continua, por lo que si queremos actualizar nuestra API, simplemente podemos enviar código nuevo a nuestro repositorio y se activará un nuevo contenedor automáticamente.

Comenzamos creando un nuevo repositorio de GitHub.

Creando repositorio de GitHub. Imagen del autor.

Luego, clonamos el repositorio, agregamos nuestro código y lo enviamos de regreso a GitHub. Así es como se ve la estructura del directorio después de agregar el código.

Estructura del directorio del repositorio de GitHub. Imagen del autor.

Con el código implementado, podemos crear un nuevo proyecto de Google Cloud Platform. Esto lo hacemos yendo al consola GCPhaciendo clic en nuestras listas de proyectos, y seleccionando “NUEVO PROYECTO”.

Creando un nuevo proyecto de GCP. Imagen del autor.

Una vez creado nuestro proyecto, podemos abrirlo y escribir «ejecutar en la nube» en la barra de búsqueda.

Buscando el servicio Cloud Run. Imagen del autor.

Cuando se abra, haremos clic en «CREAR SERVICIO». Esto nos abrirá una página para configurar nuestro servicio.

Primero, seleccionamos la opción para implementar nuestro servicio desde GitHub. Luego haga clic en «CONFIGURAR CON CLOUD BUILD».

Configuración de la implementación continua del servicio Cloud Run desde GitHub. Imagen del autor.

Para la fuente del repositorio, seleccionaremos GitHub y elegiremos el repositorio que acabamos de crear.

Elegir la fuente de nuestro repositorio. Imagen del autor.

Dejaremos la sucursal como ^principal$ y seleccione el «Tipo de compilación» como Dockerfile.

A continuación, volvemos a la pantalla de configuración del servicio. Podemos nombrar el servicio como queramos (lo dejo como el generado automáticamente). Para la región lo dejo como us-central1 porque es un Nivel 1, que ofrece las opciones informáticas más baratas (básicamente gratis para este ejemplo).

Para simplificar las cosas, «Permitiré invocaciones no autenticadas». Por supuesto, querrá solicitar autenticación para la mayoría de los sistemas. Luego, deja todo lo demás como predeterminado.

Configuración del servicio. Imagen del autor.

Finalmente, bajo “Contenedor(es), Volúmenes, Redes, Seguridad”, edite el contenedor para que tenga 1 GiB de memoria. Debemos dejar el PUERTO como 8080 porque eso es lo que configuramos en nuestro Dockerfile.

Contenedor de edición. Imagen del autor.

Podemos dejar todo lo demás por defecto y presionar “CREAR” en la parte inferior de la pantalla. ¡Después de unos minutos, el contenedor estará activo!

Servicio que se ejecuta en Cloud Run. Imagen del autor.

Luego podemos acceder a la API utilizando la URL especificada cerca de la parte superior de la página. Al hacer clic en el enlace se abrirá el punto final raíz, que fue nuestro control de estado.

Punto final de control de estado. Imagen del autor.

Podemos ejecutar manualmente una solicitud GET a la API de búsqueda usando la siguiente URL: [your-app-url-here]/search?query=LLM. Esto buscará videos relevantes para los LLM.

Realizar una solicitud GET para buscar el punto final de la API. Imagen del autor.

Bonificación: integración en la interfaz de usuario

Una vez configurado nuestro backend API, podemos conectarlo a una interfaz fácil de usar. lo hago a través de Abrazar espacios facialescual aloja aplicaciones ML completamente gratis.

Así es como se ve esa misma búsqueda a través de una interfaz de usuario. Puedes jugar con la interfaz de usuario. aquí y ver el codigo aquí.

Buscar interfaz de usuario en HF Spaces. Gif por autor.

La ciencia de datos es más que solo entrenar modelos sofisticados. Se trata de resolver problemas y generar valor. A menudo, esto requiere que implementemos modelos en entornos donde puedan tener el mayor impacto. Aquí, analizamos una estrategia simple de tres pasos para implementar modelos de aprendizaje automático utilizando FastAPI, Docker y GCP.

Si bien esto concluye el Ciencia de datos de pila completa serie, estos artículos van acompañados de un Lista de reproducción de YouTube con un vídeo extra sobre la experimentación involucrada en la creación de esta herramienta de búsqueda.

Más sobre ciencia de datos Full Stack 👇

Ciencia de datos de pila completa