Get started…

Those were my thoughts when I took up the challenge of building Maven (then known as Project Andromeda).

There were so many reasons not to do it, unfinished and continuously evolving specs, unknown complexity, several dependencies, no development team in place, no clear target user, unproven market, extremely aggressive deadlines, fear of running out of money etc. But there was one important reason to do it: it hasn’t been done before.

Having decided that I want to do it, how do I go about it?

Well there is only one way to build software, to start building it. It cannot be done by sitting on the side-lines and waiting for things to fall into place before you begin. It needs a team to be built, need analysis, write specs, design, architect, code, test… and building Maven meant that we were doing all of these at once.

And then we celebrate…

The thrill of seeing the thoughts of several people in bytes and pixels is difficult to articulate. We know that we have just scratched the surface, and there is a ton of work to be done, but we also know that the journey ahead is much more manageable than what we have went through.

Why copy-cats don’t keep me up at night.

It takes a special type of environment to achieve something like this. You need people who are willing to suspend disbelief, embrace chaos, put their careers on the line and work with single minded focus to make it happen. Maven could not be built in an enterprise environment. It would take over 10 person-years to complete, and will be way behind what Maven would be by then. But to ensure that Maven is years ahead of competition, we cannot rest on what has been achieved and continue to innovate and create new benchmarks.

It is the process of observing people to discover their needs, goals, and values. Although the course if about “Human Computer Interaction”, the process of need finding is relevant to developing new products or even new businesses.

A good starting point for any new product is to clearly identify an existing problem or need. That’s because finding a big problem and need often yields important untapped opportunities. Observing people also helps build empathy and think from their point of view. So, how do we observe people and identify their needs?

Participant Observation

Observe the users and their behavior in context (performing the activity). This is most useful when you want to see users in their element and learn about their experience.

While observing, we seek answers to these questions:

  1. What do people do now?
  2. What values and goals do people have?
  3. How are these particular activities embedded in a larger context, or the big picture?
  4. Similarities and differences across people
  5. …and other types of context, like time of day

While observing people, pay particular attention to any hacks or workarounds. These could be a gold mine for new ideas.
Continue reading

I started Sangeet Planner based on bottle.py micro framework, which allowed for rapid prototyping. Soon I hit some problems with the plug-ins (lack of them) as well as issue supporting multiple plugins. Hence I decided to move over to Flask, which has a much bigger ecosystem. Below are the steps I took to convert most of the application to Flask.

Fix the imports

Use these imports:

from flask import Flask, render_template as template, request, make_response, jsonify, abort from flask.ext.sqlalchemy import SQLAlchemy 

instead of

from bottle import route, run, template, install, static_file, response 
import bottle 

The important part was importing flask.render_template as template. This lets you continue using the bottle style call to template.

Similarly, I imported

from flask.ext.sqlalchemy import SQLAlchemy 

instead of

from bottle.ext import sqlalchemy 

Change the startup process

if __name__ == "__main__": 
    bottle.debug(True)
    run(reloader=True)

changed to

# towards the beginging of the file, soon after imports 
app = Flask(__name__, template_folder='views',static_folder='public/static') 
# towards the end of the file 
if __name__ == "__main__": 
    app.run(debug=True) 

important to note, we are overriding the flask defaults to make them use the bottle conventions of having the templates stores in the views folder (template_folder=’views’) and tell flask to serve all files from public/static as static files. This allows us to remove the static serving routes needed in bottle. For e.g. I had a few routes like the below, which are no longer needed.

@route('/static/:filename') 
def server_static(filename): 
    return static_file(filename, root='public/static') 

Fixing the sqlalchemy plugin

The plugins in Flask are initialised differently to bottle. So I needed to change the bottle style sqlalchemy load as below:

app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///mysqlite.db' 
conn = SQLAlchemy(app) 
db = conn.session 

The db object is not injected anymore in functions, so remove the input param. Along with other changes needed to account for the differences between bottle and flask the routes will look like this:

@route('/') 
def index(db): 
    #no change to function body 
    return template('index') 
@app.route('/') 
def index(): 
    #no change to function body 
    return template('index.tpl') 

Reading request values

There are subtle differences in bottle and flask. I think some of them can be fixed with some monkey patching, but a quick find/replace did the trick for me.

A comparative table:

Bottle Flask
request.forms request.form
request.POST request.form
request.POST.getunicode(key) request.form.get(key)
request.GET request.args
request.GET.getall request.args.getlist
request.get_cookie request.cookies.get (haven’t looked into secure cookies yet)
response.set_cookie response = make_response(…)
response.set_cookie

Declare the HTTP methods allowed

One last change was how allowed HTTP methods are delared.

In bottle:

@post('/some/route') 

In changed in flask to:

@app.route(' some/route', methods=['POST']) 

Finally

Well, a few changes would have been less for me if I had used an app object in bottle as well, but the fixes were trivial.

Hope this guide helps you. In case you run into some other difference, please leave a comment below.

Logistic regression is used to classify things into positive case or negative case. It is a special case of linear regression which outputs value between 0 and 1, which denote the probability or the likelihood of the given sample being positive. For e.g. what is the probability of the given email being a spam?

We can then predict a positive case if the hypothesis outputs a value above a certain threshold, which normally is 0.5 but could be more or less. The ideal threshold can be derived based on the cross validation result. More on this in a later post.

Hypothesis

As mentioned above, logistic regression is similar to linear regression, but the hypothesis always returns an output between 0 and 1. This is achieved by passing the hypothesis of the linear regression to a Sigmoid function.
Thus the hypothesis is denoted as:

hθ(x) = g(θTx) 

where g is the sigmoid function defined as:

g(z)=1/1+e^-z

Cost Function

The cost function for classification problems are a bit more involved due to the fact that simple squared error is not sufficient to work with the small differences here, since the max difference possible is 1, predictive 0% probability for a positive case. Therefore to magnify the differences a log scale is used.
cost function of logistic regression
Vectorized version:

J = sum(-y .* log(hypo) - (1-y).*log(1-hypo)) / -m

Derivative of cost function (gradient)

The gradient for hypothesis is the same as that of linear regression:
Derivative of cost function
The hypothesis of course is different as stated above.

Multiclass Classification

So far we have only looked at 2 distinct results from the algorithm, either negative (y=0) or positive (y=1). But what if there are more than 2 distinct possibilities? This is covered under multi class or one-vs-all classification.

The basic idea is simple. We train the classifier once per each possible outcome. For e.g. If we need to predict if the given sentence is in English, German or French, we will derive the value of θ for each.

So the θ for English will be able to predict whether the sentence is in English or not, thus changing the problem to a single class (positive/negative) classification. Similarly the θ for German and French are derived.

Then for a given sentence we will calculate the probability of it being English, German or French and return the language having the highest probability.