Hi there,
I work on Causal Bayesian Networks and currently implementing common cause (A <—C ---->B) and chain (A —> C ---->B) structures that work perfectly fine. Those randomvariable (A, B, and C) are dependent in general and become independent if C is given and below is my pyro’s code for common cause structure where crime (A) <—economics (C)---->obesity (B).
def model(crime_obs, economics_obs, obesity_obs):
# declare the beta dist's parameters
alphaEco, betaEco = torch.tensor(0.5), torch.tensor(0.5)
alphaCri, betaCri = torch.tensor([0.5, 0.5]), torch.tensor([0.5, 0.5])
alphaObe, betaObe = torch.tensor([0.5, 0.5]), torch.tensor([0.5, 0.5])
p_economics = torch.tensor(0.5)
p_crime = torch.tensor([0.5, 0.5])
p_obesity = torch.tensor([0.5, 0.5])
economics = pyro.sample("economics", dist.Beta(alphaEco, betaEco))
p_economics = economics
e = torch.round(economics).long()
crime = pyro.sample("crime", dist.Beta(alphaCri[e], betaCri[e]))
c = torch.round(crime).long()
p_crime[e] = crime
obesity = pyro.sample("obesity", dist.Beta(alphaObe[e], betaObe[e]))
o = torch.round(obesity).long()
p_obesity[e] = obesity
with pyro.plate('happy_plate_1', len(crime_obs), subsample_size=100) as ind:
eOb = pyro.sample("economics_obs", dist.Bernoulli(p_economics), obs=economics_obs.index_select(0, ind))
cOb = pyro.sample("crime_obs", dist.Bernoulli(p_crime[eOb.long()]), obs=crime_obs.index_select(0, ind))
oOb = pyro.sample("obesity_obs", dist.Bernoulli(p_obesity[eOb.long()]), obs=obesity_obs.index_select(0, ind))
def guide(crime_obs, economics_obs, obesity_obs):
# print("---guide---")
alphaEco = pyro.param('alphaEco', torch.tensor(0.5), constraint=constraints.positive)
betaEco = pyro.param('betaEco', torch.tensor(0.5), constraint=constraints.positive)
alphaCri = pyro.param('alphaCri', torch.tensor([0.5, 0.5]), constraint=constraints.positive)
betaCri = pyro.param('betaCri', torch.tensor([0.5, 0.5]), constraint=constraints.positive)
alphaObe = pyro.param('alphaObe', torch.tensor([0.5, 0.5]), constraint=constraints.positive)
betaObe = pyro.param('betaObe', torch.tensor([0.5, 0.5]), constraint=constraints.positive)
p_economics = pyro.param('p_economics', torch.tensor(0.5), constraint=constraints.unit_interval)
p_crime = pyro.param('p_crime', torch.tensor([0.5, 0.5]), constraint=constraints.simplex)
p_obesity = pyro.param('p_obesity', torch.tensor([0.5, 0.5]), constraint=constraints.simplex)
economics = pyro.sample("economics", dist.Beta(alphaEco, betaEco))
p_economics = economics
e = torch.round(economics).long()
crime = pyro.sample("crime", dist.Beta(alphaCri[e], betaCri[e]))
c = torch.round(crime).long()
p_crime[e] = crime
#
obesity = pyro.sample("obesity", dist.Beta(alphaObe[e], betaObe[e]))
o = torch.round(obesity).long()
p_obesity[e] = obesity
with pyro.plate('happy_plate_1', 100):
eOb = pyro.sample("economics_obs", dist.Bernoulli(p_economics))
cOb = pyro.sample("crime_obs", dist.Bernoulli(p_crime[eOb.long()]))
oOb = pyro.sample("obesity_obs", dist.Bernoulli(p_obesity[eOb.long()]))
We can see that A, B, and C are dependent ( pyro.sample with prior called outside plate), and given observation made them all independent ( pyro.sample with observation called inside plate). The learning model is satisfied because it can explain by following the rule from the discussion.
However, it becomes a problem when I applied this understanding to a collider. I am suffering to design the plate when the structure of the causal model is a collider. I have a simple collider where A causes C and B causes C, RushHour (A) —> Traffic (C) <----Expo (B). A and B are independent in general, but become dependent if C is given. Moreover, conditioning on either A or B is only affected on C. I have codded following the guidline as shown belows:
plate1 = pyro.plate(‘plate_1’, len(RushHour_obs), subsample_size=100)
plate2 = pyro.plate(‘plate_2’, len(Expo_obs), subsample_size=100)
with plate1 as ind:
rOb = pyro.sample(“RushHour_obs”, dist.Bernoulli(p_rushHour), obs=RushHour_obs.index_select(0, ind))
with plate2 as ind:
eOb = pyro.sample(“Expo_obs”, dist.Bernoulli(p_Expo), obs=Expo_obs.index_select(0, ind))
with plate1:
tOb = pyro.sample(“Traffic_obs”, dist.Bernoulli(p_Traffic[RushHour_obs.index_select(0, ind).long(), Expo_obs.index_select(0, ind).long()]), obs=Traffic_obs.index_select(0, ind))
Unfortunately, the plate is still not working and the model parameters are still unreasonable. My question is what is my confusion point that I cannot employ plate with the collider’s definition and how to encode collider structure using Pyro correctly.