Python Machine Learning Prediction con un'API REST Flask

Pubblicato: 2022-03-11

Questo articolo riguarda l'uso di Python nel contesto di un sistema di apprendimento automatico o di intelligenza artificiale (AI) per fare previsioni in tempo reale, con un'API REST di Flask. L'architettura qui esposta può essere vista come un modo per passare dalla prova di concetto (PoC) al prodotto minimo valido (MVP) per le applicazioni di apprendimento automatico.

Python non è la prima scelta a cui si può pensare quando si progetta una soluzione in tempo reale. Ma poiché Tensorflow e Scikit-Learn sono alcune delle librerie di apprendimento automatico più utilizzate supportate da Python, viene utilizzato convenientemente in molti PoC di Jupyter Notebook.

Ciò che rende fattibile questa soluzione è il fatto che l'allenamento richiede molto tempo rispetto alla previsione. Se pensi all'allenamento come al processo per guardare un film e prevedere le risposte alle domande su di esso, allora sembra abbastanza efficiente non dover rivedere il film dopo ogni nuova domanda.

La formazione è una sorta di visualizzazione compressa di quel "film" e la previsione consiste nel recuperare informazioni dalla visualizzazione compressa. Dovrebbe essere molto veloce, indipendentemente dal fatto che il film fosse complesso o lungo.

Implementiamolo con un rapido esempio [Flask] in Python!

Architettura di Machine Learning generica

Iniziamo delineando un flusso generico di formazione e architettura di previsione:

testo alternativo immagine

In primo luogo, viene creata una pipeline di formazione per conoscere i dati passati in base a una funzione obiettivo.

Questo dovrebbe produrre due elementi chiave:

  1. Funzioni di ingegneria delle funzionalità : le trasformazioni utilizzate durante l'allenamento dovrebbero essere riutilizzate al momento della previsione.
  2. Parametri del modello : l'algoritmo e gli iperparametri selezionati devono essere salvati, in modo che possano essere riutilizzati al momento della previsione

Si noti che l'ingegneria delle funzionalità eseguita durante il tempo di formazione deve essere salvata con cura per essere applicabile alla previsione. Un problema comune tra molti altri che possono emergere lungo il percorso è il ridimensionamento delle funzionalità, necessario per molti algoritmi.

Se la caratteristica X1 viene ridimensionata dal valore 1 a 1000 e viene riscalata all'intervallo [0,1] con una funzione f(x) = x/max(X1) , cosa accadrebbe se l'insieme di previsioni avesse un valore di 2000?

È necessario pensare in anticipo ad alcuni aggiustamenti attenti in modo che la funzione di mappatura restituisca output coerenti che verranno calcolati correttamente al momento della previsione.

Formazione sull'apprendimento automatico vs previsione

C'è una questione importante da affrontare qui. Perché stiamo separando formazione e previsione per cominciare?

È assolutamente vero che nel contesto di esempi e corsi di machine learning, in cui tutti i dati sono noti in anticipo (compresi i dati da prevedere), un modo molto semplice per costruire il predittore è impilare i dati di addestramento e previsione (solitamente chiamati un set di prova).

Quindi, è necessario allenarsi sul "set di formazione" e fare previsioni sul "set di test" per ottenere i risultati, mentre allo stesso tempo si esegue l'ingegneria delle funzionalità sia sui dati del treno che sui test, addestrando e prevedendo nella stessa pipeline unica .

Tuttavia, nei sistemi reali, di solito si hanno dati di addestramento e i dati da prevedere arrivano proprio mentre vengono elaborati. In altre parole, guardi il film in una sola volta e in seguito hai alcune domande a riguardo, il che significa che le risposte dovrebbero essere facili e veloci.

Inoltre, di solito non è necessario riqualificare l'intero modello ogni volta che arrivano nuovi dati poiché l'addestramento richiede tempo (potrebbero essere settimane per alcuni set di immagini) e dovrebbe essere sufficientemente stabile nel tempo.

Ecco perché formazione e previsione possono essere, o addirittura dovrebbero essere, chiaramente separate su molti sistemi, e questo riflette anche meglio il modo in cui un sistema intelligente (artificiale o meno) apprende.

La connessione con l'overfitting

La separazione tra formazione e previsione è anche un buon modo per affrontare il problema dell'overfitting.

Nelle statistiche, l'overfitting è "la produzione di un'analisi che corrisponde troppo strettamente o esattamente a un particolare insieme di dati e potrebbe, pertanto, non riuscire a adattare dati aggiuntivi o prevedere in modo affidabile osservazioni future".

testo alternativo immagine

La linea verde rappresenta un modello overfitted e la linea nera rappresenta un modello regolarizzato. Sebbene la linea verde segua al meglio i dati di addestramento, dipende troppo da quei dati ed è probabile che abbia un tasso di errore maggiore sui nuovi dati non visti, rispetto alla linea nera._

L'overfitting è particolarmente evidente nei set di dati con molte funzionalità o con set di dati con dati di addestramento limitati. In entrambi i casi, i dati hanno troppe informazioni rispetto a quanto può essere convalidato dal predittore e alcuni di essi potrebbero non essere nemmeno collegati alla variabile prevista. In questo caso, il rumore stesso potrebbe essere interpretato come un segnale.

Un buon modo per controllare l'overfitting è allenarsi su una parte dei dati e prevedere su un'altra parte su cui abbiamo la verità di base. Pertanto l'errore atteso sui nuovi dati è più o meno l'errore misurato su quel set di dati, a condizione che i dati su cui ci alleniamo siano rappresentativi della realtà del sistema e dei suoi stati futuri.

Quindi, se progettiamo un'adeguata pipeline di formazione e previsione insieme a una corretta suddivisione dei dati, non solo affrontiamo il problema dell'overfitting ma possiamo anche riutilizzare quell'architettura per prevedere nuovi dati.

L'ultimo passaggio sarebbe controllare che l'errore sui nuovi dati sia lo stesso previsto. C'è sempre uno spostamento (l'errore effettivo è sempre inferiore a quello previsto) e si dovrebbe determinare quale sia uno spostamento accettabile, ma questo non è l'argomento di questo articolo.

Un'API REST per la previsione

È qui che è utile separare chiaramente formazione e previsione. Se abbiamo salvato i nostri metodi di progettazione delle funzionalità e i parametri del nostro modello, possiamo creare una semplice API REST con questi elementi.

testo alternativo immagine

La chiave qui è caricare il modello e i parametri all'avvio dell'API. Una volta avviata e archiviata in memoria, ogni chiamata API attiva il calcolo dell'ingegneria delle funzionalità e il metodo "predict" dell'algoritmo ML. Entrambi sono generalmente abbastanza veloci da garantire una risposta in tempo reale.

L'API può essere progettata per accettare un esempio univoco da prevedere o diversi (previsioni batch).

Ecco il codice Python/Flask minimo che implementa questo principio, con JSON in e JSON out (domanda in entrata, risposta):

 app = Flask(__name__) @app.route('/api/makecalc/', methods=['POST']) def makecalc(): """ Function run at each API call No need to re-load the model """ # reads the received json jsonfile = request.get_json() res = dict() for key in jsonfile.keys(): # calculates and predicts res[key] = model.predict(doTheCalculation(key)) # returns a json file return jsonify(res) if __name__ == '__main__': # Model is loaded when the API is launched model = pickle.load(open('modelfile', 'rb')) app.run(debug=True)

Si noti che l'API può essere utilizzata per la previsione da nuovi dati, ma non è consigliabile utilizzarla per addestrare il modello. Potrebbe essere utilizzato, ma questo complica il codice di addestramento del modello e potrebbe essere più impegnativo in termini di risorse di memoria.

Esempio di implementazione - Bike Sharing

Prendiamo un set di dati Kaggle, il bike sharing, come esempio. Supponiamo di essere una società di bike sharing che vuole prevedere il numero di noleggi di biciclette ogni giorno per gestire al meglio la manutenzione della bicicletta, la logistica e altri aspetti dell'attività.

testo alternativo immagine

Gli affitti dipendono principalmente dalle condizioni meteorologiche, quindi con le previsioni del tempo, quella società potrebbe avere un'idea migliore quando gli affitti raggiungeranno il picco e cercare di evitare la manutenzione in questi giorni.

Per prima cosa, formiamo un modello e lo salviamo come un oggetto pickle che può essere visto nel taccuino di Jupyter.

La formazione e le prestazioni del modello non vengono trattate qui, questo è solo un esempio per comprendere l'intero processo.

Quindi scriviamo la trasformazione dei dati che verrà eseguita ad ogni chiamata API:

 import numpy as np import pandas as pd from datetime import date def doTheCalculation(data): data['dayofyear']=(data['dteday']- data['dteday'].apply(lambda x: date(x.year,1,1)) .astype('datetime64[ns]')).apply(lambda x: x.days) X = np.array(data[['instant','season','yr','holiday','weekday','workingday', 'weathersit','temp','atemp','hum','windspeed','dayofyear']]) return X

Questo è solo un calcolo di una variabile (giorno dell'anno) per includere sia il mese che il giorno preciso. C'è anche una selezione di colonne e il rispettivo ordine da mantenere.

Dobbiamo, quindi, scrivere l'API REST con Flask:

 from flask import Flask, request, redirect, url_for, flash, jsonify from features_calculation import doTheCalculation import json, pickle import pandas as pd import numpy as np app = Flask(__name__) @app.route('/api/makecalc/', methods=['POST']) def makecalc(): """ Function run at each API call """ jsonfile = request.get_json() data = pd.read_json(json.dumps(jsonfile),orient='index',convert_dates=['dteday']) print(data) res = dict() ypred = model.predict(doTheCalculation(data)) for i in range(len(ypred)): res[i] = ypred[i] return jsonify(res) if __name__ == '__main__': modelfile = 'modelfile.pickle' model = pickle.load(open(modelfile, 'rb')) print("loaded OK") app.run(debug=True)

Esegui questo programma, servirà l'API sulla porta 5000 per impostazione predefinita.

Se testiamo una richiesta localmente, sempre con Python:

 import requests, json url = '[http://127.0.0.1:5000/api/makecalc/](http://127.0.0.1:5000/api/makecalc/)' text = json.dumps({"0":{"instant":1,"dteday":"2011-01-01T00:00:00.000Z","season":1,"yr":0,"mnth":1,"holiday":0,"weekday":6,"workingday":0,"weathersit":2,"temp":0.344167,"atemp":0.363625,"hum":0.805833,"windspeed":0.160446}, "1":{"instant":2,"dteday":"2011-01-02T00:00:00.000Z","season":1,"yr":0,"mnth":1,"holiday":0,"weekday":3,"workingday":0,"weathersit":2,"temp":0.363478,"atemp":0.353739,"hum":0.696087,"windspeed":0.248539}, "2":{"instant":3,"dteday":"2011-01-03T00:00:00.000Z","season":1,"yr":0,"mnth":1,"holiday":0,"weekday":1,"workingday":1,"weathersit":1,"temp":0.196364,"atemp":0.189405,"hum":0.437273,"windspeed":0.248309}})

La richiesta contiene tutte le informazioni che sono state fornite al modello. Pertanto, il nostro modello risponderà con una previsione del noleggio di biciclette per le date specificate (qui ne abbiamo tre).

 headers = {'content-type': 'application/json', 'Accept-Charset': 'UTF-8'} r = requests.post(url, data=text, headers=headers) print(r,r.text) <Response [200]> { "0": 1063, "1": 1028, "2": 1399 }

Questo è tutto! Questo servizio può essere utilizzato facilmente nell'applicazione di qualsiasi azienda, per la pianificazione della manutenzione o per consentire agli utenti di essere consapevoli del traffico di biciclette, della domanda e della disponibilità di biciclette a noleggio.

Mettere tutto insieme

Il principale difetto di molti sistemi di apprendimento automatico, e in particolare dei PoC, è quello di combinare formazione e previsione.

Se sono accuratamente separati, le previsioni in tempo reale possono essere eseguite abbastanza facilmente per un MVP, con un costo di sviluppo e uno sforzo piuttosto bassi con Python/Flask, specialmente se, per molti PoC, è stato inizialmente sviluppato con Scikit-learn, Tensorflow, o qualsiasi altra libreria di apprendimento automatico Python.

Tuttavia, ciò potrebbe non essere fattibile per tutte le applicazioni, in particolare le applicazioni in cui l'ingegneria delle funzionalità è pesante o le applicazioni che recuperano la corrispondenza più vicina che deve avere i dati più recenti disponibili a ogni chiamata.

In ogni caso, hai bisogno di guardare i film più e più volte per rispondere a domande su di loro? La stessa regola vale per l'apprendimento automatico!