# Construir uma Aplicação Web para Utilizar um Modelo de ML Nesta lição, vais treinar um modelo de ML com um conjunto de dados fora do comum: _avistamentos de OVNIs ao longo do último século_, provenientes da base de dados do NUFORC. Vais aprender: - Como 'pickle' um modelo treinado - Como usar esse modelo numa aplicação Flask Continuaremos a usar notebooks para limpar os dados e treinar o modelo, mas podes levar o processo um passo adiante ao explorar como usar um modelo "no mundo real", por assim dizer: numa aplicação web. Para isso, precisas de construir uma aplicação web utilizando Flask. ## [Questionário pré-aula](https://ff-quizzes.netlify.app/en/ml/) ## Construir uma aplicação Existem várias formas de construir aplicações web para consumir modelos de machine learning. A arquitetura da tua aplicação web pode influenciar a forma como o modelo é treinado. Imagina que estás a trabalhar numa empresa onde o grupo de ciência de dados treinou um modelo que querem que utilizes numa aplicação. ### Considerações Há muitas perguntas que precisas de fazer: - **É uma aplicação web ou uma aplicação móvel?** Se estás a construir uma aplicação móvel ou precisas de usar o modelo num contexto de IoT, podes usar [TensorFlow Lite](https://www.tensorflow.org/lite/) e utilizar o modelo numa aplicação Android ou iOS. - **Onde o modelo vai residir?** Na nuvem ou localmente? - **Suporte offline.** A aplicação precisa de funcionar offline? - **Que tecnologia foi usada para treinar o modelo?** A tecnologia escolhida pode influenciar as ferramentas que precisas de usar. - **Usando TensorFlow.** Se estás a treinar um modelo com TensorFlow, por exemplo, esse ecossistema permite converter um modelo TensorFlow para uso numa aplicação web utilizando [TensorFlow.js](https://www.tensorflow.org/js/). - **Usando PyTorch.** Se estás a construir um modelo com uma biblioteca como [PyTorch](https://pytorch.org/), tens a opção de exportá-lo no formato [ONNX](https://onnx.ai/) (Open Neural Network Exchange) para uso em aplicações web JavaScript que podem utilizar o [Onnx Runtime](https://www.onnxruntime.ai/). Esta opção será explorada numa lição futura para um modelo treinado com Scikit-learn. - **Usando Lobe.ai ou Azure Custom Vision.** Se estás a usar um sistema ML SaaS (Software como Serviço) como [Lobe.ai](https://lobe.ai/) ou [Azure Custom Vision](https://azure.microsoft.com/services/cognitive-services/custom-vision-service/?WT.mc_id=academic-77952-leestott) para treinar um modelo, este tipo de software fornece formas de exportar o modelo para várias plataformas, incluindo construir uma API personalizada para ser consultada na nuvem pela tua aplicação online. Também tens a oportunidade de construir uma aplicação web Flask completa que seria capaz de treinar o modelo diretamente no navegador. Isso também pode ser feito utilizando TensorFlow.js num contexto JavaScript. Para os nossos propósitos, como temos trabalhado com notebooks baseados em Python, vamos explorar os passos necessários para exportar um modelo treinado de um notebook para um formato legível por uma aplicação web construída em Python. ## Ferramenta Para esta tarefa, precisas de duas ferramentas: Flask e Pickle, ambas executadas em Python. ✅ O que é [Flask](https://palletsprojects.com/p/flask/)? Definido como um 'micro-framework' pelos seus criadores, Flask fornece as funcionalidades básicas de frameworks web utilizando Python e um motor de templates para construir páginas web. Dá uma olhada neste [módulo de aprendizagem](https://docs.microsoft.com/learn/modules/python-flask-build-ai-web-app?WT.mc_id=academic-77952-leestott) para praticar a construção com Flask. ✅ O que é [Pickle](https://docs.python.org/3/library/pickle.html)? Pickle 🥒 é um módulo Python que serializa e desserializa uma estrutura de objetos Python. Quando 'pickle' um modelo, estás a serializar ou achatar a sua estrutura para uso na web. Atenção: pickle não é intrinsecamente seguro, por isso tem cuidado se fores solicitado a 'des-picklar' um ficheiro. Um ficheiro pickled tem o sufixo `.pkl`. ## Exercício - limpar os dados Nesta lição vais usar dados de 80.000 avistamentos de OVNIs, recolhidos pelo [NUFORC](https://nuforc.org) (Centro Nacional de Relatórios de OVNIs). Estes dados têm descrições interessantes de avistamentos de OVNIs, por exemplo: - **Descrição longa de exemplo.** "Um homem emerge de um feixe de luz que brilha num campo de relva à noite e corre em direção ao estacionamento da Texas Instruments". - **Descrição curta de exemplo.** "as luzes perseguiram-nos". A folha de cálculo [ufos.csv](../../../../3-Web-App/1-Web-App/data/ufos.csv) inclui colunas sobre a `cidade`, `estado` e `país` onde o avistamento ocorreu, a `forma` do objeto e a sua `latitude` e `longitude`. No [notebook](../../../../3-Web-App/1-Web-App/notebook.ipynb) em branco incluído nesta lição: 1. Importa `pandas`, `matplotlib` e `numpy` como fizeste nas lições anteriores e importa a folha de cálculo de OVNIs. Podes dar uma olhada num conjunto de dados de exemplo: ```python import pandas as pd import numpy as np ufos = pd.read_csv('./data/ufos.csv') ufos.head() ``` 1. Converte os dados de OVNIs para um pequeno dataframe com títulos novos. Verifica os valores únicos no campo `Country`. ```python ufos = pd.DataFrame({'Seconds': ufos['duration (seconds)'], 'Country': ufos['country'],'Latitude': ufos['latitude'],'Longitude': ufos['longitude']}) ufos.Country.unique() ``` 1. Agora, podes reduzir a quantidade de dados com que precisas de lidar ao eliminar valores nulos e importar apenas avistamentos entre 1-60 segundos: ```python ufos.dropna(inplace=True) ufos = ufos[(ufos['Seconds'] >= 1) & (ufos['Seconds'] <= 60)] ufos.info() ``` 1. Importa a biblioteca `LabelEncoder` do Scikit-learn para converter os valores de texto dos países para números: ✅ LabelEncoder codifica os dados alfabeticamente ```python from sklearn.preprocessing import LabelEncoder ufos['Country'] = LabelEncoder().fit_transform(ufos['Country']) ufos.head() ``` Os teus dados devem parecer-se com isto: ```output Seconds Country Latitude Longitude 2 20.0 3 53.200000 -2.916667 3 20.0 4 28.978333 -96.645833 14 30.0 4 35.823889 -80.253611 23 60.0 4 45.582778 -122.352222 24 3.0 3 51.783333 -0.783333 ``` ## Exercício - construir o modelo Agora podes preparar-te para treinar um modelo dividindo os dados em grupos de treino e teste. 1. Seleciona as três características que queres treinar como o teu vetor X, e o vetor y será o `Country`. Queres ser capaz de inserir `Seconds`, `Latitude` e `Longitude` e obter um id de país como retorno. ```python from sklearn.model_selection import train_test_split Selected_features = ['Seconds','Latitude','Longitude'] X = ufos[Selected_features] y = ufos['Country'] X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=0) ``` 1. Treina o modelo utilizando regressão logística: ```python from sklearn.metrics import accuracy_score, classification_report from sklearn.linear_model import LogisticRegression model = LogisticRegression() model.fit(X_train, y_train) predictions = model.predict(X_test) print(classification_report(y_test, predictions)) print('Predicted labels: ', predictions) print('Accuracy: ', accuracy_score(y_test, predictions)) ``` A precisão não é má **(cerca de 95%)**, o que não é surpreendente, já que `Country` e `Latitude/Longitude` estão correlacionados. O modelo que criaste não é muito revolucionário, pois deverias ser capaz de inferir um `Country` a partir da sua `Latitude` e `Longitude`, mas é um bom exercício para tentar treinar a partir de dados brutos que limpaste, exportaste e depois usaste este modelo numa aplicação web. ## Exercício - 'pickle' o modelo Agora, é hora de _picklar_ o modelo! Podes fazer isso em algumas linhas de código. Uma vez _pickled_, carrega o modelo pickled e testa-o contra um array de dados de exemplo contendo valores para segundos, latitude e longitude. ```python import pickle model_filename = 'ufo-model.pkl' pickle.dump(model, open(model_filename,'wb')) model = pickle.load(open('ufo-model.pkl','rb')) print(model.predict([[50,44,-12]])) ``` O modelo retorna **'3'**, que é o código de país para o Reino Unido. Incrível! 👽 ## Exercício - construir uma aplicação Flask Agora podes construir uma aplicação Flask para chamar o modelo e retornar resultados semelhantes, mas de uma forma mais visualmente agradável. 1. Começa por criar uma pasta chamada **web-app** ao lado do ficheiro _notebook.ipynb_ onde o teu ficheiro _ufo-model.pkl_ reside. 1. Nessa pasta, cria mais três pastas: **static**, com uma pasta **css** dentro dela, e **templates**. Deves agora ter os seguintes ficheiros e diretórios: ```output web-app/ static/ css/ templates/ notebook.ipynb ufo-model.pkl ``` ✅ Consulta a pasta de solução para ver a aplicação finalizada 1. O primeiro ficheiro a criar na pasta _web-app_ é o ficheiro **requirements.txt**. Tal como _package.json_ numa aplicação JavaScript, este ficheiro lista as dependências necessárias para a aplicação. No **requirements.txt** adiciona as linhas: ```text scikit-learn pandas numpy flask ``` 1. Agora, executa este ficheiro navegando até _web-app_: ```bash cd web-app ``` 1. No terminal, digita `pip install` para instalar as bibliotecas listadas no _requirements.txt_: ```bash pip install -r requirements.txt ``` 1. Agora, estás pronto para criar mais três ficheiros para finalizar a aplicação: 1. Cria **app.py** na raiz. 2. Cria **index.html** na pasta _templates_. 3. Cria **styles.css** na pasta _static/css_. 1. Desenvolve o ficheiro _styles.css_ com alguns estilos: ```css body { width: 100%; height: 100%; font-family: 'Helvetica'; background: black; color: #fff; text-align: center; letter-spacing: 1.4px; font-size: 30px; } input { min-width: 150px; } .grid { width: 300px; border: 1px solid #2d2d2d; display: grid; justify-content: center; margin: 20px auto; } .box { color: #fff; background: #2d2d2d; padding: 12px; display: inline-block; } ``` 1. Em seguida, desenvolve o ficheiro _index.html_: ```html
According to the number of seconds, latitude and longitude, which country is likely to have reported seeing a UFO?
{{ prediction_text }}