Bayesian Risk Update (Think Bayes 2) Based on Downey (2022), Chapters 1 & 2

I used Allen Downey’s ThinkBayes2 library to practice Diachronic Bayes, the process of updating a hypothesis ($H$) based on new data (D).

  • The Problem: I worked through the “Cookie Problem” and “Monty Hall Problem” to understand the mechanics of the formula: P(H|D) = (P(H)P(D|H))/(P(D)).
  • Application to Risk: I treated the “Prior” (P(H)) as our initial risk assessment (e.g., “There is a 10% chance of a breach”). I then calculated the “Likelihood” (P(D|H)) based on new evidence (e.g., “A firewall log showed 5 failed login attempts”).
  • Outcome: The calculation produced a “Posterior” probability, mathematically demonstrating that risk is dynamic. This highlighted a flaw in traditional “static” risk registers, which often fail to account for real-time threat intelligence.

Description: This script adapts the ‘Think Bayes’ methodology to a security context. It updates the probability of a specific threat (Hypothesis) being active after observing a specific indicator (Evidence).

# A simplified class structure based on Downey's 'Pmf' (Probability Mass Function)
class RiskHypothesis:
def __init__(self, priors):
"""
priors: Dictionary of {Hypothesis: Probability}
e.g., {'High_Risk': 0.1, 'Low_Risk': 0.9}
"""
self.hypotheses = priors
def normalize(self):
"""Ensures all probabilities sum to 1.0"""
total = sum(self.hypotheses.values())
for hypo in self.hypotheses:
self.hypotheses[hypo] /= total
def update(self, evidence, likelihoods):
"""
Bayes Theorem Application: P(H|E) = P(H) * P(E|H) / P(E)
evidence: String name of the evidence observed
likelihoods: Dictionary of {Hypothesis: Probability_of_Evidence}
"""
for hypo in self.hypotheses:
# 1. Get the Prior P(H)
prior = self.hypotheses[hypo]
# 2. Get the Likelihood P(E|H)
# "If this hypothesis were true, how likely is this evidence?"
likelihood = likelihoods[hypo]
# 3. Calculate Un-normalized Posterior
self.hypotheses[hypo] = prior * likelihood
# 4. Normalize (dividing by P(E))
self.normalize()
# --- USE CASE: INCIDENT RESPONSE ---
# Scenario: We see a failed login. Is it a Brute Force Attack or just a User Mistake?
# 1. ESTABLISH PRIORS (Baseline probability)
# We assume Brute Force attacks are rare (10%) compared to User Mistakes (90%)
priors = {'Brute_Force': 0.1, 'User_Mistake': 0.9}
risk_model = RiskHypothesis(priors)
print("--- PRIOR BELIEFS ---")
print(risk_model.hypotheses)
# 2. NEW EVIDENCE: 5 Failed Logins in 1 Minute
# Likelihood P(E|H):
# - If it IS a Brute Force attack, 5 fails in 1 min is very likely (90%)
# - If it IS a User Mistake, 5 fails in 1 min is rare (5%)
evidence_likelihoods = {
'Brute_Force': 0.90,
'User_Mistake': 0.05
}
# 3. UPDATE BELIEFS
risk_model.update(evidence="5_fails_1_min", likelihoods=evidence_likelihoods)
print("\n--- POSTERIOR BELIEFS (After Evidence) ---")
for hypo, prob in risk_model.hypotheses.items():
print(f"{hypo}: {prob:.2%}")
# Result: The probability of 'Brute_Force' will jump significantly

References: