On this page:
3.1 Medical diagnosis
3.2 Network diagnosis
3.3 Bayesian cryptanalysis
3.4 The common cause
3.5 Explaining away
3.6 Independent learning
3.6.1 The noisy sensors
3.6.2 The friends at a table
9.1

3 Bayesian Reasoning🔗

We are constantly observing facts about the world and updating our state of beliefs about it. We go outside and see that it is cloudy: this increases our confidence that it will rain today. We have a runny nose: we might be getting sick. We are remarkably adept at maintaining a state of belief about how the world works and integrating new information into that state in order to behave rationally.

Roulette has the ability to encode this form of belief updating using the observe! function. For example, suppose you are playing a betting game with your friend involving dice rolling. The game works like this:
  • Your friend rolls 2 dice and tells you their sum.

  • You bet on the value of the first dice, after hearing their sum.

For example, if your friend tells you that the sum of the two dice is 2, then you can safely bet your life savings on the first dice being 1. But, chances are, your friend won’t give you very good odds on that bet!

Let’s encode this situation into Roulette. First, we roll two dice:

> (define (roll-dice)
  (make-categorical `((1 . 1/6) (2 . 1/6) (3 . 1/6) (4 . 1/6) (5 . 1/6) (6 . 1/6))))
> (define d1 (roll-dice))
> (define d2 (roll-dice))

Now, suppose your friend tells you the sum of the two dice is 3. We can encode this observation into Roulette using the observe! function:

> (observe! (= (+ d1 d2) 3))
> d1

┌─────┬───────────┐

│Value│Probability│

├─────┼───────────┤

│1    │1/2        │

│2    │1/2        │

└─────┴───────────┘

Now, crucially, the probability of the first dice has changed, because we learned something new. Let’s look at what it is:

> d1

┌─────┬───────────┐

│Value│Probability│

├─────┼───────────┤

│1    │1/2        │

│2    │1/2        │

└─────┴───────────┘

As terminology, we call the probability of d1 before the observation occurs the prior probability of d1, and we call the probability after the observation the posterior probability of d1.

Intuitively, why is this answer correct? We can understand it as follows: the observation restricts the set of events down to those when the observation holds. There are two possible worlds where the event (equal? (+ d1 d2) 3) is #t:

  • d1=1, d2=2

  • d1=2, d2=1

Clearly, there are only two possibilities above, and they each have equal probability. So, the posterior probability that d1=1 is 1/2. This process of updating probabilities to incorporate new evidence is called Bayesian updating or Bayesian reasoning. We will see how it is a powerful tool for reasoning about probability distributions and expressing interesting models.

3.1 Medical diagnosis🔗

Suppose you have the following situation: there is a test that can tell you whether or not you have cancer. Before we dive into things, it helps to be very clear about the kinds of outcomes a test like this can have:

  • A true positive is when they disease is present and the test accurately reports the presence of the disease.

  • A false positive is when no disease is present but the test inaccurately reports the presence of a disease.

  • A true negative is when the disease is not present and the test accurately reports the absence of the disease.

  • A false negative is when the disease is present but the test inaccurately reports the absence of the disease.

So a crucial way to characterize a diagnostic test is by its false positive rate and false negative rate, which characterize how often a particular test outputs a false positive or false negative respectively. For instance, we might say a particular test has a false positive rate of 20% and a false negative rate of 1%.

The notion of a baseline prevalence is an important simplifying idea in probabilistic modeling. We make an assumption that an average person who walks into the hospital is drawn at random from a uniform pool of all people, who inherently carry some baseline rate of having the disease. This is an example of probabilistic abstraction.

Then, if we further suppose a baseline prevalence of 5% of people having this kind of cancer on average, then we can model this scenario in Roulette:

> (define has-cancer (flip 1/20))
> (define test-result (if has-cancer
      (flip 99/100)
      (flip 1/5)))
> (observe! test-result)
> has-cancer

┌─────┬───────────┐

│Value│Probability│

├─────┼───────────┤

│#t   │99/479     │

│#f   │380/479    │

└─────┴───────────┘

Now we must interpret the above results. We see that, even if the test comes up positive, it is still very likely that this particular patient not in fact have cancer! So, this is not a very informative test.

A common probability misconception is that you can interpret how useful a test is without knowing the baseline prevalence: this is called baseline neglect or the base rate fallacy. For example, you might assume that a test with a 0.1% false positive rate and 0.0001% false negative rate is great, but look what happens if the baseline rate is quite low:
> (define has-rare-cancer (flip 1/10000000))
> (define test-result (if has-rare-cancer
      (flip 99999/100000)
      (flip 1/1000)))
> (observe! test-result)
> has-rare-cancer

┌─────┬───────────────────┐

│Value│Probability        │

├─────┼───────────────────┤

│#t   │11111/111122211    │

│#f   │111111100/111122211│

└─────┴───────────────────┘

We see that, even though this test seemingly has a very low false-positive rate, it is still overwhelmingly likely that even with a positive test, the person does not have cancer! The moral of this story is: probability is subtle, and humans are often wrong in their intuitions about it.

3.2 Network diagnosis🔗

Work in progress

> (define r1->r2 (flip 0.96))
> (define reaches (if (flip 0.5)
       (and r1->r2 (flip 0.99))
       (and (flip 0.92) (flip 0.98))))
> (observe! (not reaches))
> r1->r2

┌─────┬──────────────────┐

│Value│Probability       │

├─────┼──────────────────┤

│#t   │0.7031351351351349│

│#f   │0.2968648648648651│

└─────┴──────────────────┘

3.3 Bayesian cryptanalysis🔗

Apocryphally, Julius Caesar would tattoo the secret key for his cipher onto the heads of the messengers who delivered his ciphertexts, who would grow their hair before their journey to ensure the key would not be revealed.

A spectacular application of Bayesian reasoning happens in the breaking of cryptographic ciphers. Let’s develop and break a famous cryptographic system called the Caesar cipher, which works as follows:
  • The key k is a number between 1 and 26.

  • To encrypt a letter, shift that letter by k. For instance, encrypting the letter e by the key 2 yields the ciphertext g.

We can implement this as the following Roulette program, where we encode the 26 letters of the English alphabet as numbers, where we use the modulo function to wrap the sum around:
> (define (enc cleartext key)
  (modulo (+ cleartext key) 26))
> (define (uniform-number low high)
   (define (make-pairs lo hi num)
     (if (> lo hi)
         '()
         (cons (cons lo num)
               (make-pairs (add1 lo) hi num))))
   (make-categorical (make-pairs low high (/ 1 (- high low)))))
> (define key (uniform-number 0 25))
> (enc 1 key)

┌─────┬───────────────────┐

│Value│Probability        │

├─────┼───────────────────┤

│16   │0.03999999999999999│

│10   │0.03999999999999999│

│11   │0.03999999999999999│

│12   │0.03999999999999999│

│13   │0.03999999999999999│

│14   │0.03999999999999999│

│15   │0.03999999999999999│

│1    │0.03999999999999999│

│17   │0.03999999999999999│

│2    │0.03999999999999999│

│18   │0.03999999999999999│

│3    │0.03999999999999999│

│19   │0.03999999999999999│

│4    │0.03999999999999999│

│20   │0.03999999999999999│

│5    │0.03999999999999999│

│21   │0.03999999999999999│

│6    │0.03999999999999999│

│22   │0.03999999999999999│

│7    │0.03999999999999999│

│23   │0.03999999999999999│

│8    │0.03999999999999999│

│24   │0.03999999999999999│

│9    │0.03999999999999999│

│25   │0.03999999999999999│

└─────┴───────────────────┘

This looks great! It’s a perfectly uniform output distribution. But, where things get tricky is when we start re-using the same key to encrypt multiple characters. Now, we need a bit of cleverness to see why this is a problem. Suppose we’re an adversary and we see the following ciphertext:

AAFEIACAEAFCAEEFIAJEFF

This text on its own is indecipherable, but we notice something: the distribution of letters we see in the ciphertext is not perfectly uniform. Some letters occur more often than others! For example, it’s well-known that in the English language, the letter "E" is the most common letter: on average, an English letter is "E" about 12% of the time, followed by the letter "T" at 9%, and so on. We can build a large dataset of the English language, and from that we can tabulate the relative frequencies of each letter and summarize them (the power of abstraction!) like this:

Of course, we can represent this distribution on letters in Roulette:

> (define letter-freqs
  '((0 . 0.08167)  (1 . 0.01492)  (2 . 0.02782)  (3 . 0.04253)
    (4 . 0.12702)  (5 . 0.02228)  (6 . 0.02015)  (7 . 0.06094)
    (8 . 0.06966)  (9 . 0.00153)  (10 . 0.00772) (11 . 0.04025)
    (12 . 0.02406) (13 . 0.06749) (14 . 0.07507) (15 . 0.01929)
    (16 . 0.00095) (17 . 0.05987) (18 . 0.06327) (19 . 0.09056)
    (20 . 0.02758) (21 . 0.00978) (22 . 0.0236) (23 . 0.0015)
    (24 . 0.01974) (25 . 0.00074)))
> (define (letter-dist)
      (make-categorical letter-freqs))

Now we can set up a frequency analysis by observing a particular ciphertext and compute the posterior probability on the key, assuming each letter is drawn from the standard distribution of letter frequencies:

; define cyphertext
> (define cyphertext (list 20 17 24 24 1))
; uniform prior on keys
> (define key (uniform-number 0 25))
; assume each letter drawn from letter distribution
> (observe! (equal? (enc (letter-dist) key) 20))
> (observe! (equal? (enc (letter-dist) key) 17))
> (observe! (equal? (enc (letter-dist) key) 24))
> (observe! (equal? (enc (letter-dist) key) 24))
> (observe! (equal? (enc (letter-dist) key) 1))
> key

┌─────┬──────────────────────┐

│Value│Probability           │

├─────┼──────────────────────┤

│9    │0.024656516149556593  │

│10   │0.05929006252600924   │

│11   │4.9829784745113537e-5 │

│12   │0.006473545405856663  │

│13   │0.3516355795121869    │

│14   │0.0012875512660878053 │

│15   │1.3039798037913137e-5 │

│0    │0.00358580532673225   │

│16   │0.13825913158756636   │

│1    │5.905071507522831e-6  │

│17   │0.037196339815590625  │

│2    │0.00019043001414818382│

│18   │4.841500589417426e-6  │

│3    │0.0031697178106921743 │

│19   │0.0038040821130813905 │

│4    │2.7325170746876036e-5 │

│20   │0.04499047201637577   │

│5    │0.03355310759807375   │

│21   │0.00024096787650173295│

│6    │0.04418622427022179   │

│22   │0.0012434705647599907 │

│7    │0.019239313652002262  │

│23   │0.0004369335001639928 │

│8    │1.1238044474625339e-6 │

│24   │0.2264586838643183    │

└─────┴──────────────────────┘

We see that the most likely key by far is 13. It turns out that the key here is in fact 13: if we rotate these letters by 13, we get back the plaintext "HELLO". Surely, seeing more text would have given us more information: this plaintext is quite short. There are a few observations I want to make about this example:

  • We are starting to scale up our examples. Maybe you’re starting to notice something else here: this is quite a big and complicated probability distribution! There are a lot of possible random letters, and a fairly sophisticated constraint being placed on them. There’s no way you would want to calculate this by hand.

  • Seemingly aggressive probabilistic abstractions can nonetheless yield valid conclusions. Our simplifying assumption about English was extremely aggressive: we assumed every letter was drawn uniformly from the baseline frequency of letters, which clearly not how we ourselves speak English! But, nonetheless, as we observe more data, structure emerges from the abstractions like a crystal forming in solution.

3.4 The common cause🔗

Hopefully by now you’re convinced that probability is essential for making rational inferences about the world, and also that probability can be surprisingly tricky and subtle to work with. To grapple with this, let’s dive deeper into the structure of probabilistic reasoning and begin to build important vocabulary, and get some more experience with modeling and reasoning. We’ll do this by examining more abstract scenarios and observing how probabilities change as we observe things about them, in order to give general principles and phenomena names.

One important phenomenon is the common cause: when two observable outcomes both are caused by the same underlying event. A good example of this is when a disease has two possible observable symptoms. For example, a flu might have the symptoms of coughing and having a fever, which we can model:

> (define has-flu (flip 1/50))
> (define has-cough (if has-flu (flip 3/4) (flip 1/30)))
> (define has-fever (if has-flu (flip 1/2) (flip 1/50)))

A key dynamic of a common cause is learning about one of the effects tells you information about the other. For instance, before observing anything, we can get the probabilities of has-cough and has-fever:
> has-cough

┌─────┬────────────────────┐

│Value│Probability         │

├─────┼────────────────────┤

│#t   │0.047666666666666656│

│#f   │0.9523333333333334  │

└─────┴────────────────────┘

> has-fever

┌─────┬────────────────────┐

│Value│Probability         │

├─────┼────────────────────┤

│#t   │0.029600000000000005│

│#f   │0.9703999999999999  │

└─────┴────────────────────┘

Then, suppose we observe that someone has a cough. Perhaps surprisingly, this increases the chance you have a fever:
> (observe! has-cough)
> has-fever

┌─────┬───────────────────┐

│Value│Probability        │

├─────┼───────────────────┤

│#t   │0.17104895104895104│

│#f   │0.8289510489510489 │

└─────┴───────────────────┘

Why is this the case? Intuitively, it is because learning that someone has a cough increases the probability that they have a flu, which in turn increases the probability that they have a fever.

3.5 Explaining away🔗

> (define burglary (flip 1/10))
> (define earthquake (flip 1/30))
> (define alarm (if (or burglary earthquake) (flip 4/5) (flip 1/100)))
> (observe! alarm)
> burglary

┌─────┬──────────────────┐

│Value│Probability       │

├─────┼──────────────────┤

│#t   │0.709849157054126 │

│#f   │0.2901508429458739│

└─────┴──────────────────┘

> earthquake

┌─────┬───────────────────┐

│Value│Probability        │

├─────┼───────────────────┤

│#t   │0.23661638568470866│

│#f   │0.7633836143152913 │

└─────┴───────────────────┘

Now we can observe alarm:
> (observe! earthquake)
> burglary

┌─────┬───────────────────┐

│Value│Probability        │

├─────┼───────────────────┤

│#t   │0.09999999999999999│

│#f   │0.9                │

└─────┴───────────────────┘

Notice how the probability of burglary went down after observing alarm! This is because we learned about what caused the alarm to go off, which greatly reduced the chances of it being caused by the burglary. This is called explaining away: the knowledge of an earthquake explained away the possible burglary.

3.6 Independent learning🔗

3.6.1 The noisy sensors🔗

You are running a moon base and you have three separate sensors that monitor oxygen levels. There are three possible oxygen levels: nominal, low, and danger. Each sensor has a 5% chance of being broken, meaning it outputs a random oxygen level. On average, the oxygen is nominal 98% of the time, low 1.9% of the time, and danger 0.1% of the time. If the sensor is working (95% chance), then there is a 85% chance the sensor outputs the correct oxygen level and a 15% chance it outputs nominal.

Suppose the sensors output nominal nominal danger. Write a Roulette program that outputs the probability that the oxygen level is dangerous in this situation.

3.6.2 The friends at a table🔗

Suppose there are 5 friends named Alice, Bob, Charlie, Derek, and Evan that want to sit around a table with 5 seats. Alice and Charlie do not want to sit next to each other. Write a Roulette program that calculates the probability that Alice sits next to either Evan or Derek.