Hoppa till innehåll

Bayesianska nätverk i Python

Jag implementerar två bayesianska nätverk i denna handledning, en modell för Monty Hall-problemet och en modell för ett larmproblem. Ett bayesianskt nätverk är en kunskapsbas med sannolikhetsbaserad information, det kan användas för beslutsfattande i osäkra miljöer.

Bayesianska nätverk är en systematisk representation av villkorade oberoende relationer. Dessa nätverk kan användas för att lagra osäker kunskap på ett naturligt sätt. Bayesianska nätverk tillämpar sannolikhetsteori i världar med objekt och relationer. Villkorade oberoende relationer mellan variabler minskar antalet sannolikheter som måste specificeras för att representera en fullständig gemensam fördelning. En fullständig gemensam distribution kan ge svar på alla frågor, men en sådan fullständig fördelning kan bli väldigt omfattande när antalet variabler ökar.

Ett bayesianskt nätverk skapas som en riktad acyklisk graf (DAG) med noder, kanter och villkorade sannolikheter. Villkorade sannolikheter beräknas med Bayes teorem, beräkningar baseras på gemensamma sannolikhetsfördelningar som vi skapar när vi bygger nätverket. Noder representerar variabler (Alarm, Inbrott) och kanter representerar länkarna (anslutningarna) mellan noderna.

Vi kan ställa frågor till ett bayesianskt nätverk och få svar med uppskattade sannolikheter för händelser. Det är möjligt att använda olika metoder för slutsatser, vissa är exakta och långsamma medan andra är ungefärliga och snabba. Biblioteket som jag använder har följande inferensalgoritmer: Causal Inference, Variable Elimination, Belief Propagation, MPLP and Dynamic Bayesian Network Inference.

Bibliotek

Jag använder pgmpy, networkx och pylab i den här handledningen. Jag hade problem när jag installerade pgmpy eftersom biblioteket kräver torch, installationen av torch misslyckades. Jag installerade torch till Python 3.7 med: pip install https://download.pytorch.org/whl/cpu/torch-1.1.0-cp37-cp37m-win_amd64.whl.

Monty Hall-problemet

Detta problem handlar om en tävling där en tävlande kan välja mellan 1 av 3 dörrar, det är ett pris bakom en av dörrarna. Programledaren (Monty) öppnar en tom dörr efter att deltagaren har valt sin dörr och frågar deltagaren om han vill byta till den andra dörren. Frågan är om det är bäst att hålla fast vid den valda dörren eller byta till den andra dörren. Det är bäst att byta till den andra dörren eftersom det är högre sannolikhet att priset ligger bakom den dörren.

# Import libraries
import pgmpy.models
import pgmpy.inference
import networkx as nx
import pylab as plt

# Create a bayesian network
model = pgmpy.models.BayesianModel([('Guest', 'Monty'), 
                                    ('Price', 'Monty')])

# Define conditional probability distributions (CPD)

# Probability of guest selecting door 0, 1 and 2
cpd_guest = pgmpy.factors.discrete.TabularCPD('Guest', 3, [[0.33, 0.33, 0.33]])

# Probability that the price is behind door 0, 1 and 2
cpd_price = pgmpy.factors.discrete.TabularCPD('Price', 3, [[0.33, 0.33, 0.33]])

# Probability that Monty selects a door (0, 1, 2), when we know which door the guest has selected and we know were the price is
cpd_monty = pgmpy.factors.discrete.TabularCPD('Monty', 3, [[0, 0, 0, 0, 0.5, 1, 0, 1, 0.5], 
                                                           [0.5, 0, 1, 0, 0, 0, 1, 0, 0.5], 
                                                           [0.5, 1, 0, 1, 0.5, 0, 0, 0, 0]], 
                                              evidence=['Guest', 'Price'], 
                                              evidence_card=[3, 3])

# Add CPDs to the network structure
model.add_cpds(cpd_guest, cpd_price, cpd_monty)

# Check if the model is valid, throw an exception otherwise
model.check_model()

# Print probability distributions
print('Probability distribution, P(Guest)')
print(cpd_guest)
print()
print('Probability distribution, P(Price)')
print(cpd_price)
print()
print('Joint probability distribution, P(Monty | Guest, Price)')
print(cpd_monty)
print()

# Plot the model
nx.draw(model, with_labels=True)
plt.savefig('C:\\DATA\\Python-data\\bayesian-networks\\monty-hall.png')
plt.close()

# Perform variable elimination for inference
# Variable elimination (VE) is a an exact inference algorithm in bayesian networks
infer = pgmpy.inference.VariableElimination(model)

# Calculate probabilites for doors including price, the guest has selected door 0 and Monty has selected door 2
posterior_probability = infer.query(['Price'], evidence={'Guest': 0, 'Monty': 2})

# Print posterior probability
print('Posterior probability, Guest(0) and Monty(2)')
print(posterior_probability)
print()
Probability distribution, P(Guest)
+----------+------+
| Guest(0) | 0.33 |
+----------+------+
| Guest(1) | 0.33 |
+----------+------+
| Guest(2) | 0.33 |
+----------+------+

Probability distribution, P(Price)
+----------+------+
| Price(0) | 0.33 |
+----------+------+
| Price(1) | 0.33 |
+----------+------+
| Price(2) | 0.33 |
+----------+------+

Joint probability distribution, P(Monty | Guest, Price)
+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+
| Guest    | Guest(0) | Guest(0) | Guest(0) | Guest(1) | Guest(1) | Guest(1) | Guest(2) | Guest(2) | Guest(2) |
+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+
| Price    | Price(0) | Price(1) | Price(2) | Price(0) | Price(1) | Price(2) | Price(0) | Price(1) | Price(2) |
+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+
| Monty(0) | 0.0      | 0.0      | 0.0      | 0.0      | 0.5      | 1.0      | 0.0      | 1.0      | 0.5      |
+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+
| Monty(1) | 0.5      | 0.0      | 1.0      | 0.0      | 0.0      | 0.0      | 1.0      | 0.0      | 0.5      |
+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+
| Monty(2) | 0.5      | 1.0      | 0.0      | 1.0      | 0.5      | 0.0      | 0.0      | 0.0      | 0.0      |
+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+

Finding Elimination Order: : : 0it [00:00, ?it/s]
0it [00:00, ?it/s]
Posterior probability, Guest(0) and Monty(2)
+----------+--------------+
| Price    |   phi(Price) |
+==========+==============+
| Price(0) |       0.3333 |
+----------+--------------+
| Price(1) |       0.6667 |
+----------+--------------+
| Price(2) |       0.0000 |
+----------+--------------+

Larmproblem

En person har installerat ett nytt larmsystem som kan utlösas av ett inbrott eller en jordbävning. Den här personen har också två grannar (John och Mary) som ombeds ringa om de hör larmet. Detta problem modelleras i ett bayesianskt nätverk med sannolikheter kopplade till varje kant. Alarm har inbrott och jordbävning som föräldrar, JohnCalls har Alarm som förälder och MaryCalls har Alarm som förälder. Vi kan fråga nätverket: vad är sannolikheten för ett inbrott om både John och Mary ringer.

# Import libraries
import pgmpy.models
import pgmpy.inference
import networkx as nx
import pylab as plt

# Create a bayesian network 
model = pgmpy.models.BayesianModel([('Burglary', 'Alarm'), 
                                    ('Earthquake', 'Alarm'),
                                    ('Alarm', 'JohnCalls'), 
                                    ('Alarm', 'MaryCalls')])

# Define conditional probability distributions (CPD)

# Probability of burglary (True, False)
cpd_burglary = pgmpy.factors.discrete.TabularCPD('Burglary', 2, [[0.001, 0.999]])

# Probability of earthquake (True, False)
cpd_earthquake = pgmpy.factors.discrete.TabularCPD('Earthquake', 2, [[0.002, 0.998]])

# Probability of alarm going of (True, False) given a burglary and/or earthquake
cpd_alarm = pgmpy.factors.discrete.TabularCPD('Alarm', 2, [[0.95, 0.94, 0.29, 0.001], 
                                                           [0.05, 0.06, 0.71, 0.999]], 
                                              evidence=['Burglary', 'Earthquake'], 
                                              evidence_card=[2, 2])

# Probability that John calls (True, False) given that the alarm has sounded
cpd_john = pgmpy.factors.discrete.TabularCPD('JohnCalls', 2, [[0.90, 0.05], 
                                                           [0.10, 0.95]], 
                                              evidence=['Alarm'], 
                                              evidence_card=[2])

# Probability that Mary calls (True, False) given that the alarm has sounded
cpd_mary = pgmpy.factors.discrete.TabularCPD('MaryCalls', 2, [[0.70, 0.01], 
                                                           [0.30, 0.99]], 
                                              evidence=['Alarm'], 
                                              evidence_card=[2])

# Add CPDs to the network structure
model.add_cpds(cpd_burglary, cpd_earthquake, cpd_alarm, cpd_john, cpd_mary)

# Check if the model is valid, throw an exception otherwise
model.check_model()

# Print probability distributions
print('Probability distribution, P(Burglary)')
print(cpd_burglary)
print()
print('Probability distribution, P(Earthquake)')
print(cpd_earthquake)
print()
print('Joint probability distribution, P(Alarm | Burglary, Earthquake)')
print(cpd_alarm)
print()
print('Joint probability distribution, P(JohnCalls | Alarm)')
print(cpd_john)
print()
print('Joint probability distribution, P(MaryCalls | Alarm)')
print(cpd_mary)
print()

# Plot the model
nx.draw(model, with_labels=True)
plt.savefig('C:\\DATA\\Python-data\\bayesian-networks\\alarm.png')
plt.close()

# Perform variable elimination for inference
# Variable elimination (VE) is a an exact inference algorithm in bayesian networks
infer = pgmpy.inference.VariableElimination(model)

# Calculate the probability of a burglary if John and Mary calls (0: True, 1: False)
posterior_probability = infer.query(['Burglary'], evidence={'JohnCalls': 0, 'MaryCalls': 0})

# Print posterior probability
print('Posterior probability of Burglary if JohnCalls(True) and MaryCalls(True)')
print(posterior_probability)
print()

# Calculate the probability of alarm starting if there is a burglary and an earthquake (0: True, 1: False)
posterior_probability = infer.query(['Alarm'], evidence={'Burglary': 0, 'Earthquake': 0})

# Print posterior probability
print('Posterior probability of Alarm sounding if Burglary(True) and Earthquake(True)')
print(posterior_probability)
print()
Probability distribution, P(Burglary)
+-------------+-------+
| Burglary(0) | 0.001 |
+-------------+-------+
| Burglary(1) | 0.999 |
+-------------+-------+

Probability distribution, P(Earthquake)
+---------------+-------+
| Earthquake(0) | 0.002 |
+---------------+-------+
| Earthquake(1) | 0.998 |
+---------------+-------+

Joint probability distribution, P(Alarm | Burglary, Earthquake)
+------------+---------------+---------------+---------------+---------------+
| Burglary   | Burglary(0)   | Burglary(0)   | Burglary(1)   | Burglary(1)   |
+------------+---------------+---------------+---------------+---------------+
| Earthquake | Earthquake(0) | Earthquake(1) | Earthquake(0) | Earthquake(1) |
+------------+---------------+---------------+---------------+---------------+
| Alarm(0)   | 0.95          | 0.94          | 0.29          | 0.001         |
+------------+---------------+---------------+---------------+---------------+
| Alarm(1)   | 0.05          | 0.06          | 0.71          | 0.999         |
+------------+---------------+---------------+---------------+---------------+

Joint probability distribution, P(JohnCalls | Alarm)
+--------------+----------+----------+
| Alarm        | Alarm(0) | Alarm(1) |
+--------------+----------+----------+
| JohnCalls(0) | 0.9      | 0.05     |
+--------------+----------+----------+
| JohnCalls(1) | 0.1      | 0.95     |
+--------------+----------+----------+

Joint probability distribution, P(MaryCalls | Alarm)
+--------------+----------+----------+
| Alarm        | Alarm(0) | Alarm(1) |
+--------------+----------+----------+
| MaryCalls(0) | 0.7      | 0.01     |
+--------------+----------+----------+
| MaryCalls(1) | 0.3      | 0.99     |
+--------------+----------+----------+

Finding Elimination Order: : 100%|█████████████| 2/2 [00:00<00:00, 1002.94it/s]
Eliminating: Alarm: 100%|███████████████████████| 2/2 [00:00<00:00, 401.10it/s]
Posterior probability of Burglary if JohnCalls(True) and MaryCalls(True)
+-------------+-----------------+
| Burglary    |   phi(Burglary) |
+=============+=================+
| Burglary(0) |          0.2842 |
+-------------+-----------------+
| Burglary(1) |          0.7158 |
+-------------+-----------------+

Finding Elimination Order: : 100%|█████████████| 2/2 [00:00<00:00, 2008.77it/s]
Eliminating: MaryCalls: 100%|███████████████████| 2/2 [00:00<00:00, 501.08it/s]
Posterior probability of Alarm sounding if Burglary(True) and Earthquake(True)
+----------+--------------+
| Alarm    |   phi(Alarm) |
+==========+==============+
| Alarm(0) |       0.9500 |
+----------+--------------+
| Alarm(1) |       0.0500 |
+----------+--------------+
Etiketter:

Lämna ett svar

Din e-postadress kommer inte publiceras. Obligatoriska fält är märkta *