A Tell of Tensorflow Probability. Regardless of whatever we think about... | by Shubhadeep Roychowdhury | Towards Data Science

Regardless of whatever we think about the mysterious subject of Probability, we live and breath in a stochastic environment. From the ever elusive Quantum Mechanics to our daily life (“There is 70% chance it will rain today”, “The chance of getting the job done in time is less than 30%” ... ) we use it, knowingly or unknowingly. We live in a “Chancy, Chancy, Chancy world”. And thus, knowing how to reason about it, is one of the most important tools in the arsenal of any person.
In this article, I will not introduce or explain probability in details (that would make it too long anyway), neither will I try to give some general picture. This article is about a specific problem and how I solved it using Python and Tensorflow probability.
We will touch upon some basic definitions before going into the implementation details. For a detailed introduction, you can pick up any good book or if you are short in time, then you can have a look at this excellent article.
Without further ado, let us begin the journey.
A fair dice is rolled if the result is an odd number then a fair coin is tossed 3 times. Otherwise, if the result is even number then a fair coin will be tossed 2 times. In both cases, # of heads is counted. What’s the variance of # heads obtained?
There are, of course, a few ways to solve this problem. You can look into them in the original Quora thread where I got it from. The approaches are different, however, in the end the result is about 0.68.
Here, we will see how can we use Python and TensorFlow-probability to simulate an experiment and verify the result we obtained previously.
One word of caution: we could have used any other packages, e.g. numpy, to achieve the same goal, but I am starting to read the super impressive and super popular “Probabilistic Programming & Bayesian Methods for Hackers” and in the recent version it has been ported to tfp (that is how we will call TensorFlow-Probability from now on) and hence I am using it.
Before we define what Random Variable and Probability Distribution mean, we need to give a usable definition of what we mean by Probability. I am going to quote it from the brilliant article I mentioned before because I believe it nicely captures the essence of the term without any mathematical instrumentation.
Probability theory is a mathematical framework for quantifying our uncertainty about the world. It allows us (and our software) to reason effectively in situations where being certain is impossible
Okay, that is great. Now we need to understand what is a random variable and what do we mean by a probability distribution.
Random Variable —A random variable is exactly what it says, a variable which can take random values. However, there is a small caveat. It can not take any random value. Whenever we speak about a random variable we implicitly or explicitly attach it to something called “A Sample Space”. Now that may sound a bit scary but fear not.
A sample space is the set of all values that a certain variable can take when we are performing an experiment. Let’s say, for the sake of simplicity, we are tossing a coin. Then the sample space can be expressed as — {H, T} where H stands for a Head, and T stands for a Tail. So if we assign the result of the experiment of tossing a coin to a variable x then a series of values of x may look like this — {H, T, H, T, T, H, H, H, T...} As we can see that x can take the value H or T randomly and at the end of each experiment we record the value it took for that particular one.
One thing to notice here, we did not talk about any chance event yet. We just established a variable which can take random values. That is because the chance factor will come next. We call it a probability distribution.
Probability Distribution — A probability distribution is a way (function, mathematically) to determine what is the chance that a random variable takes a certain value in the sample space. So, extending from the example above, let’s say we are tossing a fair coin, in that case, we have 50% chance that it turns out as Head and 50% chance that it turns out as Tail. We can model this using something called Bernoulli Distribution where we can mention that there is p (p ≤ 1) chance that event 1 occurs (in our case which will map to Head) and a 1-p chance that event 0 happens (in our case which will mean Tail). If we write a code where we model the phenomena of coin tossing using Bernoulli distribution and then run the experiment enough amount of time we will see the percentage of Head is converging to 50% and same for Tail.
We can think an extension of Bernoulli distribution for an experiment where more than 2 types of result are possible (rolling a die). We can define the probability of the occurrence of each of the results. As an example, for the case of a fair die, the probability of each surface is 1/6. We call this kind of probability distribution a Multinomial Distribution. (I did not say the entire truth, Multinomial Distribution is not really an extension of Bernoulli distribution, it is rather a generalization of Binomial Distribution. And Bernoulli distribution is a special case of Binomial Distribution. However, for the sake of this implementation, we can overlook this detail.)
We are now armed with all the knowledge we need to solve this problem. I am assuming basic familiarity with terms like “Variance”, “Mean” etc. so not going to explain them. That being said, I present the logical steps to solve this problem using Python.
We need to model the throw of a die using a Multinomial Distribution and throw the die (actually performing the experiment) many times. For the sake of the solution, we will assume we are throwing the die 1000 times.
We need to record how many times each face turns up and store that in a list.
Once the above step is done we need to take each member of the list, and depending upon whether it is an even or odd indexed element we have to throw a coin number_of_times_that_index_turned_up * (2 or 3). To understand this statement better let’s take an example list — [1, 3, 2, 1, 2, 1]. Suppose this signifies the result of throwing the die 10 times. Now this means among 10 times 1 turned up once while 2 turned up 3 times. So on and so forth. Once we have this list we need to go over it one by one and depending upon the face (odd or even) we need to toss a fair coin, value * 3 or value * 2 times. So for 1, we will toss it 3 times only (Number of times 1 showed up during our experiment is 1). However, for 2 we will toss it 6 times (it showed up 3 times and for each time we need to toss twice, so 3 * 2 = 6)
We model the tossing using Bernoulli distribution and record the number of times head shows up (assuming 1 means Head)
We calculate the variance of the Heads at end of each tossing experiment and store the that in a global list
We do all the above steps many times, let’s say 100 times.
At the end of 100 times, we take the mean of the Global Variance list.
And finally, here is the code for the logic I just described.
#import important libs
import tensorflow as tf
import tensorflow_probability as tfp
import numpy as np
# Set the mode to eager execution
tf.enable_eager_execution()

# We have a fair die, so the probability of each side is equally likely
probs = [1/6.] * 6
probs

[0.16666666666666666,
 0.16666666666666666,
 0.16666666666666666,
 0.16666666666666666,
 0.16666666666666666,
 0.16666666666666666]
# This is where we are going to store the final result of each experiment
var_arr = []

# We do the same experiment 100 times
for k in range(0, 100):
    # Each time we throw our fair die 1000 times and record how many times each side is shown
    dices = tfp.distributions.Multinomial(total_count=1000, probs=probs)
    n = dices.sample()
    # Then conver the tf.tensor to a numpy array
    l = n.numpy()
    L = []
    # For each side of the die
    for i, nums in enumerate(l):
        # For how many times that side has turned up
        for _ in range(int(nums)):
            # if it is an odd number fase of the die (1, 3, 5) then toss the fair coin 3 times else 2 times
            mul_by = 3 if (i + 1) % 2 != 0 else 2
            # It is a fair coin thus each side is equally probable
            tosses = tfp.distributions.Bernoulli(probs=0.5)
            coin_flip_data = tosses.sample(mul_by)
            l2 = coin_flip_data.numpy()
            # How many times head turned up (assuming HEAD = 1)
            num_heads = np.sum(l2)
            L += [num_heads]
    # Calculate the variance and store it
    var_arr.append(np.var(L))

np.mean(var_arr)

0.68355216
There it is, the mean of the Global Variance list is... (drum roll 🥁) 0.68355... We have verified the mathematical prediction using robust modeling and experiment. Bravo!
Thank you for reading until here. I know it has been a long read and you had been very understanding with me. Thanks again for that. You need to have Tensorflow, tfp, and numpy installed to run this notebook. They are fairly easy to install using pip so I will not go into the details.
If you enjoyed this article, please clap as many times as you want, this will be encouraging for me to write similar articles in the future.
Also, I am writing a book on Data Wrangling using Python with my co-author Tirthajyoti and it will come out soon. We hope to add a lot of value to the ecosystem via this. Have a look or pick up one ;) if you wish!
Finally, please contact me on Linkedin if you have any feedback (or leave a comment here!). I will be very happy to connect.
See you soon :)