Algorithmia

How to Solve FizzBuzz Using Machine Learning and Scikit-Learn

Using machine learning to solve FizzBuzzThis is a guest post by Daniël Heres, a software engineer & Computing Science student. Want to contribute your own how-to post? Let us know.

FizzBuzz is a programming exercise some interviewers use to test a developer’s skills. To solve FizzBuzz, count from 1 to 100 and replace numbers divisible by 3 with “fizz”, and numbers divisible by 5 with “buzz.” For numbers divisible by both 3 and 5, we replace them with “fizzbuzz”.

Sounds straight-forward, right?

Instead of programming a bunch of if statements and checking whether each number can be divided by 3 or 5, we’re going to use machine learning. In this tutorial I’ll show you how can create your own AI FizzBuzz model, and host it on Algorithmia. Try the final result here.

Prerequisites

You’ll need both SciPy, and scikit-learn.

Follow these instructions to install SciPy and to install scikit-learn.

Step 1: Generate Your FizzBuzz Sample Data

For AI FizzBuzz, we’re going to need to generate lots of data.

The model is trained with a moving window of the previous 15 integer’s fizzbuzz results as an input vector. However, for the first 15 integers we need a way to maintain proper input dimensionality, which requires us to add “PAD” values in place of the regular fizzbuzz results.

[
    [
        ['PAD', 'PAD', 'PAD', 'PAD',
            'PAD', 'PAD', 'PAD', 'PAD',
            'PAD', 'PAD', 'PAD', 'PAD',
            'PAD', 'PAD', 'PAD'
        ], 'i'
    ],
    [
        ['PAD', 'PAD', 'PAD', 'PAD',
            'PAD', 'PAD', 'PAD', 'PAD',
            'PAD', 'PAD', 'PAD', 'PAD',
            'PAD', 'PAD', 'i'
        ], 'i'
    ],
    [
        ['PAD', 'PAD', 'PAD', 'PAD',
            'PAD', 'PAD', 'PAD', 'PAD',
            'PAD', 'PAD', 'PAD', 'PAD',
            'PAD', 'i', 'i'
        ], 'fizz'
    ],
    [
        ['PAD', 'PAD', 'PAD', 'PAD',
            'PAD', 'PAD', 'PAD', 'PAD',
            'PAD', 'PAD', 'PAD', 'PAD',
            'i', 'i', 'fizz'
        ], 'i'
    ],
    [
        ['PAD', 'PAD', 'PAD', 'PAD',
            'PAD', 'PAD', 'PAD', 'PAD',
            'PAD', 'PAD', 'PAD', 'i',
            'i', 'fizz', 'i'
        ], 'buzz'
    ],
    [
        ['PAD', 'PAD', 'PAD', 'PAD',
            'PAD', 'PAD', 'PAD', 'PAD',
            'PAD', 'PAD', 'i', 'i',
            'fizz', 'i', 'buzz'
        ], 'fizz'
    ]
...
]

The first datapoint contains 15 “PAD” tags, the second contains 14 “PAD” tags followed by one “i” tag, the third contains 13 “PAD” tags followed by an “i” tag and a “fizz” tag, etc.

Create a new file called fizzbuzz.py, and copy/paste this in:

from sklearn.preprocessing import LabelBinarizer
from sklearn.linear_model import LogisticRegression
from sklearn import cross_validation
import numpy as np
import pickle

import Algorithmia
client = Algorithmia.client()

def fizz_buzz(i):
    """The fizzbuzz algorithm! (You can modify this)"""
    if i % 15 == 0:
        return "fizzbuzz"
    elif i % 5 == 0:
        return "buzz"
    elif i % 3 == 0:
        return "fizz"
    else:
        return "i"

sequence_length = 15

def build_samples(samples):
    """Create list of features and labels we want to predict"""
    padding = ['PAD'] * sequence_length
    for i in range(len(samples) - sequence_length - 1):
        yield [padding + samples[max(0, i - sequence_length):i], samples[i]]
        padding = padding[1:]

That’s what we’ll use to generate our sample data.

Step 2: Create Your FizzBuzz Model

Now that we’re able to generate some data, we can create our model to fit it.

We’ll generate a list of 15,000 sample numbers, fit the first 10,000, and then validate our results using the last 5,000 (10,000 training / 5,000 test).

The inputs are transformed to one-hot vectors using the scikit-learn’s LabelBinarizer. This transforms all the tokens into a list of numbers with all values zero except at one location:

  • i becomes [1,0,0,0,0]
  • fizz = [0,1,0,0,0]
  • buzz = [0,0,1,0,0]
  • etc.

We use a logistic regression classifier to learn to predict the FizzBuzz sequence.

After fitting and validating, we save our binarizer and classifier using pickle. By using the score function of the model, we measure the performance of our model. It gets a perfect score of 1.0! We rock!

Ok, add this to the bottom of the file you just made:

def learn():
    num_samples = 15000

    fizz_buzz_samples = [fizz_buzz(i) for i in range(1, num_samples + 1)]
    print(fizz_buzz_samples)
    samples = list(build_samples(fizz_buzz_samples))

    lb = LabelBinarizer()
    lb.fit(fizz_buzz_samples + ['PAD'])

    X = np.array([np.array(lb.transform(x)).flatten() for x, y in samples])
    y = np.array([y for x, y in samples])

    X_train, X_test, y_train, y_test = X[0:10000], X[10000:15000], y[0:10000], y[10000:15000], 

    # Learn a simple logistic regression classifier
    clf = LogisticRegression(tol=1e-6)
    clf.fit(X_train, y_train)

    print(clf.score(X_test, y_test))

    # save binarize and classifier
    with open('binarizer.pkl', 'wb') as binarizer_file:
        pickle.dump(lb, binarizer_file)

    with open('classifier.pkl', 'wb') as classifier_file:
        pickle.dump(clf, classifier_file)

if __name__ == "__main__":
    learn()

Let’s run this from the command line with python fizzbuzz.py. You’ll see all the sample data, and the binarizer.pkl and classifier.pkl files will be saved in the folder.

Generating machine learning sample data to solve fizzbuzz

Step 3: Upload the Model Files

Now it is time to upload the two model files to Algorithmia. Create a new collection on Algorithmia, and upload the two model files there.

Adding machine learning models to Data API

Read the Data API docs for more information.

Step 4: An API to Solve FizzBuzz

With our data on Algorithmia, we can load it using the Algorithmia client, and create an API endpoint to generate the first 100 FizzBuzz’s from our trained model.

We’ll use the original number when the model predicts “i” — otherwise we print the predicted class.

Now, let’s create a new Python algorithm on Algorithmia, and add scikit-learn as a dependency. We’ll use the following code to load our model files, and generate our AI FizzBuzz output.

import numpy as np
import pickle

import Algorithmia

client = Algorithmia.client()

sequence_length = 15

binarizer_url = 'data://PetiteProgrammer/FizzBuzz/binarizer.pkl'
classifier_url = 'data://PetiteProgrammer/FizzBuzz/classifier.pkl'
binarizer_path = client.file(binarizer_url).getFile().name
classifier_path = client.file(classifier_url).getFile().name
with open(binarizer_path, 'rb') as binarizer_file:
    lb = pickle.load(binarizer_file)
with open(classifier_path, 'rb') as classifier_file:
    clf = pickle.load(classifier_file)

def apply(input):
    pad = ['PAD'] * sequence_length
    input = []
    res = []
    for i in range(1, 101):
        X = lb.transform(pad + input)
        predicted = clf.predict([np.array(X).flatten()])[0]
        if predicted == "i":
            res.append(str(i))
        else:
            res.append(predicted)
        
        input.append(predicted)

        pad = pad[1:]
        if len(input) > sequence_length:
            input = input[1:]
    return res

Remember to replace the URLs with the location of your own hosted files. The urls are in this format:

data://:username/:collection/:filename

Compile the algorithm, and run it by passing in an empty string, like this:

import Algorithmia

input = ""
client = Algorithmia.client("API KEY HERE")
algo = client.algo('PetiteProgrammer/FizzBuzz/0.1.1')
print algo.pipe(input).result

The output will look like this:

[
 "1",
 "2",
 "fizz",
 "4",
 "buzz",
 "fizz",
 "7",
 "8",
 "fizz",
 "buzz",
 "11",
 "fizz",
 "13",
 "14",
 "fizzbuzz",
 "16",
 "17",
...
 "88",
 "89",
 "fizzbuzz",
 "91",
 "92",
 "fizz",
 "94",
 "buzz",
 "fizz",
 "97",
 "98",
 "fizz",
 "buzz"
]

And that’s it! That’s how you write and train a simple FizzBuzz prediction model using machine learning and scikit-learn.

To recap:

  • We generated some data and used logistic regression to predict the FizzBuzz sequence.
  • Uploaded the model files to Algorithmia
  • And, then created an API out of it any developer could use to generate their own FizzBuzz predications.

Although solving FizzBuzz with machine learning is admittedly overkill, it’s a fun way to learn how to apply machine learning techniques. Plus, now you know how to turn your machine learning or deep learning models into scalable, production-ready APIs.

Here’s the complete code sample for fizzbuzz.py:

from sklearn.preprocessing import LabelBinarizer
from sklearn.linear_model import LogisticRegression
from sklearn import cross_validation
import numpy as np
import pickle

import Algorithmia
client = Algorithmia.client()

def fizz_buzz(i):
    """The fizzbuzz algorithm! (You can modify this)"""
    if i % 15 == 0:
        return "fizzbuzz"
    elif i % 5 == 0:
        return "buzz"
    elif i % 3 == 0:
        return "fizz"
    else:
        return "i"

sequence_length = 15

def build_samples(samples):
    """Create list of features and labels we want to predict"""
    padding = ['PAD'] * sequence_length
    for i in range(len(samples) - sequence_length - 1):
        yield [padding + samples[max(0, i - sequence_length):i], samples[i]]
        padding = padding[1:]


def learn():
    num_samples = 15000

    fizz_buzz_samples = [fizz_buzz(i) for i in range(1, num_samples + 1)]
    print(fizz_buzz_samples)
    samples = list(build_samples(fizz_buzz_samples))

    lb = LabelBinarizer()
    lb.fit(fizz_buzz_samples + ['PAD'])

    X = np.array([np.array(lb.transform(x)).flatten() for x, y in samples])
    y = np.array([y for x, y in samples])

    X_train, X_test, y_train, y_test = X[0:10000], X[10000:15000], y[0:10000], y[10000:15000], 

    # Learn a simple logistic regression classifier
    clf = LogisticRegression(tol=1e-6)
    clf.fit(X_train, y_train)

    print(clf.score(X_test, y_test))

    # save binarize and classifier
    with open('binarizer.pkl', 'wb') as binarizer_file:
        pickle.dump(lb, binarizer_file)

    with open('classifier.pkl', 'wb') as classifier_file:
        pickle.dump(clf, classifier_file)

if __name__ == "__main__":
    learn()

And, here is the code for your Algorithmia API:

import numpy as np
import pickle

import Algorithmia

client = Algorithmia.client()

sequence_length = 15

binarizer_url = 'data://USER_NAME/COLLECTION_NAME/binarizer.pkl'
classifier_url = 'data://USER_NAME/COLLECTION_NAME/classifier.pkl'
binarizer_path = client.file(binarizer_url).getFile().name
classifier_path = client.file(classifier_url).getFile().name
with open(binarizer_path, 'rb') as binarizer_file:
    lb = pickle.load(binarizer_file)
with open(classifier_path, 'rb') as classifier_file:
    clf = pickle.load(classifier_file)

def apply(input):
    pad = ['PAD'] * sequence_length
    input = []
    res = []
    for i in range(1, 101):
        X = lb.transform(pad + input)
        predicted = clf.predict([np.array(X).flatten()])[0]
        if predicted == "i":
            res.append(str(i))
        else:
            res.append(predicted)
        
        input.append(predicted)

        pad = pad[1:]
        if len(input) > sequence_length:
            input = input[1:]
    return res

Daniël is a software engineer & Computing Science student currently researching whether Machine Learning models can improve Source Code Plagiarism Detection. Github

More Posts - Website

Follow Me:
TwitterLinkedIn