Las citas motivacionales estaban de moda en los días en que MMS y el reenvío de correo electrónico eran populares. Recuerdo que mis padres me reenviaban al comienzo de cada mañana. Avance rápido hasta hoy, si tiene suerte, es parte de algún grupo de reenvío en la aplicación de mensajería de su elección (Whatsapp, Telegram, etc.).
Inspirándonos en la misma idea, hoy vamos a crear un servicio que envía a nuestros amigos y familiares una cita motivacional del día generada por IA. En lugar de codificar una lista de citas motivacionales, vamos a utilizar un modelo de aprendizaje automático para generar una cita a pedido para que nunca nos quedemos sin citas para compartir.
El modelo OpenAI GPT-2 fue propuesto en Language Models are Unsupervised Multitask Learners por Alec Radford, Jeffrey Wu, Rewon Child, David Luan, Dario Amodei e Ilya Sutskever. Es un transformador causal entrenado previamente usando modelado de lenguaje en un corpus muy grande de ~40 GB de datos de texto.
Para simplificar esto, en un nivel alto OpenAI GPT2 es un modelo de lenguaje grande que ha sido entrenado en cantidades masivas de datos. Este modelo se puede utilizar para predecir el siguiente token en una secuencia determinada.
Si eso suena demasiado complicado, no se preocupe, no necesita saber nada de aprendizaje automático o inteligencia artificial para seguir este proyecto. bibliotecas como
Usaremos el
Afortunadamente, en nuestro caso hay un modelo ajustado que ha sido entrenado en el conjunto de datos de cotizaciones de 500k:
Con Hugging Face, usar este modelo es tan fácil como crear un tokenizador
from transformers import AutoTokenizer, AutoModelWithLMHead, pipeline tokenizer = AutoTokenizer.from_pretrained("nandinib1999/quote-generator")
luego construir un modelo a partir del modelo pre-entrenado
model = AutoModelWithLMHead.from_pretrained("nandinib1999/quote-generator")
y finalmente, construir el generador que podemos usar para generar la cotización
generator = pipeline("text-generation", model=model, tokenizer=tokenizer) # use a starting prompt generator("Keep an open mind and") [{'generated_text': 'Keep an open mind and a deep love for others'}]
Ahora que tenemos una forma de generar cotizaciones para nosotros, tenemos que pensar en cómo podemos usar esto en nuestra aplicación. Hay varias formas de construir esto.
Una ventaja clave de la segunda opción es que una vez que se carga el modelo, la API puede respondernos rápidamente y también se puede usar en otras aplicaciones. FWIW, la primera opción también es un enfoque totalmente válido.
Podemos usar __ Fast API __ para crear una API de servicio rápido. Esto es lo que parece
# in file api.py from pydantic import BaseModel from fastapi import FastAPI, HTTPException from transformers import AutoTokenizer, AutoModelWithLMHead, pipeline ## create the pipeline tokenizer = AutoTokenizer.from_pretrained("nandinib1999/quote-generator") model = AutoModelWithLMHead.from_pretrained("nandinib1999/quote-generator") generator = pipeline("text-generation", model=model, tokenizer=tokenizer) app = FastAPI() class QuoteRequest(BaseModel): text: str class QuoteResponse(BaseModel): text: str ### Serves the Model API to generate quote @app.post("/generate", response_model=QuoteResponse) async def generate(request: QuoteRequest): resp = generator(request.text) if not resp[0] and not resp[0]["generated_text"]: raise HTTPException(status_code=500, detail='Error in generation') return QuoteResponse(text=resp[0]["generated_text"])
Vamos a probarlo
$ uvicorn api:app INFO: Started server process [40767] INFO: Waiting for application startup. INFO: Application startup complete. INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
Ahora podemos comenzar a enviar solicitudes al punto final /generate
que generará una cotización para nosotros.
Ahora que tenemos una forma de generar cotizaciones a pedido, podemos detenernos aquí y comenzar a trabajar en enviar esto a través de
Dada nuestra API, ahora podemos hacer lo siguiente para generar una cotización
from random import choice # feel free to add more starting prompts for more variety canned_seeds = ["Always remember to", "Start today with", "It is okay to"] seed = choice(canned_seeds) resp = requests.post('http://127.0.0.1:8000/generate', data=json.dumps({"text": seed})) return resp.json()["text"]
El primer desafío es obtener una hermosa imagen de fondo para nuestra cotización. Para eso, usaremos la API de Unsplash que proporciona un buen punto final para devolver una imagen aleatoria que coincida con una consulta. Apertura
Para mantener las cosas interesantes, podemos usar diferentes términos de consulta, como estrellas, etc. Así es como se ve el código para descargar nuestra imagen de fondo:
from random import choice image_backgdrops = ['nature', 'stars', 'mountains', 'landscape'] backdrop = choice(image_backdrops) response = requests.get("https://source.unsplash.com/random/800×800/?"+ backdrop, stream=True) # write the output the img.png on our filesystem with open('img.png', 'wb') as out_file: shutil.copyfileobj(response.raw, out_file) del response
Bien, ahora tenemos nuestra imagen de fondo y un presupuesto, lo que significa que podemos trabajar en el ensamblaje de la imagen final que se enviará a los destinatarios. En un nivel alto, queremos colocar texto en una imagen, pero incluso esta tarea simple puede ser un desafío. Para empezar, hay una serie de preguntas que debemos responder.
Las respuestas a algunas de estas preguntas son más complicadas que otras. Para mantenerlo simple, colocaremos el texto en el centro y lo envolveremos para que se vea bien. Finalmente, usaremos un texto de color claro por ahora. Para toda la manipulación de imágenes, usaremos la Biblioteca de imágenes de Python (PIL) para que nos resulte más fácil.
# use the image we downloaded in the above step img = Image.open("img.png") width, height = img.size image_editable = ImageDraw.Draw(img) # wrap text lines = textwrap.wrap(text, width=40) # get the line count and generate a starting offset on y-axis line_count = len(lines) y_offset = height/2 - (line_count/2 * title_font.getbbox(lines[0])[3]) # for each line of text, we generate a (x,y) to calculate the positioning for line in lines: (_, _, line_w, line_h) = title_font.getbbox(line) x = (width - line_w)/2 image_editable.text((x,y_offset), line, (237, 230, 211), font=title_font) y_offset += line_h img.save("result.jpg") print("generated " + filename) return filename
Esto genera la imagen final llamada result.jpg
Para el penúltimo paso, necesitamos cargar la imagen para poder usarla con Courier. En este caso, estoy usando Firebase Storage, pero puedes usar lo que quieras.
import firebase_admin from firebase_admin import credentials from firebase_admin import storage cred = credentials.Certificate('serviceaccount.json') firebase_admin.initialize_app(cred, {...}) bucket = storage.bucket() blob = bucket.blob(filename) blob.upload_from_filename(filename) blob.make_public() return blob.public_url
Finalmente, tenemos todo lo que necesitamos para comenzar a enviar nuestras increíbles frases a nuestros amigos y familiares. Podemos usar Courier para crear una plantilla de correo electrónico atractiva.
Enviar un mensaje con Courier es tan fácil como parece. Si bien Courier tiene sus propios SDK que pueden facilitar la integración, prefiero usar su extremo de API para simplificar las cosas. Con mi AUTH_TOKEN
y TEMPLATE_ID
en la mano, podemos usar el siguiente código para enviar nuestra imagen
import requests headers = { "Accept": "application/json", "Content-Type": "application/json", "Authorization": "Bearer {}".format(os.environ['COURIER_AUTH_TOKEN']) } message={ "to": { "email": os.environ["COURIER_RECIPIENT"] }, "data": { "date": datetime.today().strftime("%B %d, %Y"), "img": image_url ## this is image url we generated earlier }, "routing": { "method": "single", "channels": [ "email" ] }, "template": os.environ["COURIER_TEMPLATE"] } requests.post("https://api.courier.com/send", json={"message": message}, headers=headers)
La clave API se puede encontrar en
Este tutorial demostró lo fácil que es comenzar con el aprendizaje automático y Courier.
Si quieres seguir adelante y mejorar este proyecto, aquí hay algunas ideas interesantes para probar