It has been a long time since I wrote anything on my blog. So thought about giving everyone a treat this time. Or so I think it is.
Recently I was thinking about a way to deploy all these machine learning models I create in python. I searched through the web but couldn’t find anything nice and easy. Then I fell upon this book by Sebastian Rashcka and I knew that it was what I was looking for. To tell you the truth I did had some experience in Flask earlier but this book made it a whole lot easier to deploy a machine learning model in flask.
So today I am going to give a brief intro about Flask Apps and how to deploy them using a service called Openshift.
Flask is a Python Web Framework that makes it easier to create webapps from python.
Openshift is a free service(if we only use 1 small instance) which lets us use their services to deploy our flask web-apps.
So that we don’t get lost, let me tell you the flow of this post.
Now once you do this you have installed Openshift Client tools on your system.
So now I am going to do a lot of things in this post. But don’t get bothered much it is just code and HTML quirks. I will try to provide enough details on which parts are necessary. First of all, you will need to create a domain on Openshift platform. This can be done by using:
rhc domain create -n DomainName -l EmailAddress -p password
For this example I created:
rhc domain create -n mlwhiz -l MyEmailAddress -p Mypassword
In the free version for Openshift you can run 3 web-apps with a single domain. For example I can create a maximum of 3 webapps whose web address would be:
Once we create a domain we need to create a webapp:
rhc app create HelloWorld python-2.7
This creates the app named helloworld for us. The app currently resides at this address on web: http://helloworld-mlwhiz.rhcloud.com/ This command also creates a folder where our app resides. cd into this folder.
cd helloworld
Now get a basic template to work upon in this directory. You can think of this as a starter code for flask. We can do this by pulling and merging from Github using the following commands.
git remote add upstream -m master git://github.com/openshift/flask-example.git
git pull -s recursive -X theirs upstream master
Use Virtualenv to isolate Python development environments. It’s a tool that allows you setup an isolated, self-contained Python environment in a folder on your dev box. This way you can experiment with various versions of Python without affecting your system wide configurations:
brew install python-virtualenv
cd helloworld/wsgi/
virtualenv venv --python=python2.7
#Activate the virtual environment
. venv/bin/activate
# Install all these into your virtual python environment.
pip install flask flask-wtf flask-babel markdown flup
Now Change the name of flaskapp.py in wsgi to run.py
put this code in run.py
import os
from flask import Flask
app = Flask(__name__)
@app.route('/')
def home():
"""Render website's home page."""
return 'Hello World!'
if __name__ == '__main__':
app.run(debug="True")
Also change the file named application to:
#!/usr/bin/python
import os
import sys
sys.path.insert(0, os.path.dirname(__file__) or '.')
PY_DIR = os.path.join(os.environ['OPENSHIFT_HOMEDIR'], "python")
virtenv = PY_DIR + '/virtenv/'
PY_CACHE = os.path.join(virtenv, 'lib', os.environ['OPENSHIFT_PYTHON_VERSION'], 'site-packages')
os.environ['PYTHON_EGG_CACHE'] = os.path.join(PY_CACHE)
virtualenv = os.path.join(virtenv, 'bin/activate_this.py')
try:
exec(open(virtualenv).read(), dict(__file__=virtualenv))
except IOError:
pass
from run import app as application
Run this to host your app:
cd helloworld/wsgi
python run.py
You should be able to see your app on: http://127.0.0.1:5000/ You can deploy this webapp to Openshift using:
cd helloworld
git add .
git commit -a -m "Initial deployment of this app to the web"
git push
Open http://helloworld-mlwhiz.rhcloud.com/ in your browser. You would see Hello World! there. Now we have got a very basic structure complete.
We will now work on creating a app that operates on two numbers provided by the user. The functions possible are +,- and *. You can see this web app in action here before moving on. This app will help us in understanding how user forms work with Flask and how to manage user inputs in Flask. First of all change the code in run.py to
import os
from flask import Flask,render_template, request
from wtforms import Form, TextAreaField, validators,SelectField
app = Flask(__name__)
# Code to create a WTForm with three fields. 2 text fields and 1 dropdown menu.
class OutputForm(Form):
myChoices=[('+', '+'), ('-', '-'), ('*', '*')]
num1 = TextAreaField('',[validators.DataRequired()])
num2 = TextAreaField('',[validators.DataRequired()])
Operator = SelectField(u'', choices = myChoices, validators = [validators.DataRequired()])
# This uses the render_template method in flask to use a template first_app.html.
# This html contains placeholders for the form that is provided in the kwargs argument to the function call.
@app.route('/')
def index():
#return 'Hello World!'
form = OutputForm(request.form)
return render_template('first_app.html',form = form)
# This is the output that is displayed. It checks if the form is validated and POST request is made.
# If true it renders the output.html else renders the main index page.
# Most of the work is done here. Gets the user inputs using the request.form method.
@app.route('/output', methods=['POST'])
def output():
form = OutputForm(request.form)
if request.method == 'POST' and form.validate():
num1 = request.form['num1']
num2 = request.form['num2']
op = request.form['Operator']
if op=="+":
name=str(int(num1)+int(num2))
elif op=="-":
name=str(int(num1)-int(num2))
elif op=="*":
name=str(int(num1)*int(num2))
return render_template('output.html', name=name)
return render_template('first_app.html', form=form)
if __name__ == '__main__':
app.run(debug="True")
We use WTF forms here to create a form object. We pass this form object to the HTML render_template method. We have accessed these again in the output function so that we can show them in output.html where all the major work is done for creating the app.
Now Create a folder named template in helloworld/wsgi and create a file named _formhelpers.html with this content. You really don’t need to see the content in this file.
{% macro render_field(field) %}
<dt>{{ field.label }}
<dd>{{ field(**kwargs)|safe }}
{% if field.errors %}
<ul class=errors>
{% for error in field.errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
{% endif %}
</dd>
{% endmacro %}
Also add another file named first_app.html with this content. Notice how we access the wtform here.
<!doctype html>
<html>
<head>
<title>First app</title>
<link rel="stylesheet" href="{{ url_for('static',filename='style.css') }}">
</head>
<body>
{% from "_formhelpers.html" import render_field %}
<div>Calculator: Please enter two numbers and a function you want to apply</div>
<form method=post action="/output">
{{ render_field(form.num1) }}{{ render_field(form.Operator) }}{{ render_field(form.num2) }}
<input type=submit value='Result' name='submit_btn'>
</form>
</body>
</html>
Create a file named output.html where the final output will be shown.
<!doctype html>
<html>
<head>
<title>First app</title>
<link rel="stylesheet" href="{{ url_for('static',filename='style.css') }}">
</head>
<body>
<div>The output is: {{ name }}</div>
</body>
</html>
Also add a style.css file in the static folder. You can put this in it for right now or any other thing you want.
h1 {
color: blue;
font-family: verdana;
font-size: 300%;
}
p {
color: red;
font-family: courier;
font-size: 160%;
}
And we are mostly done. Run run.py in the wsgi directory and you would be able to access the app at : http://127.0.0.1:5000/. Again deploy this webapp to Openshift using:
cd helloworld
git add .
git commit -a -m "Initial deployment of this app to the web"
git push
So here we took inputs from the user and show the output using the flask App. The final app is hosted at http://helloworld-mlwhiz.rhcloud.com/ for you to see. This code provides us with a code skeletn which will be valuable when we will deploy a whole ML model, which is the main motive of this series.