Serving Prophet Model with Flask — Predicting Future | by Andrej Baranovskij | Towards Data Science

An accurate forecast and future prediction are crucial almost for any business. This is an obvious thing and it doesn’t need explanation. There is a concept of time series data, this data is ordered by date and typically each date is assigned with one or more values specific to that date. Machine Learning powered models could generate forecasts based on time series data. Such forecasts could be an important source of information for business decisions.
I was researching LSTM models for time series forecasting. LSTM neural networks are good in predicting sequence data, time series is sequence data too and this is the reasons LSTM networks are used to forecast time series. LSTM implementation is complex, it is hard to prepare input data for LSTM training. Probably all LSTM examples demonstrate forecast based on data from current dataset, but not the real future data — this makes it hard to apply LSTM in practice.
When I found on GitHub and evaluated Prophet Python library for future forecasting, I was impressed by its simplicity and practicality. Prophet — Open Source forecast library developed by Facebook.
I will share a sample Python notebook with Prophet forecast implementation and show how to save/load Prophet model and serve it through Flask API.
Source code and data are available in my GitHub repo.
First, you need to import Prophet library:
import pandas as pdimport matplotlib.pyplot as pltfrom fbprophet import Prophet%matplotlib inline
If Prophet is not installed in your environment, you need to install it. I was installing it in Ubuntu running within Docker container. It didn’t work to install with pip. But I was able to install with conda:
conda install gccconda install -c conda-forge fbprophet
Typically the next step is to load data. For this sample, I’m using iron/steel price dataset (downloaded from here). Data is loaded into the frame with Pandas library. Pandas data frame allows to manipulate and wrangle data. We are removing unused columns, setting index and re-ordering time series data:
df = pd.read_csv('Dow Jones Iron & Steel Historical Data.csv')df = df[['Date', 'Price']].dropna()df['Date'] = pd.to_datetime(df['Date'])df = df.set_index('Date')daily_df = df.resample('D').mean()d_df = daily_df.reset_index().dropna()
Prophet operates with ds/y columns, we should rename columns in the data frame:
d_df.columns = ['ds', 'y']fig = plt.figure(facecolor='w', figsize=(20, 6))plt.plot(d_df.ds, d_df.y)
Iron/steel price data we are going to use for Prophet model training:
Here it comes the key part — Prophet model training:
m = Prophet()m.fit(d_df)future = m.make_future_dataframe(periods=90)forecast = m.predict(future)forecast[['ds', 'yhat', 'yhat_lower', 'yhat_upper']].tail()
Model training is executed by calling fit function and passing Pandas data frame. Future prediction is executed by predict function and passing parameter to describe how many days into the future to predict (90 days in the above example). Prophet returns a data frame with various parameters to describe the forecast. One of the most important:
ds — forecast dateyhat — forecast value for the given dateyhat_lower — lower forecast boundary for the given dateyhat_uppet — upper forecast boundary for the given date
ds — forecast date
yhat — forecast value for the given date
yhat_lower — lower forecast boundary for the given date
yhat_uppet — upper forecast boundary for the given date
Calling plot function for Prophet model displays how the model was trained according to training data (black points — training data, blue line — forecast value, light blue area — forecast boundaries):
This chart shows model prediction for 90 days into the future. But it is hard to see it, the chart shows all the data. We can zoom into the data and render chart with a vertical line splitting prediction from training data:
from datetime import datetime, timedeltafig1 = m.plot(forecast)#datenow = datetime.now()datenow = datetime(2019, 7, 2)dateend = datenow + timedelta(days=90)datestart = dateend - timedelta(days=450)plt.xlim([datestart, dateend])plt.title("Iron/steel forecast", fontsize=20)plt.xlabel("Day", fontsize=20)plt.ylabel("Iron/steel price", fontsize=20)plt.axvline(datenow, color="k", linestyle=":")plt.show()
This helps to see the prediction for 90 days clearer. We can see a downward trend in the price forecast:
There is a simple way to print forecast values, accessing last 90 days from the forecast data frame:
forecast[['ds', 'yhat', 'yhat_lower', 'yhat_upper']][-90:]
To evaluate the model, it is useful to display learned trends:
fig2 = m.plot_components(forecast)
Prophet learns that price is usually going down from March to October.
You should check what is the expected error, when predicting by a certain number of days. This is called model cross-validation:
from fbprophet.diagnostics import cross_validation, performance_metricsdf_cv = cross_validation(m, horizon='90 days')df_p = performance_metrics(df_cv)df_p.head(5)
Result:
We can plot these metrics to see visually how the model would perform. It is easier to understand percentage error (MAPE), this is how we can plot it:
from fbprophet.plot import plot_cross_validation_metricfig3 = plot_cross_validation_metric(df_cv, metric='mape')
It shows that 10 days forecast results in around 10% error and then error grows to about 18%:
The model should be re-trained when new data becomes available. There is no point to re-train model, if data is not changed. Save model instead and use it again, when user wants to call predict function. Use pickle functionality for that:
import picklewith open('forecast_model.pckl', 'wb') as fout:    pickle.dump(m, fout)with open('forecast_model.pckl', 'rb') as fin:    m2 = pickle.load(fin)
This will save the model into a physical file on disk. In this example I show how to save and load the model for your convenience, in a real implementation, load function will be called in a different place (in a function which will be responsible to process user request for forecast).
Flask works perfectly to expose Prophet model to the external world through REST API. Import Flask library:
from flask import Flask, jsonify, requestfrom flask_cors import CORS, cross_origin
Call predict within the function with Flask annotation:
app = Flask(__name__)CORS(app)@app.route("/katana-ml/api/v1.0/forecast/ironsteel", methods=['POST'])def predict():    horizon = int(request.json['horizon'])        future2 = m2.make_future_dataframe(periods=horizon)    forecast2 = m2.predict(future2)        data = forecast2[['ds', 'yhat', 'yhat_lower', 'yhat_upper']][-horizon:]        ret = data.to_json(orient='records', date_format='iso')        return ret# running REST interface, port=3000 for direct testif __name__ == "__main__":    app.run(debug=False, host='0.0.0.0', port=3000)
Example of REST API call through Postman. Prediction horizon parameter is passed to the API and we get back JSON response with forecast data:
Source code and sample data are available in my GitHub repo.