Deploying a Simple Machine Learning Model in a Modern Web Application (Flask, Angular, Docker & Scikit-learn)

Daniel Elsner
Feb 10, 2018 · 7 min read

Architecture Diagram
Final Application


## backend/Simple Iris Model.ipynbfrom sklearn import svm
from sklearn import datasets
# read iris dataset
iris = datasets.load_iris()
X, y = iris.data, iris.target
# fit model
clf = svm.SVC(
C=1.0,
probability=True,
random_state=1)
clf.fit(X, y)
print('Training Accuracy: ', clf.score(X, y))print('Prediction results: ', clf.predict_proba([[5.2, 3.5, 2.4, 1.2]]))
## backend/app.pyfrom flask import Flask, jsonify, request
from sklearn import svm
from sklearn import datasets
from sklearn.externals import joblib
# declare constants
HOST = '0.0.0.0'
PORT = 8081
# initialize flask application
app = Flask(__name__)
@app.route('/api/train', methods=['POST'])
def train():
# get parameters from request
parameters = request.get_json()

# read iris data set
iris = datasets.load_iris()
X, y = iris.data, iris.target

# fit model
clf = svm.SVC(C=float(parameters['C']),
probability=True,
random_state=1)
clf.fit(X, y)
# persist model
joblib.dump(clf, 'model.pkl')
return jsonify({'accuracy': round(clf.score(X, y) * 100, 2)})if __name__ == '__main__':
# run web server
app.run(host=HOST,
debug=True, # automatic reloading enabled
port=PORT)
## frontend/src/app/pages/home/types.tsexport class SVCParameters {
C: number = 2.0;
}

export class SVCResult {
accuracy: number;
}
## frontend/src/app/pages/home/iris.service.tsimport {Injectable} from '@angular/core';
import {Http} from "@angular/http";
import {Observable} from "rxjs/Observable";
import 'rxjs/add/operator/map';
import {
SVCParameters,
SVCResult
} from "./types";

const SERVER_URL: string = 'api/';

@Injectable()
export class IrisService {

constructor(private http: Http) {
}

public trainModel(svcParameters: SVCParameters): Observable<SVCResult> {
return this.http.post(`${SERVER_URL}train`, svcParameters).map((res) => res.json());
}
}
## frontend/src/app/pages/home/home.component.tsimport {Component, OnInit} from '@angular/core';
import {IrisService} from "./iris.service";
import {
SVCParameters,
SVCResult
} from "./types";

@Component({
selector: 'home',
templateUrl: './home.component.html',
styleUrls: ['./home.component.scss']
})
export class HomeComponent implements OnInit {

public svcParameters: SVCParameters = new SVCParameters();
public svcResult: SVCResult;

constructor(private irisService: IrisService) {
}

ngOnInit() {
}

public trainModel() {
this.irisService.trainModel(this.svcParameters).subscribe((svcResult) => {
this.svcResult = svcResult;
});
}
}
## frontend/src/app/pages/home/home.component.html<div fxLayoutAlign="center center" fxLayout="column">
<h1>Iris Model - Example</h1>

<div class="home__training">
<h3>Train Classification Model (Support Vector Machine)</h3>
<md-card fxLayoutAlign="center center" fxLayout="column">
<h4>Set Parameter for Model Training</h4>
<md-input-container>
<input
mdInput
name="C Parameter"
placeholder="C Parameter"
[(ngModel)]="svcParameters.C"
>
</md-input-container>
<button md-raised-button (click)="trainModel()">
Train model
</button>
</md-card>

<div *ngIf="svcResult"
fxLayoutAlign="center center"
fxLayout="column"
>
<h4>Training Accuracy</h4>
<ngx-charts-linear-gauge
[view]="[300, 100]"
[value]="svcResult.accuracy"
[min]="0"
[max]="100"
[units]="'% accuracy'"
>
</ngx-charts-linear-gauge>
</div>
</div>
</div>
## backend/app.py...@app.route('/api/predict', methods=['POST'])
def predict():
# get iris object from request
X = request.get_json()
X = [[float(X['sepalLength']), float(X['sepalWidth']), float(X['petalLength']), float(X['petalWidth'])]]

# read model
clf = joblib.load('model.pkl')
probabilities = clf.predict_proba(X)

return jsonify([{'name': 'Iris-Setosa', 'value': round(probabilities[0, 0] * 100, 2)},
{'name': 'Iris-Versicolour', 'value': round(probabilities[0, 1] * 100, 2)},
{'name': 'Iris-Virginica', 'value': round(probabilities[0, 2] * 100, 2)}])
...
## frontend/src/app/pages/home/types.ts...export class Iris {
sepalLength: number = 5.0;
sepalWidth: number = 3.5;
petalLength: number = 2.5;
petalWidth: number = 1.2;
}

export class ProbabilityPrediction {
name: string;
value: number;
}
...
## frontend/src/app/pages/home/iris.service.ts...public predictIris(iris: Iris): Observable<ProbabilityPrediction[]> {
return this.http.post(`${SERVER_URL}predict`, iris).map((res) => res.json());
}
...
## frontend/src/app/pages/home/home.component.html...<div class="home__prediction"
fxLayoutAlign="center center"
fxLayout="column"
>
<h3>Predict Iris classes</h3>

<md-card fxLayoutAlign="center center" fxLayout="column">
<h4>Define Iris</h4>
<div fxLayout="row">
<md-input-container>
<input mdInput
name="Sepal width"
placeholder="Sepal width"
[(ngModel)]="iris.sepalWidth"
>
</md-input-container>
<md-input-container>
<input mdInput
name="Sepal length"
placeholder="Sepal length"
[(ngModel)]="iris.sepalLength"
>
</md-input-container>
</div>
<div fxLayout="row">
<md-input-container>
<input mdInput
name="Petal width"
placeholder="Petal width"
[(ngModel)]="iris.petalWidth"
>
</md-input-container>
<md-input-container>
<input mdInput
name="Petal length"
placeholder="Petal length"
[(ngModel)]="iris.petalLength"
>
</md-input-container>
</div>
<div>
<button md-raised-button (click)="predictIris()">Predict iris</button>
</div>
</md-card>

<div *ngIf="probabilityPredictions"
fxLayoutAlign="center center"
fxLayout="column"
class="home__chart-container"
>
<h4>Prediction Result</h4>
<ngx-charts-pie-chart
[results]="probabilityPredictions"
[scheme]="colorScheme"
[labels]="true"
[legend]="true"
>
</ngx-charts-pie-chart>
</div>
</div>
...
## frontend/src/app/pages/home/home.component.ts...public iris: Iris = new Iris();
public probabilityPredictions: ProbabilityPrediction[];
...public predictIris() {
this.irisService.predictIris(this.iris).subscribe((probabilityPredictions) => {
this.probabilityPredictions = probabilityPredictions;
});
}
...

Daniel Elsner

Written by

Developer & data enthusiast

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade