As citações motivacionais estavam na moda na época em que MMS e encaminhamento de e-mail eram populares. Lembro-me de meus pais me encaminhando no início de cada manhã. Avanço rápido para hoje, se você tiver sorte, você faz parte de algum grupo de encaminhamento em seu aplicativo de mensagens de escolha (Whatsapp, Telegram, etc.).
Inspirados pela mesma ideia, hoje vamos construir um serviço que envia a nossos amigos e familiares uma citação motivacional do dia gerada por IA. Em vez de codificar uma lista de citações motivacionais, vamos usar um modelo de aprendizado de máquina para gerar uma cotação sob demanda, para que nunca fiquemos sem citações para compartilhar!
O modelo OpenAI GPT-2 foi proposto em Language Models are Unsupervisioned Multitask Learners por Alec Radford, Jeffrey Wu, Rewon Child, David Luan, Dario Amodei e Ilya Sutskever. É um transformador causal pré-treinado usando modelagem de linguagem em um corpus muito grande de aproximadamente 40 GB de dados de texto.
Para simplificar isso, em um OpenAI GPT2 de alto nível é um grande modelo de linguagem que foi treinado em grandes quantidades de dados. Este modelo pode ser usado para prever o próximo token em uma determinada sequência.
Se isso parece muito complicado, não se preocupe, você não precisa saber nada de Machine Learning ou AI para acompanhar este projeto. Bibliotecas como
vamos usar o
Felizmente, em nosso caso, há um modelo ajustado que foi treinado no conjunto de dados de 500 mil cotações -
Com o Hugging Face, usar este modelo é tão fácil quanto criar um tokenizador
from transformers import AutoTokenizer, AutoModelWithLMHead, pipeline tokenizer = AutoTokenizer.from_pretrained("nandinib1999/quote-generator")
em seguida, construindo um modelo a partir do modelo pré-treinado
model = AutoModelWithLMHead.from_pretrained("nandinib1999/quote-generator")
e, finalmente, construindo o gerador que podemos usar para gerar a cotação
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'}]
Agora que temos uma maneira de gerar cotações para nós, temos que pensar em como podemos usar isso em nosso aplicativo. Existem várias maneiras de construir isso.
Um ponto positivo importante da segunda opção é que, uma vez que o modelo é carregado, a API pode nos responder rapidamente e também pode ser usada em outros aplicativos. FWIW, a primeira opção também é uma abordagem totalmente válida.
Podemos usar __ Fast API __para criar uma API de serviço rápido. Aqui está o 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 testar
$ 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)
Agora podemos começar a enviar solicitações para o endpoint /generate
que irá gerar uma cotação para nós.
Agora que temos uma maneira de gerar cotações sob demanda, podemos parar por aqui e começar a trabalhar no envio por meio de
Dada nossa API, agora podemos fazer o seguinte para gerar uma cotação
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"]
O primeiro desafio é conseguir uma bela imagem de fundo para a nossa citação. Para isso, usaremos a API Unsplash, que fornece um bom endpoint para retornar uma imagem aleatória correspondente a uma consulta. Abertura
Para manter as coisas interessantes, podemos usar diferentes termos de consulta, como estrelas, etc. Veja como é o código para baixar nossa imagem de plano de fundo -
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
Ok, agora temos nossa imagem de fundo e uma citação, o que significa que podemos trabalhar na montagem da imagem final que será enviada aos destinatários. Em um nível alto, queremos colocar algum texto em uma imagem, mas mesmo essa tarefa simples pode ser desafiadora. Para começar, há uma série de perguntas para respondermos
As respostas para algumas dessas perguntas são mais complicadas do que outras. Para simplificar, colocaremos o texto no centro e faremos alguns contornos para que fique bem. Por fim, usaremos um texto de cor clara por enquanto. Para todas as manipulações de imagem, usaremos a Python Image Library (PIL) para facilitar isso para nós.
# 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
Isso gera a imagem final chamada result.jpg
Para a penúltima etapa, precisamos carregar a imagem para que possamos usá-la com o Courier. Neste caso, estou usando o Firebase Storage, mas você pode usar o que quiser.
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
Por fim, temos tudo o que precisamos para começar a enviar nossas citações incríveis para nossos amigos e familiares. Podemos usar o Courier para criar um modelo de e-mail bonito.
Enviar uma mensagem com o Courier é muito fácil. Embora o Courier tenha seus próprios SDKs que podem facilitar a integração, prefiro usar seu endpoint de API para manter as coisas simples. Com meu AUTH_TOKEN
e TEMPLATE_ID
em mãos, podemos usar o seguinte trecho de código para enviar nossa imagem
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)
A chave API pode ser encontrada em
Este tutorial demonstrou como é fácil começar a usar aprendizado de máquina e Courier.
Se você quiser ir em frente e melhorar este projeto, aqui estão algumas ideias interessantes para tentar