Skip to content
coden

Bayesian networks: why the grass is wet and what it means for the sprinkler

Joint distribution and its factorisation, Bayesian network and conditional probability tables, inference by enumeration, explaining away, d-separation and approximate inference by sampling — on the network cloudy, sprinkler, rain, wet grass.

13 minutes Matěj Žalmánek 2026-10-15

Series AI basics Part 40 of 52
  1. Map of artificial intelligence: what is what and how it all fits together
  2. A brief history of artificial intelligence: from Turing to language models
  3. Data, model, training, inference: the basic terms on a single example
  4. How to read mathematical notation: symbols, indices, sums and quantifiers
  5. Sets and relations: the building blocks of definitions in AI
  6. Functions and mappings: why every AI model is a function
  7. Logic and proofs: the rules AI reasons by
  8. Graphs and trees: the structure that both search and neural networks stand on
  9. Linear algebra for machine learning: vectors, matrices and why AI mostly multiplies
  10. Derivatives, the gradient and the chain rule: how a model finds out which way to learn
  11. Gradient descent: how a model learns
  12. What probability is: from rolling dice to the Kolmogorov axioms
  13. Conditional probability and Bayes’ theorem: how to update your estimate when you learn something new
  14. Random variables and the cumulative distribution function: one function that tells you everything about a distribution
  15. Probability density and probability mass functions: why a continuous variable has zero probability at a point
  16. Expected value, variance and covariance: where a distribution lies, how wide it is and what goes with what
  17. Overview of probability distributions: Bernoulli, binomial, Poisson, uniform, normal and exponential
  18. Law of large numbers and central limit theorem: why averages behave so predictably
  19. Statistics and parameter estimation: maximum likelihood, MAP and confidence intervals
  20. Information theory: entropy, cross-entropy and KL divergence — where loss functions come from
  21. State-space search: BFS, DFS, UCS and A* with animations
  22. Games and adversaries: minimax, alpha-beta pruning and Monte Carlo tree search
  23. Constraint satisfaction: backtracking, constraint propagation and AC-3 on sudoku and a map of Czech regions
  24. Logic and knowledge systems: forward and backward chaining, resolution and expert systems
  25. Planning: STRIPS, the blocks world, heuristics from relaxation and planning in AI agents
  26. Local search and simulated annealing: a travelling salesman through the Czech regional capitals
  27. Genetic algorithms: population, crossover, mutation and selection on a backpack for a hike
  28. Evolution strategies and genetic programming: from the one-fifth rule to Kepler’s law
  29. Ant colony optimization: how a pheromone trail finds a short path
  30. Particle swarm optimisation: inertia, personal experience and the swarm’s experience
  31. Neuroevolution: evolving the weights and shape of neural networks, and the NEAT algorithm
  32. Fuzzy sets and membership functions: how to write down “somewhat warm”
  33. Fuzzy logic and probability: why “somewhat warm” does not mean “probably warm”
  34. Fuzzy logic: t-norms, implications and IF–THEN rules
  35. Fuzzy control: Mamdani and Sugeno from a fan to a pole on a cart
  36. Fuzzy clustering: the fuzzy c-means algorithm and points between clusters
  37. Neuro-fuzzy systems (ANFIS): fuzzy rules that learn from data
  38. Markov chains: the transition matrix, the stationary distribution and PageRank
  39. Hidden Markov models: the Viterbi algorithm and the dishonest casino
  40. Bayesian networks: why the grass is wet and what it means for the sprinkler
  41. The Kalman filter: how to estimate where a car is and how fast it goes from imprecise measurements
  42. Types of machine learning: supervised, unsupervised, semi-supervised and more
  43. Prompts, context and RAG: how to give a model the right information
  44. Variational autoencoders (VAE): new images from a continuous latent space
  45. Generative adversarial networks (GAN): the forger versus the detective
  46. Diffusion models: how pure noise becomes an image
  47. Markov decision processes: states, actions, rewards and the Bellman equations
  48. Q-learning and DQN: learning the value of actions, from a table to a neural network
  49. Policy gradient and PPO: learning the policy directly
  50. How an AI agent works: a language model in a loop with tools
  51. Programming with AI agents: the task, tests and review
  52. Evaluating and securing AI systems: how to measure and how to keep watch

Full series syllabus 52 of 83 topics published

What you will take away
  • How a Bayesian network shrinks a huge probability table into a few small tables.
  • How a network reasons from effects to causes and what explaining away is.
  • How to read from the graph which variables are independent, and how probabilities are estimated by sampling.

Useful to know first: Conditional probability and Bayes’ theorem: how to update your estimate when you learn something new, Graphs and trees: the structure that both search and neural networks stand on

You step outside in the morning and the grass is wet. Did it rain, or was the sprinkler on? And when a neighbour then tells you it rained during the night — does that change what you think about the sprinkler? People reason like this without thinking. A machine can do it too, as long as it knows what is connected to what. A Bayesian network is a picture of these connections from which you can compute the answer to any question. Judea Pearl received the 2011 Turing Award for it and for the theory of causality built on top of it.

It builds on the parts Conditional probability and Bayes’ theorem and Graphs and trees.

1. The problem of the big table

Complete information about several random variables is their joint distribution — the probability of every combination of values. For four yes/no variables there are 16 combinations, so 15 independent numbers. For 37 variables (that is how many the well-known ALARM medical network for monitoring patients under anaesthesia has) it would be over 137 billion numbers, even if they were all just yes/no. Nobody can build such a table, store it or estimate it from data. What saves us is that most variables are related only indirectly.

Definition — joint distribution and the number of its parameters

Let (Ω, 𝓕, P) be a probability space (see the part What is probability), n ≥ 1, and let X1, …, Xn be discrete random variables on it (see the part Random variables); the variable Xi takes values in a finite or countable set 𝒳i, i = 1, …, n. The joint distribution of the variables X1, …, Xn is the function 𝒳1 × ⋯ × 𝒳n → [0, 1] that assigns to every combination of values (x1, …, xn) the number

P(x1, …, xn) = P({ω ∈ Ω : X1(ω) = x1 ∧ … ∧ Xn(ω) = xn}),

also written P(x1, …, xn) = P(X1 = x1, …, Xn = xn). From the axioms of probability it follows that:

  1. P(x1, …, xn) ≥ 0 for all (x1, …, xn) ∈ 𝒳1 × ⋯ × 𝒳n;
  2. ∑(x1, …, xn) ∈ 𝒳1 × ⋯ × 𝒳n P(x1, …, xn) = 1.

Number of parameters. If all the variables are two-valued (|𝒳i| = 2 for i = 1, …, n, values yes/no), the domain has 2n elements. Any 2n − 1 values can be chosen independently (non-negative, with a sum of at most 1), and the last one is determined by condition 2 as 1 minus their sum. The joint distribution of n two-valued variables therefore has

2n − 1

independent parameters — that many numbers are needed to determine it uniquely.

Calculation. 24 − 1 = 15; 237 − 1 = 137,438,953,471.

Theorem — the chain rule

Let (Ω, 𝓕, P) be a probability space, n ≥ 2, X1, …, Xn discrete random variables with values in 𝒳1, …, 𝒳n, and (x1, …, xn) ∈ 𝒳1 × ⋯ × 𝒳n a combination of values for which P(x1, …, xn−1) > 0. By P(xi | x1, …, xi−1) we mean the conditional probability (see the part on conditional probability) of the event {Xi = xi} given {X1 = x1, …, Xi−1 = xi−1}. Then

P(x1, …, xn) = P(x1) · P(x2 | x1) · P(x3 | x1, x2) ⋯ P(xn | x1, …, xn−1) = ∏i=1n P(xi | x1, …, xi−1),

where the factor for i = 1 is P(x1) (empty condition).

Proof: for k ≤ n − 1 the event {X1 = x1, …, Xk = xk} is a superset of the event {X1 = x1, …, Xn−1 = xn−1}, so it has positive probability and all the conditional probabilities in the formula are defined. By the definition of conditional probability

P(xn | x1, …, xn−1) = P(x1, …, xn)/P(x1, …, xn−1),

so P(x1, …, xn) = P(xn | x1, …, xn−1) · P(x1, …, xn−1). We apply the same to the second factor and so on, until only P(x1) is left. ∎

The chain rule saves nothing: the last factor still depends on all the preceding variables, so it is just another way of writing the same huge table. The savings come once we drop some of the conditions.

2. Bayesian network

Definition — Bayesian network

Let n ≥ 1 and let X1, …, Xn be variables, Xi with values in a finite non-empty set 𝒳i, i = 1, …, n. A Bayesian network over these variables is a pair (G, Θ), where

  1. G = (V, H) is a directed acyclic graph (DAG, see the part Graphs and trees) with vertex set V = {X1, …, Xn} and edge set H ⊆ V × V; the parents of the vertex Xi are parents(Xi) = {Xj ∈ V : (Xj, Xi) ∈ H};
  2. Θ assigns to every vertex Xi a conditional probability table: for every combination of values u of its parents a distribution P( · | u) on 𝒳i, i.e. numbers P(xi | u) ≥ 0 for xi ∈ 𝒳i such that ∑xi ∈ 𝒳i P(xi | u) = 1. A vertex without parents has a single distribution P(xi).

The network determines the joint distribution of the variables X1, …, Xn by the rule

P(x1, …, xn) = ∏i=1n P(xi | parents(Xi))   for all (x1, …, xn) ∈ 𝒳1 × ⋯ × 𝒳n,

where in the i-th factor we substitute for parents(Xi) the values the parents have in the combination (x1, …, xn). The product is non-negative and sums to 1 over all combinations (if we first sum over the values of the vertex that comes last in topological order, its factor gives 1 and the network without it remains), so it really is a joint distribution.

In plain words

Arrows lead from causes to effects. For each variable it is enough to know how it depends on its direct causes — nothing more. The probability of the whole situation is the product of these local numbers. Instead of one huge table we have a few small ones.

Why may the network write just P(xi | parents) and not the whole history like the chain rule? Because it assumes conditional independences about the graph.

Definition — conditional independence

Let (Ω, 𝓕, P) be a probability space and A, C, E discrete random variables on it with values in 𝒜, 𝒞, ℰ (each of them may also be a group of variables, i.e. a vector; E may be an empty group). For short we write P(a | c, e) = P(A = a | C = c, E = e) and so on. The variables A and C are conditionally independent given E, written A ⊥ C | E, if and only if

∀a ∈ 𝒜 ∀c ∈ 𝒞 ∀e ∈ ℰ :   P(c, e) > 0 ⇒ P(a | c, e) = P(a | e).

In words: once we know the value of E, knowing the value of C does not change the probability of any value of A. The equality in the definition:

P(a | c, e) = P(a | e)

P(a | c, e)
what we know about A when we know E and also C
P(a | e)
what we know about A from E alone; the equality says that C adds nothing more — the whole connection between A and C runs through E
Equivalently, P(a, c | e) = P(a | e) · P(c | e) for all a, c and all e with P(e) > 0. For an empty E it is ordinary independence.
Theorem — factorisation is the chain rule plus local independences

Let G = (V, H) be a directed acyclic graph with vertices V = {X1, …, Xn}, which are discrete random variables on a probability space (Ω, 𝓕, P), and let the vertices be numbered topologically: (Xj, Xi) ∈ H ⇒ j < i. The descendants of a vertex Xi are the vertices other than Xi to which a directed path leads from Xi; non-descendants(Xi) = V ∖ ({Xi} ∪ descendants(Xi)). Assume the local Markov condition:

∀i ∈ {1, …, n} :   Xi ⊥ (non-descendants(Xi) ∖ parents(Xi)) | parents(Xi),

i.e. every variable is, given its parents, conditionally independent of all other variables that are not its descendants. Then for every combination of values (x1, …, xn) with P(x1, …, xn−1) > 0

P(x1, …, xn) = ∏i=1n P(xi | parents(Xi)).

Proof: in the chain rule in topological order we have P(xi | x1, …, xi−1). Among X1, …, Xi−1 are all the parents of Xi (a parent has a smaller number) and none of its descendants (a descendant comes after it in the order), so {X1, …, Xi−1} ⊆ non-descendants(Xi). By the Markov condition we can drop all variables except the parents from the condition, and what remains is P(xi | parents(Xi)). ∎

It also holds the other way round: every distribution given by a product of tables satisfies the local Markov condition. That is why a Bayesian network can be defined in either of the two ways.

Our network: when it is cloudy, rain is more likely and it is less likely that someone turns on the sprinkler. Both rain and the sprinkler make the grass wet. The numbers come from Kevin Murphy’s popular example:

cloudy Z P(Z) 0.5 sprinkler P Z P(P) − 0.5 + 0.1 rain D Z P(D) − 0.2 + 0.8 wet grass T P D P(T) + − 0.9 + + 0.99 − + 0.9 − − 0
The “wet grass” network with conditional probability tables + yes, − no

The network has 1 + 2 + 2 + 4 = 9 numbers instead of 15. With four variables the saving is small, with hundreds it is huge: if every vertex has at most k parents, the number of numbers grows with n linearly (n · 2k), not exponentially (2n). The probability of the situation “cloudy, the sprinkler was off, it rained, the grass is wet” is 0.5 · 0.9 · 0.8 · 0.9 = 0.324 (colours as in the formula below).

P(z, p, d, t) = P(z) · P(p | z) · P(d | z) · P(t | p, d)

P(z)
cloudy has no parents, its table is a single number; in the example 0.5
P(p | z)
the sprinkler depends only on the clouds — here nothing differs from the chain rule; in the example it was off under a cloudy sky: 1 − 0.1 = 0.9
P(d | z)
the chain rule would want P(d | z, p); the network assumes that once the sky is known, the sprinkler says nothing about rain, so p drops out of the condition; in the example 0.8
P(t | p, d)
the chain rule would want P(t | z, p, d); the grass “knows” about the clouds only through rain and the sprinkler, so z drops out; in the example (sprinkler no, rain yes) 0.9
Every dropped term of a condition is one conditional independence the network assumes — exactly these cut 15 numbers down to 9.

3. Inference: from effect to cause

Definition — inference by enumeration

Let (G, Θ) be a Bayesian network over the variables X1, …, Xn, and let these variables be split into three disjoint groups: the query Q (one variable with values in 𝒬), the evidence E = (E1, …, Em) with observed values e = (e1, …, em), and the hidden variables Y (all the others) with the set of value combinations 𝒴. Assume P(e) > 0. Then for every q ∈ 𝒬

P(q | e) = ∑y ∈ 𝒴 P(q, e, y)/∑q′ ∈ 𝒬 ∑y ∈ 𝒴 P(q′, e, y),

where each P(q, e, y) is a value of the joint distribution of the network — a product of numbers from the tables according to the factorisation. Inference by enumeration is the algorithm that computes by this formula:

Input: the network (G, Θ), the query variable Q, evidence E = e with P(e) > 0. Output: the numbers P(q | e) for all q ∈ 𝒬. Steps:

  1. For every q ∈ 𝒬 set S(q) := 0.
  2. For every q ∈ 𝒬 and every y ∈ 𝒴 compute the product P(q, e, y) from the tables and add it to S(q).
  3. After going through all (finitely many) pairs (q, y) set Z := ∑q′ ∈ 𝒬 S(q′) = P(e) and return P(q | e) = S(q)/Z for every q ∈ 𝒬.
In plain words

We go through all possible worlds that agree with what we know (the grass is wet) and look at how much weight the ones where it rained have among them. It is Bayes’ theorem from the part on conditional probability, only summed over more variables at once.

P(q | e) = ∑y P(q, e, y)/∑q′, y P(q′, e, y)

e
evidence — the values we observed (in our case wet grass); they are fixed in all the worlds being summed
∑y
we do not know the hidden variables (in our case cloudy and the sprinkler) and do not care about them, so we sum over all their possibilities — marginalisation; the worlds are mutually exclusive, so their probabilities add up
P(q, e, y)
the probability of one complete world in which both the query and the evidence hold — a product of numbers from the tables according to the factorisation
∑q′, y P(q′, e, y)
= P(e), the weight of all worlds that agree with the evidence; the division is the definition of conditional probability P(q | e) = P(q, e)/P(e) and guarantees that ∑q P(q | e) = 1 (for a yes/no query P(Q | e) + P(¬Q | e) = 1)
Example — P(rain | wet grass) by enumeration

The evidence is T = yes, the query is D, and Z and P are hidden. There are 23 = 8 worlds with wet grass; each is the product P(z) · P(p | z) · P(d | z) · P(t | p, d):

raincloudysprinklerproductP(world)
yesyesyes0.5 · 0.1 · 0.8 · 0.990.0396
yesnoyes0.5 · 0.5 · 0.2 · 0.990.0495
yesyesno0.5 · 0.9 · 0.8 · 0.90.3240
yesnono0.5 · 0.5 · 0.2 · 0.90.0450
noyesyes0.5 · 0.1 · 0.2 · 0.90.0090
nonoyes0.5 · 0.5 · 0.8 · 0.90.1800
noyesno0.5 · 0.9 · 0.2 · 00.0000
nonono0.5 · 0.5 · 0.8 · 00.0000

The rows where it rained are the worlds P(q, e, y) for q = rain yes; their sum over the hidden Z, P gives P(D, T) = 0.4581, the sum of all eight P(T) = 0.6471. The last two rows are zero: without rain and without the sprinkler the grass is not wet. Therefore (colours as in the formula above)

P(D | T) = 0.4581/0.6471 = 0.708.

def dotaz(Q, dukazy, sit):
    citatel = jmenovatel = 0.0
    for svet in vsechna_prirazeni(sit):              # 2^n complete worlds
        if any(svet[k] != v for k, v in dukazy.items()):
            continue                                 # disagrees with the evidence
        p = 1.0
        for uzel in sit:                             # factorisation of the network
            p *= P(uzel, svet[uzel], svet[rodice(uzel)])
        jmenovatel += p                              # → P(e)
        if svet[Q]:
            citatel += p                             # → P(Q, e)
    return citatel / jmenovatel

Beforehand (without any observation) the probability of rain is 0.5 and of the sprinkler 0.3. Wet grass raises both: P(rain | wet grass) = 0.708 and P(sprinkler | wet grass) = 0.430. Rain is the more likely explanation, because it rains more often than the sprinkler runs. Wet grass also raises the probability that it was cloudy — from 0.5 to 0.576 — even though the grass and the clouds are not directly connected. Information flows through the network against the arrows.

4. Explaining away

Now we learn that it really did rain during the night. What happens to the sprinkler?

00.51 0.30 0.43 0.19 sprinkler 0.50 0.71 0.32 rain cause of the wet grass (category) probability P (0–1)
Sprinkler and rain: beforehand, given wet grass, and given wet grass when we know about the other cause beforehand the grass is wet and we know about the other cause

The probability of the sprinkler drops from 0.430 to 0.194. Rain has already explained the wet grass, so there is no reason left for the sprinkler. Conversely, when we know the sprinkler was on, the probability of rain falls from 0.708 to 0.320. This phenomenon is called explaining away. Two causes that were beforehand only weakly related start “pushing each other out” as soon as we observe their common effect. And if we knew that it had not rained, the sprinkler would have to have been on: P(sprinkler | wet grass, no rain) = 1, because in our network the grass cannot be wet by itself.

Example — explaining away in numbers

From the table in the previous example: the worlds where it rained, the sprinkler was on and the grass is wet together have P(P, D, T) = 0.0396 + 0.0495 = 0.0891. Hence

P(P | T, D) = P(P, D, T)/P(D, T) = 0.0891/0.4581 = 0.194,

P(D | T, P) = P(P, D, T)/P(P, T) = 0.0891/0.2781 = 0.320

(the denominator is the probability of the evidence P(e), colours as in the formula for inference by enumeration above).

Formally, explaining away is the inequality P(P | T, D) < P(P | T): confirming one cause lowers the probability of the other (0.194 < 0.430).

5. What the network tells you: d-separation

The graph does not only say how to compute — it also says which variables are independent of each other, and without a single number. All possibilities are made up of three basic shapes:

  • Chain A → B → C: A and C are related, but once we know B, they are not. (Cloudy → rain → wet grass: if you know whether it rained, the clouds tell you nothing about the grass.)
  • Fork A ← B → C: a common cause makes A and C dependent; once we know it, the dependence disappears. (The sprinkler and rain are related beforehand through the clouds: P(sprinkler | rain) = 0.18, but P(sprinkler | no rain) = 0.42. When we know whether it is cloudy, they are independent.)
  • Collider A → B ← C: the causes are independent until we observe the common effect — then they become dependent. That is explaining away.
Definition — d-separation

Let G = (V, H) be a directed acyclic graph, A, C ∈ V distinct vertices and E ⊆ V ∖ {A, C} a set of vertices (the observed variables).

  1. An undirected path between A and C is a sequence of mutually distinct vertices (V0, V1, …, Vk), k ≥ 1, with V0 = A, Vk = C, such that (Vj−1, Vj) ∈ H ∨ (Vj, Vj−1) ∈ H   for j = 1, …, k (neighbouring vertices are joined by an edge, the direction of the arrow does not matter).
  2. An inner vertex Vj of the path (0 < j < k) is the middle of a chain if and only if Vj−1 → Vj → Vj+1 or Vj−1 ← Vj ← Vj+1; a fork if and only if Vj−1 ← Vj → Vj+1; a collider if and only if Vj−1 → Vj ← Vj+1 (U → W means (U, W) ∈ H). Every inner vertex is exactly one of these three.
  3. A path is blocked by the set E if and only if it has an inner vertex Vj for which at least one of the following holds: (a)  Vj is the middle of a chain or a fork ∧ Vj ∈ E, (b)  Vj is the middle of a collider ∧ ({Vj} ∪ descendants(Vj)) ∩ E = ∅, where descendants(Vj) are the vertices to which a directed path leads from Vj.
  4. The vertices A and C are d-separated by the set E if and only if every undirected path between A and C is blocked by E.

Theorem (Verma and Pearl). Let (G, Θ) be a Bayesian network, P the joint distribution it determines, and A, C, E as above. If A and C are d-separated by the set E, then A ⊥ C | E with respect to P — and this for every choice of the tables Θ.

In plain words

Information flows through the graph along paths. A known variable in the middle of a chain or a fork stops the flow — it is already “explained”. With a collider it is the other way round: as long as we do not know the effect, the flow stands still there; as soon as we observe it, it opens. From the picture you can thus read off what needs to be measured and what is unnecessary.

In our network, cloudy and wet grass are d-separated when we know both the sprinkler and the rain — the calculation confirms it: the probability of a cloudy sky then does not depend on the grass at all.

Example — three shapes verified by calculation
  • Fork sprinkler ← cloudy → rain. Without knowing the sky: P(P | D) = 0.18 ≠ P(P) = 0.3, dependent. With the sky known: P(P | Z, D) = 0.1 = P(P | Z), so P ⊥ D | Z.
  • Collider sprinkler → wet grass ← rain. With the sky known, P and D are independent (P(P | Z, D) = P(P | Z, ¬D) = 0.1), but as soon as we observe the grass, they are not: P(P | Z, D, T) = 0.109, whereas P(P | Z, ¬D, T) = 1.
  • D-separation of the cloudy sky and the wet grass by the set {P, D}: both paths Z → P → T and Z → D → T are chains with a known middle, so they are blocked. And indeed: P(Z | P, D) = 0.444 and P(Z | P, D, T) = 0.444; P(Z | P, ¬D) = 0.048 and P(Z | P, ¬D, T) = 0.048.

6. Approximate inference by sampling

Inference by enumeration goes through all combinations of the hidden variables — in a general network exponentially many. Exact inference in Bayesian networks is NP-hard. For large networks the probabilities are therefore estimated by simulation.

Algorithms — rejection sampling and likelihood weighting

Both algorithms have the same input: a Bayesian network (G, Θ) over the variables X1, …, Xn numbered topologically (a parent has a smaller number than its child), a query Q = q, evidence E = e and a number of samples N ∈ ℕ, N ≥ 1; and the same output: an estimate P̂(q | e) of the number P(q | e). To draw xi means to choose a random value from 𝒳i according to the distribution P( · | u) from the table of the vertex Xi, where u are the values of its parents already determined in this sample.

Rejection sampling:

  1. Set Nacc := 0, Nq := 0.
  2. Repeat N times: for i = 1, …, n draw xi. If the sample agrees with the evidence (every evidence variable has its observed value in it), increase Nacc by 1, and if moreover Q = q holds in it, increase Nq by 1 too; otherwise throw the sample away.
  3. After N repetitions stop and return P̂(q | e) = Nq/Nacc (for Nacc = 0 the estimate is undefined).

Likelihood weighting:

  1. Set W := 0, Wq := 0.
  2. Repeat N times (the k-th sample): set wk := 1 and for i = 1, …, n: if Xi is an evidence variable with observed value ei, set xi := ei (do not draw) and wk := wk · P(ei | u), where u are the values of the parents in the sample; otherwise draw xi. Then W := W + wk, and if Q = q holds in the sample, also Wq := Wq + wk.
  3. After N repetitions stop and return P̂(q | e) = Wq/W (for W = 0 the estimate is undefined).
In plain words

Rejection is honest but wasteful: when the evidence is rare, it throws away almost all samples. Likelihood weighting throws away nothing — it just gives each sample a weight according to how well it fits what we know.

def zamitani(Q, dukazy, n, sit):
    prijato = ano = 0
    for _ in range(n):
        svet = {}
        for uzel in sit:                             # from causes to effects
            svet[uzel] = nahoda() < P(uzel, True, svet[rodice(uzel)])
        if any(svet[k] != v for k, v in dukazy.items()):
            continue                                 # into the bin
        prijato += 1
        ano += svet[Q]
    return ano / prijato

def vazena(Q, dukazy, n, sit):
    s = s_ano = 0.0
    for _ in range(n):
        svet, w = {}, 1.0
        for uzel in sit:
            p = P(uzel, True, svet[rodice(uzel)])
            if uzel in dukazy:                       # we do not draw the evidence…
                svet[uzel] = dukazy[uzel]
                w *= p if dukazy[uzel] else 1 - p    # …but we weigh how likely it is
            else:
                svet[uzel] = nahoda() < p
        s += w
        s_ano += w * svet[Q]
    return s_ano / s

P̂(Q | e) = ∑k wk [Q holds in sample k]/∑k wk,   wk = ∏j P(ej | parentsk)

wk
the weight of the sample (green like the likelihood-weighting curve in the chart) — the product of the probabilities of all pieces of evidence ej given the values of their parents that came up in sample k: we did not draw the evidence but imposed it, so we must take into account how likely it would be given the other drawn values — otherwise the estimate would be biased
[Q holds in sample k]
1 if the query holds in the sample, otherwise 0 — we count only the weight of samples in which it rained
∑k wk
the total weight; dividing by it turns the sum into a fraction, just like dividing by P(e) in enumeration. Thanks to it, it does not matter that the weights do not sum to 1
Rejection is a special case: the weight is 1 for samples that agree with the evidence and 0 for the others.
Example — weights given the evidence “the grass is wet”

The only piece of evidence is T = yes, and its parents are the sprinkler and rain. The weight of a sample is therefore simply P(T = yes | p, d) from the wet grass table:

  • sprinkler and rain: w = 0.99,
  • only one of them: w = 0.9,
  • neither: w = 0 — such a sample contributes nothing, just as rejection would throw it away.

(Colour as in the formula above.)

The weight does not depend on the sky: cloudy is not a parent of the grass.

0.60.70.8 101001,00010,000 samples (count, log scale) estimate P(rain | wet) (0–1)
Estimate of P(rain | wet grass) by number of samples rejection likelihood weighting exact 0.708

With a hundred samples, rejection gave the estimate 0.686 and used only 70 of them — the remaining 30 had dry grass and went into the bin. With 10,000 samples (of which 6,524 were left) the estimate is 0.703, close to the exact value 0.708. The error decreases as 1/√N, as the central limit theorem says: for an estimate ten times more precise you need a hundred times more samples.

σP̂ = √p(1 − p)/Nacc,   Nacc ≈ N · P(e)

p(1 − p)
the variance of one yes/no sample (Bernoulli distribution with probability p); it is largest for p = 0.5, when a single sample is least predictable
Nacc
the number of samples that actually count; in rejection only those that passed, and on average they make up the fraction P(e) — with rare evidence almost none. The variance of the mean of N independent samples is N times smaller
The square root converts the variance back into units of probability — that is why the error decreases only as 1/√N. (The equality is approximate: the number of accepted samples is itself random.)
Example — how big an error to expect

Exactly, p = 0.708 and P(e) = P(T) = 0.6471, so out of 10,000 samples we expect about 6,471 accepted (in reality there were 6,524).

  • Nacc = 70: σ = √0.708 · 0.292/70 = 0.054; actual error |0.686 − 0.708| = 0.022.
  • Nacc = 6,524: σ = 0.0056; actual error 0.0050.

Both actual errors are smaller than one standard deviation — consistent with about two thirds of the estimates falling within ±σ (normal distribution by the central limit theorem).

7. Bayesian networks in practice

  • Diagnosis. Medical networks (ALARM, QMR-DT with hundreds of diseases and thousands of symptoms) and fault diagnosis of machines and networks: infer the most likely cause from the observed symptoms — and suggest what to measure next.
  • Learning from data. The tables can be estimated from data by counting (by the maximum likelihood method from the part on statistics), even when some values are missing — with the EM algorithm, a relative of the Baum–Welch algorithm from the previous part.
  • Causality. Pearl showed that a network read as a graph of causes can also answer questions like “what would happen if we intervened” — turned on the sprinkler ourselves — which cannot be read from correlations alone. Today’s causal inference in epidemiology and economics rests on this.
  • Relatives. The naive Bayes classifier is a Bayesian network with one cause and many independent effects. Hidden Markov models from the previous part are a Bayesian network unrolled in time.

8. Summary

  • A Bayesian network is a directed acyclic graph with tables P(X | parents); the joint distribution is the product of these tables. Our network needs 9 numbers instead of 15.
  • Inference also works against the arrows: wet grass raises the probability of rain to 0.708 and of the sprinkler to 0.430.
  • Explaining away: when we know it rained, the probability of the sprinkler drops to 0.194.
  • D-separation reads independences straight from the graph — a chain and a fork are closed by observation, a collider is opened.
  • Exact inference is NP-hard in general; sampling gives estimates with an error decreasing as 1/√N.

Want to get your hands on it? Have an AI agent build a Bayesian network for a fault in your own car or computer (“it won’t start — the battery or the starter?”) and see how the suspicion changes as you add observations.

Do you see it differently? Write to support@coden-app.com.

An Agent on your computer, driven from your phone