Last chance! 50% off DataCamp Premium
Sale ends in
Stel je de volgende situatie voor:
Je hebt een supergaaf machinelearningmodel gebouwd dat kan voorspellen of een bepaalde transactie frauduleus is of niet. Een vriend van je ontwikkelt nu een Android-app voor algemene bankactiviteiten en wil jouw machinelearningmodel integreren in zijn app voor dit doel.
Maar je vriend ontdekte dat jij je model in Python hebt gecodeerd, terwijl hij zijn app in Java bouwt. En dus? Is het dan niet mogelijk om je machinelearningmodel in de app van je vriend te integreren?
Gelukkig is er de kracht van API’s. En de bovenstaande situatie is er één van de vele waarin het enorm belangrijk is om je machinelearningmodellen om te zetten in API’s. Veel bedrijven zoeken nu naar data scientists die dit kunnen. Een machinelearningmodel inpakken als een API is niet heel moeilijk, en precies dat ga je in deze tutorial doen – Zet je machinelearningmodel om in een API.
Meestal ligt het echte nut van je machinelearningmodel in de kern van een intelligent product – dat kan een kleine component van een aanbevelingssysteem zijn of een slimme chatbot. Juist dan lijken de barrières lastig te overwinnen.
Zo gebruiken de meeste ML-practitioners R/Python voor hun experimenten. Maar de afnemers van die ML-modellen zijn vaak software-engineers met een totaal andere technologiestack. Er zijn twee manieren om dit probleem op te lossen:
Voordat we verdergaan, kijken we eerst wat een API nu eigenlijk is.
"Eenvoudig gezegd is een API een (hypothetisch) contract tussen 2 softwarepakketten dat stelt dat als de gebruikerssoftware input levert in een vooraf gedefinieerd formaat, de andere zijn functionaliteit zal uitbreiden en de uitkomst zal teruggeven aan de gebruikerssoftware." - Analytics Vidhya
Lees de volgende artikelen om te begrijpen waarom API’s populair zijn onder developers:
In essentie lijken API’s erg op webapplicaties, maar in plaats van een mooi opgemaakte HTML-pagina te geven, retourneren API’s data in een standaard data-uitwisselingsformaat zoals JSON, XML, enzovoort. Zodra een developer de gewenste output heeft, kan die het opmaken zoals hij wil. Er zijn ook veel populaire ML-API’s, bijvoorbeeld de ML-API van IBM Watson, die het volgende kan:
Google Vision API is ook een uitstekend voorbeeld en biedt specifieke services voor computervisiontaken. Klik hier om een idee te krijgen van wat je met de Google Vision API kunt doen.
In feite bieden veel cloudproviders en kleinere, op machine learning gerichte bedrijven kant-en-klare API’s. Ze bedienen developers/bedrijven zonder ML-expertise die ML in hun processen of productaanbod willen integreren.
Populaire voorbeelden van machinelearning-API’s die expliciet geschikt zijn voor webontwikkeling zijn DialogFlow, Microsoft’s Cognitive Toolkit, TensorFlow.js, enzovoort.
Nu je een goed beeld hebt van wat API’s zijn, kijken we hoe je een machinelearningmodel (ontwikkeld in Python) als een API kunt aanbieden in Python.
Je vraagt je misschien af: wat is een webservice? Een webservice is in feite een vorm van API die veronderstelt dat een API op een server wordt gehost en kan worden geconsumeerd. Web-API, webservice – deze termen worden meestal door elkaar gebruikt.
Dan nu Flask: dit is een framework voor het ontwikkelen van webservices in Python. Het is niet de enige in Python; er zijn er nog een paar, zoals Django, Falcon, Hug, enzovoort. Maar in deze tutorial gebruik je Flask. Om meer te leren over Flask kun je deze tutorials raadplegen.
Als je de Anaconda-distributie hebt gedownload, staat Flask al geïnstalleerd. Anders moet je het zelf installeren met:
pip install flask
Flask is erg minimalistisch. Flask is om veel redenen favoriet bij Python-developers. Het framework wordt geleverd met een ingebouwde, lichte webserver die minimale configuratie vereist en vanuit je Python-code kan worden aangestuurd. Dat is een van de redenen waarom het zo populair is.
De volgende code laat Flask’s minimalisme mooi zien. De code creëert een eenvoudige Web-API die bij een bepaald URL-patroon een specifieke output teruggeeft.
from flask import Flask
app = Flask(__name__)
@app.route("")
def hello():
return "Welcome to machine learning model APIs!"
if __name__ == '__main__':
app.run(debug=True)
Na uitvoeren kun je naar het webadres gaan (voer het adres in een webbrowser in) dat op de terminal wordt getoond en het resultaat bekijken.

Jupyter Notebooks zijn geweldig voor alles met markdowns, R en Python. Maar voor het bouwen van een webserver kan het inconsistent gedrag vertonen. Het is dus een goed idee om Flask-code in een teksteditor zoals Sublime te schrijven en de code vanuit de terminal/opdrachtprompt uit te voeren.
Zorg dat je het bestand niet flask.py noemt.
Flask draait standaard op poort 5000. Soms start de Flask-server succesvol op deze poort, maar wanneer je de URL (die de server in de terminal toont) in een webbrowser of in een API-client zoals Postman opent, krijg je mogelijk geen output. Bekijk de volgende situatie:

Volgens Flask is de server succesvol gestart op poort 5000, maar bij het openen van de URL in de browser kwam er niets terug. Dit kan duiden op een poortconflict. In dat geval is het verstandig de standaardpoort 5000 te wijzigen naar een gewenste poort. Dat kan zo:
app.run(debug=True,port=12345)
Dan ziet de Flask-server er ongeveer zo uit:

Nu lopen we stap voor stap door de code die je schreef:
Je maakte een instance van de Flask-klasse en gaf de variabele "name" (door Python zelf gevuld) door. Deze variabele is "main" als dit bestand direct als script door Python wordt uitgevoerd. Als je het bestand importeert, is de waarde van "name" de bestandsnaam die je importeerde. Als je bijvoorbeeld test.py en run.py hebt en test.py importeert in run.py, dan is de "name"-waarde van test.py test (app = Flask(test)).
Boven de definitie van de methode hello() staat @app.route(""). route() is een decorator die Flask vertelt welke URL de functie hello() moet triggeren.
De methode hello() is verantwoordelijk voor het produceren van een output (Welcome to machine learning model APIs!) zodra je API correct wordt aangeroepen (geconsumeerd). In dit geval levert het openen van localhost:5000/ in een webbrowser de bedoelde output op (mits de Flask-server op poort 5000 draait).
Nu bekijk je een aantal factoren waar je rekening mee moet houden als je je machinelearningmodellen (gebouwd met scikit-learn) omzet naar een Flask-API.
Het is nog nooit zo eenvoudig geweest om in Python van zeer simpele tot zeer complexe machinelearningmodellen te maken met scikit-learn. Maar er zijn een paar punten om te onthouden over scikit-learn:
(Bekijk zeker DataCamps cursus Supervised Learning met scikit-learn, gegeven door de core developer van scikit-learn – Andreas Müller)
LabelEncoder, OneHotEncoder, enzovoort. Deze vind je in de module sklearn.preprocessing. sklearn.preprocessing.Labelencoding en ontbrekende waarden zijn belangrijke datavoorbewerkingstappen die essentieel zijn voor het bouwen van een goed machinelearningmodel. Wil je hier meer over leren, bekijk dan deze DataCamp-cursus:
Voor deze tutorial gebruik je de Titanic-dataset, een van de populairste datasets, onder andere omdat de dataset verschillende typen variabelen bevat en ook ontbrekende waarden heeft. Deze DataCamp-tutorial bevat een uitstekende analyse van de dataset, en je kunt de dataset hier downloaden.
Deze dataset behandelt een classificatieprobleem: voorspellen of een passagier zou overleven op basis van enkele gegevens over hem/haar.
Opmerking: De termen variabelen en features worden in deze tutorial vaak door elkaar gebruikt.
Om het nog verder te vereenvoudigen, gebruik je slechts vier variabelen: age, sex, embarked en survived, waarbij survived het klasselabel is.
# Import dependencies
import pandas as pd
import numpy as np
# Load the dataset in a dataframe object and include only four features as mentioned
url = "http://s3.amazonaws.com/assets.datacamp.com/course/Kaggle/train.csv"
df = pd.read_csv(url)
include = ['Age', 'Sex', 'Embarked', 'Survived'] # Only four features
df_ = df[include]
"Sex" en "Embarked" zijn categorische features met niet-numerieke waarden en hebben daarom numerieke transformaties nodig. De feature “Age” bevat ontbrekende waarden. Deze waarden kunnen worden geïmputeerd met een samenvattende statistiek zoals mediaan of gemiddelde. Ontbrekende waarden kunnen veelzeggend zijn; het is de moeite waard te onderzoeken wat ze in real-life toepassingen betekenen.
Scikit-learn behandelt celwaarden die niets bevatten als NaN’s. Hier vervang je NaN’s simpelweg door 0, en je schrijft daar een hulpfunctie voor.
categoricals = []
for col, col_type in df_.dtypes.iteritems():
if col_type == 'O':
categoricals.append(col)
else:
df_[col].fillna(0, inplace=True)
De bovenstaande regels code doen het volgende:
df en de kolommen (met niet-numerieke waarden) toevoegen aan een lijst categorical.Als kolommen geen niet-numerieke waarden hebben (wat in dit geval alleen Age is), dan wordt gecontroleerd of er missende waarden zijn en die worden gevuld met 0.
NaN’s vullen met één enkele waarde kan onbedoelde gevolgen hebben, vooral als de waarde waarmee je vervangt binnen het geobserveerde bereik van de numerieke variabele ligt. Omdat nul geen geobserveerde en legitieme leeftijdswaarde is, introduceer je geen bias, wat wel het geval zou zijn als je bijvoorbeeld 36 gebruikte! - Bron
Nu je de ontbrekende waarden hebt afgehandeld en de niet-numerieke kolommen hebt gescheiden, kun je ze omzetten naar numeriek. Dat doe je met one-hot encoding. Pandas biedt een eenvoudige methode get_dummies() om OHE-variabelen te maken voor een gegeven dataframe.
df_ohe = pd.get_dummies(df_, columns=categoricals, dummy_na=True)
Bij OHE wordt voor elke kolom/waarde-combinatie een nieuwe kolom gemaakt, in een kolom_waarde-formaat. Bijvoorbeeld – voor de variabele “Embarked” levert OHE “Embarked_C”, “Embarked_Q”, “Embarked_S” en “Embarked_nan” op.
Nu je de dataset succesvol hebt voorbewerkt, ben je klaar om het machinelearningmodel te trainen. Je gebruikt hiervoor een Logistic Regression-classifier.
from sklearn.linear_model import LogisticRegression
dependent_variable = 'Survived'
x = df_ohe[df_ohe.columns.difference([dependent_variable])]
y = df_ohe[dependent_variable]
lr = LogisticRegression()
lr.fit(x, y)
LogisticRegression(C=1.0, class_weight=None, dual=False, fit_intercept=True,
intercept_scaling=1, max_iter=100, multi_class='ovr', n_jobs=1,
penalty='l2', random_state=None, solver='liblinear', tol=0.0001,
verbose=0, warm_start=False)
Je hebt je machinelearningmodel gebouwd. Nu ga je dit model opslaan. Technisch heet dit het serialiseren van het model. In Python noem je dit pickling.
Hiervoor gebruik je joblib van sklearn.
from sklearn.externals import joblib
joblib.dump(lr, 'model.pkl')
['model.pkl']
Het Logistic Regression-model is nu gepersisteerd. Je kunt dit model met één regel code weer in het geheugen laden. Het model terugladen in je werkomgeving heet deserialisatie.
lr = joblib.load('model.pkl')
Je bent nu klaar om Flask te gebruiken om je gepersisteerde model te serveren. Je hebt al gezien hoe minimalistisch Flask is om mee te starten.
Om je model met Flask te serveren, doe je twee dingen:
Concreet ziet je voorbeeldinput voor de API er zo uit:
[
{"Age": 85, "Sex": "male", "Embarked": "S"},
{"Age": 24, "Sex": '"female"', "Embarked": "C"},
{"Age": 3, "Sex": "male", "Embarked": "C"},
{"Age": 21, "Sex": "male", "Embarked": "S"}
]
(dit is een JSON-list met inputs)
en je API geeft zoiets terug:
{"prediction": [0, 1, 1, 0]}
De voorspellingen geven de overlevingsstatussen aan, waarbij 0 Nee betekent en 1 Ja.
JSON staat voor JavaScript Object Notation en is een van de meest gebruikte data-uitwisselingsformaten. Als je een snelle introductie nodig hebt, volg dan deze tutorials.
Laten we een functie predict() schrijven die het volgende doet:
Je hebt al gezien hoe je een gepersisteerd model laadt. Nu focus je op hoe je het kunt gebruiken om de overlevingsstatus te voorspellen zodra er input binnenkomt.
from flask import Flask, jsonify
app = Flask(__name__)
@app.route('/predict', methods=['POST'])
def predict():
json_ = request.json
query_df = pd.DataFrame(json_)
query = pd.get_dummies(query_df)
prediction = lr.predict(query)
return jsonify({'prediction': list(prediction)})
Fantastisch! Maar er is een klein probleem.
De functie die je schreef werkt alleen als het inkomende verzoek alle mogelijke waarden voor de categorische variabelen bevat, wat in de praktijk wel of niet het geval kan zijn. Als het inkomende verzoek niet alle mogelijke waarden van de categorische variabelen bevat, dan zal get_dummies() volgens de huidige definitie van predict() een dataframe genereren met minder kolommen dan de classifier verwacht, wat resulteert in een runtimefout.
Om dit op te lossen, persisteer je tijdens het trainen ook de kolomlijst. Je kunt elk Python-object serialiseren naar een .pkl-bestand. Je gebruikt joblib op dezelfde manier als eerder.
(Onthoud dat het, zoals eerder besproken, beter is om al het servergerelateerde programmeren in een teksteditor te doen en het daarna vanuit een terminal uit te voeren)
model_columns = list(x.columns)
joblib.dump(model_columns, 'model_columns.pkl')
['model_columns.pkl']
Omdat je de kolomlijst al hebt gepersisteerd, kun je de ontbrekende waarden afhandelen op het moment van voorspellen. Je moet de modelkolommen laden wanneer de applicatie start.
@app.route('/predict', methods=['POST']) # Your API endpoint URL would consist /predict
def predict():
if lr:
try:
json_ = request.json
query = pd.get_dummies(pd.DataFrame(json_))
query = query.reindex(columns=model_columns, fill_value=0)
prediction = list(lr.predict(query))
return jsonify({'prediction': prediction})
except:
return jsonify({'trace': traceback.format_exc()})
else:
print ('Train the model first')
return ('No model here to use')
Je hebt alle vereiste elementen in de "/predict"-API opgenomen en hoeft nu alleen nog de main-class te schrijven.
if __name__ == '__main__':
try:
port = int(sys.argv[1]) # This is for a command-line argument
except:
port = 12345 # If you don't provide any port then the port will be set to 12345
lr = joblib.load(model_file_name) # Load "model.pkl"
print ('Model loaded')
model_columns = joblib.load(model_columns_file_name) # Load "model_columns.pkl"
print ('Model columns loaded')
app.run(port=port, debug=True)
Je API is nu klaar om gehost te worden. Maar voordat we verdergaan, vatten we samen wat je tot nu toe hebt gedaan:
Laten we alle code op één plek zetten, zodat je niets mist. Het is bovendien goede programmeerpraktijk om je Logistic Regression-modelcode en je Flask-API-code te scheiden in aparte .py-bestanden.
Je model.py ziet er dan als volgt uit:
# Import dependencies
import pandas as pd
import numpy as np
# Load the dataset in a dataframe object and include only four features as mentioned
url = "http://s3.amazonaws.com/assets.datacamp.com/course/Kaggle/train.csv"
df = pd.read_csv(url)
include = ['Age', 'Sex', 'Embarked', 'Survived'] # Only four features
df_ = df[include]
# Data Preprocessing
categoricals = []
for col, col_type in df_.dtypes.iteritems():
if col_type == 'O':
categoricals.append(col)
else:
df_[col].fillna(0, inplace=True)
df_ohe = pd.get_dummies(df_, columns=categoricals, dummy_na=True)
# Logistic Regression classifier
from sklearn.linear_model import LogisticRegression
dependent_variable = 'Survived'
x = df_ohe[df_ohe.columns.difference([dependent_variable])]
y = df_ohe[dependent_variable]
lr = LogisticRegression()
lr.fit(x, y)
# Save your model
from sklearn.externals import joblib
joblib.dump(lr, 'model.pkl')
print("Model dumped!")
# Load the model that you just saved
lr = joblib.load('model.pkl')
# Saving the data columns from training
model_columns = list(x.columns)
joblib.dump(model_columns, 'model_columns.pkl')
print("Models columns dumped!")
Je api.py ziet er als volgt uit:
# Dependencies
from flask import Flask, request, jsonify
from sklearn.externals import joblib
import traceback
import pandas as pd
import numpy as np
# Your API definition
app = Flask(__name__)
@app.route('/predict', methods=['POST'])
def predict():
if lr:
try:
json_ = request.json
print(json_)
query = pd.get_dummies(pd.DataFrame(json_))
query = query.reindex(columns=model_columns, fill_value=0)
prediction = list(lr.predict(query))
return jsonify({'prediction': str(prediction)})
except:
return jsonify({'trace': traceback.format_exc()})
else:
print ('Train the model first')
return ('No model here to use')
if __name__ == '__main__':
try:
port = int(sys.argv[1]) # This is for a command-line input
except:
port = 12345 # If you don't provide any port the port will be set to 12345
lr = joblib.load("model.pkl") # Load "model.pkl"
print ('Model loaded')
model_columns = joblib.load("model_columns.pkl") # Load "model_columns.pkl"
print ('Model columns loaded')
app.run(port=port, debug=True)
Netjes! Nu ga je deze API testen in een API-client genaamd Postman. Zorg er even voor dat model.py en api.py in dezelfde map staan en dat je ze allebei hebt gecompileerd voordat je gaat testen. Zie de volgende snapshot van de terminal, genomen nadat beide .py-bestanden succesvol zijn gecompileerd.

Als al je bestanden succesvol zijn gecompileerd, zou de mapstructuur er als volgt uit moeten zien: 
Opmerking: Het IPYNB-bestand is optioneel.
Om je API te testen heb je een API-client nodig. Postman is zonder twijfel een van de beste. Je kunt Postman eenvoudig downloaden via de bovenstaande link.
De interface van Postman ziet er ongeveer zo uit als je de nieuwste versie hebt gedownload: 
Nadat je de Flask-server succesvol hebt gestart, moet je de juiste URL met het correcte poortnummer in Postman invoeren. Dat ziet er ongeveer zo uit:

Gefeliciteerd! Je hebt zojuist je allereerste machinelearning-API gebouwd.
Je API kan voorspellen of een passagier de Titanic-scheepsramp overleefde op basis van age, sex en embarked. Je vriend kan ‘m nu vanuit de front-endcode aanroepen en de output van de API verwerken tot iets interessants.
In deze tutorial heb je een van de meest gevraagde vaardigheden van een full-stack data scientist behandeld: een API bouwen op basis van een machinelearningmodel. Hoewel de API eenvoudig is, is het altijd beter om te beginnen met de simpelste dingen zodat je de ins en outs goed leert kennen.
Je kunt nog veel meer doen om dit te verbeteren. Mogelijke opties om te overwegen:
keras en bouw er een API van.De mogelijkheden en kansen zijn hier enorm. Je hoeft alleen zorgvuldig te kiezen welke het beste bij jou passen.
Wil je meer leren over machine learning in Python, volg dan DataCamps cursus Preprocessing for Machine Learning in Python en bekijk onze tutorial Machine Learning Basics - The Norms.
De volgende referenties zijn geraadpleegd bij het schrijven van deze blog:
Leer meer over Python en Machine Learning
Cursus
Cursus
Cursus

blog
Adel Nehme
15 min