# Bouw een webapp om een ML-model te gebruiken In deze les ga je een ML-model trainen op een dataset die letterlijk buitenaards is: _UFO-waarnemingen van de afgelopen eeuw_, afkomstig uit de NUFORC-database. Je leert: - Hoe je een getraind model kunt 'pickle' - Hoe je dat model kunt gebruiken in een Flask-app We blijven notebooks gebruiken om data schoon te maken en ons model te trainen, maar je kunt het proces een stap verder brengen door te verkennen hoe je een model 'in het wild' kunt gebruiken, bijvoorbeeld in een webapp. Om dit te doen, moet je een webapp bouwen met Flask. ## [Pre-lecture quiz](https://ff-quizzes.netlify.app/en/ml/) ## Een app bouwen Er zijn verschillende manieren om webapps te bouwen die machine learning-modellen gebruiken. Je webarchitectuur kan invloed hebben op de manier waarop je model wordt getraind. Stel je voor dat je werkt in een bedrijf waar de data science-afdeling een model heeft getraind dat ze willen dat jij gebruikt in een app. ### Overwegingen Er zijn veel vragen die je moet stellen: - **Is het een webapp of een mobiele app?** Als je een mobiele app bouwt of het model in een IoT-context wilt gebruiken, kun je [TensorFlow Lite](https://www.tensorflow.org/lite/) gebruiken en het model toepassen in een Android- of iOS-app. - **Waar zal het model zich bevinden?** In de cloud of lokaal? - **Offline ondersteuning.** Moet de app offline werken? - **Welke technologie is gebruikt om het model te trainen?** De gekozen technologie kan invloed hebben op de tools die je moet gebruiken. - **Gebruik van TensorFlow.** Als je een model traint met TensorFlow, biedt dat ecosysteem de mogelijkheid om een TensorFlow-model te converteren voor gebruik in een webapp met [TensorFlow.js](https://www.tensorflow.org/js/). - **Gebruik van PyTorch.** Als je een model bouwt met een bibliotheek zoals [PyTorch](https://pytorch.org/), kun je het exporteren in [ONNX](https://onnx.ai/) (Open Neural Network Exchange)-formaat voor gebruik in JavaScript-webapps die de [Onnx Runtime](https://www.onnxruntime.ai/) kunnen gebruiken. Deze optie wordt in een toekomstige les verkend voor een Scikit-learn-getraind model. - **Gebruik van Lobe.ai of Azure Custom Vision.** Als je een ML SaaS (Software as a Service)-systeem zoals [Lobe.ai](https://lobe.ai/) of [Azure Custom Vision](https://azure.microsoft.com/services/cognitive-services/custom-vision-service/?WT.mc_id=academic-77952-leestott) gebruikt om een model te trainen, biedt dit type software manieren om het model te exporteren voor veel platforms, inclusief het bouwen van een op maat gemaakte API die in de cloud kan worden geraadpleegd door je online applicatie. Je hebt ook de mogelijkheid om een volledige Flask-webapp te bouwen die het model zelf kan trainen in een webbrowser. Dit kan ook worden gedaan met TensorFlow.js in een JavaScript-context. Voor onze doeleinden, aangezien we hebben gewerkt met Python-gebaseerde notebooks, laten we de stappen verkennen die je moet nemen om een getraind model uit zo'n notebook te exporteren naar een formaat dat leesbaar is door een Python-gebouwde webapp. ## Tool Voor deze taak heb je twee tools nodig: Flask en Pickle, beide draaien op Python. ✅ Wat is [Flask](https://palletsprojects.com/p/flask/)? Door de makers gedefinieerd als een 'micro-framework', biedt Flask de basisfuncties van webframeworks met Python en een template-engine om webpagina's te bouwen. Bekijk [deze Learn-module](https://docs.microsoft.com/learn/modules/python-flask-build-ai-web-app?WT.mc_id=academic-77952-leestott) om te oefenen met het bouwen met Flask. ✅ Wat is [Pickle](https://docs.python.org/3/library/pickle.html)? Pickle 🥒 is een Python-module die een Python-objectstructuur serialiseert en deserialiseert. Wanneer je een model 'pickled', serialiseer of 'plat' je de structuur voor gebruik op het web. Let op: Pickle is niet intrinsiek veilig, dus wees voorzichtig als je wordt gevraagd een bestand te 'un-picklen'. Een pickled bestand heeft de extensie `.pkl`. ## Oefening - maak je data schoon In deze les gebruik je data van 80.000 UFO-waarnemingen, verzameld door [NUFORC](https://nuforc.org) (The National UFO Reporting Center). Deze data bevat enkele interessante beschrijvingen van UFO-waarnemingen, bijvoorbeeld: - **Lange voorbeeldbeschrijving.** "Een man komt tevoorschijn uit een lichtstraal die 's nachts op een grasveld schijnt en rent naar de parkeerplaats van Texas Instruments". - **Korte voorbeeldbeschrijving.** "de lichten achtervolgden ons". De [ufos.csv](../../../../3-Web-App/1-Web-App/data/ufos.csv) spreadsheet bevat kolommen over de `stad`, `staat` en `land` waar de waarneming plaatsvond, de `vorm` van het object en de `latitude` en `longitude`. In het lege [notebook](../../../../3-Web-App/1-Web-App/notebook.ipynb) dat bij deze les is inbegrepen: 1. Importeer `pandas`, `matplotlib` en `numpy` zoals je in eerdere lessen deed en importeer de ufos-spreadsheet. Je kunt een voorbeelddataset bekijken: ```python import pandas as pd import numpy as np ufos = pd.read_csv('./data/ufos.csv') ufos.head() ``` 1. Converteer de ufos-data naar een kleine dataframe met nieuwe titels. Controleer de unieke waarden in het `Land`-veld. ```python ufos = pd.DataFrame({'Seconds': ufos['duration (seconds)'], 'Country': ufos['country'],'Latitude': ufos['latitude'],'Longitude': ufos['longitude']}) ufos.Country.unique() ``` 1. Nu kun je de hoeveelheid data die we moeten verwerken verminderen door null-waarden te verwijderen en alleen waarnemingen tussen 1-60 seconden te importeren: ```python ufos.dropna(inplace=True) ufos = ufos[(ufos['Seconds'] >= 1) & (ufos['Seconds'] <= 60)] ufos.info() ``` 1. Importeer Scikit-learn's `LabelEncoder`-bibliotheek om de tekstwaarden voor landen om te zetten naar een nummer: ✅ LabelEncoder codeert data alfabetisch ```python from sklearn.preprocessing import LabelEncoder ufos['Country'] = LabelEncoder().fit_transform(ufos['Country']) ufos.head() ``` Je data zou er nu zo uit moeten zien: ```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 ``` ## Oefening - bouw je model Nu kun je je voorbereiden om een model te trainen door de data te verdelen in een trainings- en testgroep. 1. Selecteer de drie kenmerken waarop je wilt trainen als je X-vector, en de y-vector zal het `Land` zijn. Je wilt in staat zijn om `Seconden`, `Latitude` en `Longitude` in te voeren en een land-id te krijgen als resultaat. ```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. Train je model met behulp van logistieke regressie: ```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)) ``` De nauwkeurigheid is niet slecht **(ongeveer 95%)**, wat niet verrassend is, aangezien `Land` en `Latitude/Longitude` correleren. Het model dat je hebt gemaakt is niet erg revolutionair, omdat je een `Land` zou moeten kunnen afleiden uit de `Latitude` en `Longitude`, maar het is een goede oefening om te proberen te trainen met ruwe data die je hebt schoongemaakt, geëxporteerd en vervolgens dit model te gebruiken in een webapp. ## Oefening - 'pickle' je model Nu is het tijd om je model te _picklen_! Je kunt dit doen in een paar regels code. Zodra het is _gepickled_, laad je je gepickled model en test je het tegen een voorbeelddata-array met waarden voor seconden, latitude en 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]])) ``` Het model retourneert **'3'**, wat de landcode is voor het VK. Wauw! 👽 ## Oefening - bouw een Flask-app Nu kun je een Flask-app bouwen om je model aan te roepen en vergelijkbare resultaten te retourneren, maar op een visueel aantrekkelijkere manier. 1. Begin met het maken van een map genaamd **web-app** naast het _notebook.ipynb_-bestand waar je _ufo-model.pkl_-bestand zich bevindt. 1. Maak in die map drie extra mappen: **static**, met een map **css** erin, en **templates**. Je zou nu de volgende bestanden en mappen moeten hebben: ```output web-app/ static/ css/ templates/ notebook.ipynb ufo-model.pkl ``` ✅ Raadpleeg de oplossingmap voor een weergave van de voltooide app 1. Het eerste bestand dat je moet maken in de _web-app_-map is het **requirements.txt**-bestand. Net zoals _package.json_ in een JavaScript-app, bevat dit bestand de afhankelijkheden die nodig zijn voor de app. Voeg in **requirements.txt** de regels toe: ```text scikit-learn pandas numpy flask ``` 1. Voer nu dit bestand uit door naar _web-app_ te navigeren: ```bash cd web-app ``` 1. Typ in je terminal `pip install` om de bibliotheken te installeren die in _requirements.txt_ worden vermeld: ```bash pip install -r requirements.txt ``` 1. Nu ben je klaar om drie extra bestanden te maken om de app af te maken: 1. Maak **app.py** in de root. 2. Maak **index.html** in de _templates_-map. 3. Maak **styles.css** in de _static/css_-map. 1. Bouw het _styles.css_-bestand uit met een paar stijlen: ```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. Bouw vervolgens het _index.html_-bestand uit: ```html
According to the number of seconds, latitude and longitude, which country is likely to have reported seeing a UFO?
{{ prediction_text }}