End to End Air Quality Index Prediction Of Bangalore
Tags: Data Science, Heroku, Flask, Python, Machine Learning

Introduction and Overview of the problem:

Air Quality Index (AQI) is a tool to showcase air quality status. It transforms complex air quality data of various pollutants into a single number and colour. AQI has six categories of air quality. These are: Good, Satisfactory, Moderately Polluted, Poor, Very Poor and Severe. Each of these categories is decided based on ambient concentration values of air pollutants and their likely health impacts. As the AQI increases, an increasingly large percentage of the population is likely to experience health effects.

Problem Statement:

Build a model to predict PM2.5 levels.

Data Collection:

First I will collect the data by web scrapping. I am extracting the data from http://en.tutiempo.net/

With the help of this web scrapping I am able to extract the independent features which has the average data of each day.

Now, I am going to collect PM2.5 which is dependent feature from 2013 to 2018. So, for the PM2.5 I am able to get the data from the weathermap.com.

But while extracting PM2.5 I am able to get the data of each and every hour. So, I will combine the data and take the average of the day. And the data is not clean I am also going to do some cleaning.

I can also plot the graph between PM2.5 and Day.

I am going to import Plot_AQI.py file in my next BeautifulSoup to extract the data from html.

I will combine both the independent and dependent variables.

So, our data will look something like this.

We are perfectly able to web scrap the dependent and independent variables.

Exploratory Data Analysis (EDA):

Exploratory Data Analysis or (EDA) is understanding the data sets by summarizing their main characteristics often plotting them visually. This step is very important especially when we arrive at modeling the data in order to apply Machine learning.

Importing the required libraries for EDA:

Loading the data into the data frame:

Check for null values:

Dropping the Missing values:

After dropping the values:

Separating the Independent and Dependent features:

Pair Plot:

Pair Plot produces a matrix of relationships between each variable in your data for an instant examination of our data.

Correlation Matrix with Heatmap

Correlation states how the features are related to each other or the target variable.

Correlation can be positive (increase in one value of feature increases the value of the target variable) or negative (increase in one value of feature decreases the value of the target variable)

Heatmap makes it easy to identify which features are most related to the target variable, we will plot heatmap of correlated features using the seaborn library.

Index(['T', 'TM', 'Tm', 'SLP', 'H', 'VV', 'V', 'VM', 'PM 2.5'], dtype='object')

Feature Importance

You can get the feature importance of each feature of your dataset by using the feature importance property of the model.

Feature importance gives you a score for each feature of your data, the higher the score more important or relevant is the feature towards your output variable.

Feature importance is an inbuilt class that comes with Tree Based Regressor, we will be using Extra Tree Regressor for extracting the top 10 features for the dataset.

ExtraTreesRegressor()

[0.19026977 0.10797685 0.18725173 0.14832213 0.08656328 0.18485114

0.05135576 0.04340933]

Train Test Split

Selecting and Training Machine Learning Models:

Since this is a regression problem, I chose to train the following models:

Linear Regression Decision Tree Regressor Random Forest Regressor XGBOOST Regressor ANN

It’s a simple 4-step process:

Create an instance of the model class. Train the model using the fit() method. Make predictions. Evaluating the model using Mean Absolute Error (MAE), Mean Squared Error (MSE), Root Mean Squared Error (RMSE).

Linear Regression

LinearRegression()

array([ -2.69090829, 0.46219975, -3.86705184, -0.04494855,

-1.21193616, -40.11490762, -2.53563257, 0.56148181])

448.11616967588736

Error rate getting with respect to x_train and y_train.

Coefficient of determination R^2 <-- on train set: 0.551516808175875

Error rate getting with respect to x_test and y_test.

Coefficient of determination R^2 <-- on train set: 0.4852533130856792

0.4710569304807392

Model Evaluation

Interpreting the coefficients:

Holding all other features fixed, a 1 unit increase in T is associated with an decrease of 2.690 in AQI PM2.5 .

Holding all other features fixed, a 1 unit increase in TM is associated with an increase of 0.46 in AQI PM 2.5.

The difference between y_test and prediction looks almost good.

MAE: 44.836241266286365

MSE: 3687.5430309324165

RMSE: 60.72514331751236

Creating a pickle file for Deployment:

Comparison Linear, Ridge and Lasso Regression

Linear Regression

-3686.201777339885

Ridge Regression

I am going to implement Ridge regression and see that whether I can get a value that is much more nearer to zero as compare to Linear regression.

Suppose if I get a value -3500 that is much more towards zero so I will select then ridge regression model.

GridSearchCV(cv=5, estimator=Ridge(),

param_grid={'alpha': [1e-15, 1e-10, 1e-08, 0.001, 0.01, 1, 5, 10,

20, 30, 35, 40]},

scoring='neg_mean_squared_error')

{'alpha': 40}

-3664.3648996071743

Lasso Regression

Lasso Regression is similar to Ridge Regression but it is slightly better then ridge.

GridSearchCV(cv=5, estimator=Lasso(),

param_grid={'alpha': [1e-15, 1e-10, 1e-08, 0.001, 0.01, 1, 5, 10,

20, 30, 35, 40]},

scoring='neg_mean_squared_error')

{'alpha': 1}

-3666.78325343702

Model Evaluation

MAE: 44.50831198875126

MSE: 3627.8109390424697

RMSE: 60.2313119485411

Creating a pickle file for deployment:

Decision Tree Regression

DecisionTreeRegressor()

Coefficient of determination R^2 <-- on train set: 1.0

Coefficient of determination R^2 <-- on test set: 0.6836721835465965

0.4315969249390227

As we can see that our model is overfitting so we will do hyperparameter tuning.

Tree Visualization

['T', 'TM', 'Tm', 'SLP', 'H', 'VV', 'V', 'VM']

Model Evaluation

Hyperparameter Tuning Decision Tree Regressor

Fitting 10 folds for each of 10240 candidates, totalling 102400 fits [Parallel(n_jobs=-1)]: Using backend LokyBackend with 4 concurrent workers.

[Parallel(n_jobs=-1)]: Done 24 tasks | elapsed: 3.1s

[Parallel(n_jobs=-1)]: Done 1108 tasks | elapsed: 6.3s

[Parallel(n_jobs=-1)]: Done 3668 tasks | elapsed: 13.1s

[Parallel(n_jobs=-1)]: Done 7252 tasks | elapsed: 21.8s

[Parallel(n_jobs=-1)]: Done 11860 tasks | elapsed: 33.0s

[Parallel(n_jobs=-1)]: Done 17492 tasks | elapsed: 47.9s

[Parallel(n_jobs=-1)]: Done 24148 tasks | elapsed: 1.1min

[Parallel(n_jobs=-1)]: Done 31828 tasks | elapsed: 1.5min

[Parallel(n_jobs=-1)]: Done 40532 tasks | elapsed: 2.0min

[Parallel(n_jobs=-1)]: Done 50260 tasks | elapsed: 2.4min

[Parallel(n_jobs=-1)]: Done 61012 tasks | elapsed: 3.0min

[Parallel(n_jobs=-1)]: Done 72788 tasks | elapsed: 3.6min

[Parallel(n_jobs=-1)]: Done 85588 tasks | elapsed: 4.3min

[Parallel(n_jobs=-1)]: Done 99412 tasks | elapsed: 5.0min

[Parallel(n_jobs=-1)]: Done 102400 out of 102400 | elapsed: 5.1min finished Time taken: 0 hours 5 minutes and 8.58 seconds.

{'max_depth': 8,

'max_features': None,

'max_leaf_nodes': 20,

'min_samples_leaf': 4,

'min_weight_fraction_leaf': 0.1,

'splitter': 'best'}

-3131.6489619178874

MAE: 23.674504573170733

MSE: 2266.109651031081

RMSE: 47.60367266326287

Creating a pickle file for Deployment:

Random Forest Regressor

RandomForestRegressor()

Coefficient of determination R^2 <-- on train set: 0.9737825275525134

Coefficient of determination R^2 <-- on train set: 0.7790383481598548

0.7298539095199639

Model Evaluation

Hyperparameter Tuning

[100, 200, 300, 400, 500, 600, 700, 800, 900, 1000, 1100, 1200]

{'n_estimators': [100, 200, 300, 400, 500, 600, 700, 800, 900, 1000, 1100, 1200], 'max_features': ['auto', 'sqrt'], 'max_depth': [5, 10, 15, 20, 25, 30], 'min_samples_split': [2, 5, 10, 15, 100], 'min_samples_leaf': [1, 2, 5, 10]}

RandomizedSearchCV(cv=5, estimator=RandomForestRegressor(), n_iter=100,

n_jobs=1,

param_distributions={'max_depth': [5, 10, 15, 20, 25, 30],

'max_features': ['auto', 'sqrt'],

'min_samples_leaf': [1, 2, 5, 10],

'min_samples_split': [2, 5, 10, 15,

100],

'n_estimators': [100, 200, 300, 400,

500, 600, 700, 800,

900, 1000, 1100,

1200]},

random_state=42, scoring='neg_mean_squared_error',

verbose=2)

{'n_estimators': 500,

'min_samples_split': 2,

'min_samples_leaf': 1,

'max_features': 'sqrt',

'max_depth': 15}

-1541.722112688083

MAE: 24.300982342479678

MSE: 1582.925388467951

RMSE: 39.78599487844876

Creating a pickle file for Deployment:

XGBOOST Regressor

XGBRegressor(base_score=0.5, booster='gbtree', colsample_bylevel=1,

colsample_bynode=1, colsample_bytree=1, gamma=0, gpu_id=-1,

importance_type='gain', interaction_constraints='',

learning_rate=0.300000012, max_delta_step=0, max_depth=6,

min_child_weight=1, missing=nan, monotone_constraints='()',

n_estimators=100, n_jobs=0, num_parallel_tree=1, random_state=0,

reg_alpha=0, reg_lambda=1, scale_pos_weight=1, subsample=1,

tree_method='exact', validate_parameters=1, verbosity=None)

Coefficient of determination R^2 <-- on train set: 0.9997717196147036

Coefficient of determination R^2 <-- on train set: 0.8123312956923813

0.725127678258157

Model Evaluation

Hyperparameter Tuning

[100, 200, 300, 400, 500, 600, 700, 800, 900, 1000, 1100, 1200]

{'n_estimators': [100, 200, 300, 400, 500, 600, 700, 800, 900, 1000, 1100, 1200], 'learning_rate': ['0.05', '0.1', '0.2', '0.3', '0.5', '0.6'], 'max_depth': [5, 10, 15, 20, 25, 30], 'subsample': [0.7, 0.6, 0.8], 'min_child_weight': [3, 4, 5, 6, 7]}

RandomizedSearchCV(cv=5,

estimator=XGBRegressor(base_score=None, booster=None,

colsample_bylevel=None,

colsample_bynode=None,

colsample_bytree=None, gamma=None,

gpu_id=None, importance_type='gain',

interaction_constraints=None,

learning_rate=None,

max_delta_step=None, max_depth=None,

min_child_weight=None, missing=nan,

monotone_constraints=None,

n_estimators=100, n...

validate_parameters=None,

verbosity=None),

n_iter=100, n_jobs=1,

param_distributions={'learning_rate': ['0.05', '0.1', '0.2',

'0.3', '0.5', '0.6'],

'max_depth': [5, 10, 15, 20, 25, 30],

'min_child_weight': [3, 4, 5, 6, 7],

'n_estimators': [100, 200, 300, 400,

500, 600, 700, 800,

900, 1000, 1100,

1200],

'subsample': [0.7, 0.6, 0.8]},

random_state=42, scoring='neg_mean_squared_error',

verbose=2)

{'subsample': 0.8,

'n_estimators': 1100,

'min_child_weight': 3,

'max_depth': 30,

'learning_rate': '0.05'}

-1380.5446667169047

MAE: 19.580280400079683

MSE: 1344.4213246754996

RMSE: 36.66635139573475

Creating a pickle file for Deployment:

ANN

Model Evaluation

MAE: 25.245581808943093

MSE: 1681.8142776645072

RMSE: 41.009929013161035

So, XGBOOST Regressor is performing well among all the models.

Now, we are going to deploy our model on Heroku.

Setting Up For Deployment

This is the folder that we want to deploy onto Heroku.

The static directory contains your CSS files, and the template contains the HTML file, which is used for rendering.

Create your app.py file:

Below is how my result.html file looks like

We are almost done with the coding part, and now it’s time to deploy our flask app.

Before deploying the app, first, check the flask app on the localhost.

Deployment Steps

Login to your Heroku account using CLI

heroku login

2. Create a web app on Heroku

We can create a new application on Heroku using the following command

heroku create your_app_name

3. Create requirements.txt file in the same project directory

4. Create a Procfile

Procfile is a Process file which is required for all Heroku applications.

Procfile specifies the commands that are executed by the app on startup.

5. Now click on to the option Github and select project from github and then deploy that.

Our app is live Now we can see our web app using generated URL

https://airqualityprediction20.herokuapp.com/

The source code for this is available on Github https://github.com/INZA111/Air-Quality-Index