🎯要点
🎯空间不确定性和动态相互作用自我认知异类模型 | 🎯精神病神经元算法推理 | 🎯集体信念催化个人行动力数学模型 | 🎯物种基因进化关系网络算法 | 🎯电路噪声低功耗容错解码算法
📜和-积消息传递算法用例
📜MATLAB激光通信和-积消息传递算法(Python图形模型算法)模拟调制
🍪语言内容分比
🍇Python贝叶斯网络消息传递算法
首先,假设我们有一个多叉树,它是没有循环的图。例如,我们有 4 个变量“下雨”、“洒水器”、“福尔摩斯”和“华生”,有向边分别为“下雨”到“福尔摩斯”、“下雨”到“华生”和“洒水器”到“福尔摩斯”。贝叶斯网络模拟了福尔摩斯和华生是邻居的故事。一天早上,福尔摩斯走出家门,发现草地湿了。要么是下雨了,要么是他忘了关洒水器。于是他去找邻居华生,看看他的草地是否也湿了。当他看到草地确实湿了时,他很确定他没有忘了洒水器,而是下雨了。因此,信息从华生流向洒水器。这种信息流由贝叶斯网络中的消息传递算法建模。
可能性包含有关观察的信息,例如,福尔摩斯草地在未观察的情况下的可能性为 1(湿)和 1(不湿)。如果观察到湿草,可能性变为 1(湿)和 0(不湿)。这些单位向量未归一化。
L
(
X
)
=
∏
K
λ
(
K
→
X
)
L(X)=\prod_K \lambda_{(K \rightarrow X)}
L(X)=K∏λ(K→X)
似然函数基本上是变量子级发送的所有传入消息的乘积。它返回一个似然向量,其中包含变量每个可能值的似然值。对于“下雨”,它的基数为 2,代表“是”和“否”两种状态。
如果某个变量没有子节点(因为它是图中的叶节点且未被观察到),则其似然向量将是一个单位向量,其所有可能值均为 1,例如,由于我们一开始没有观察到福尔摩斯的草,因此我们将其似然向量分别设置为 [1, 1],代表“不湿”和“湿”。
Python伪码表示:
def likelihood(self):
incoming_children_messages = np.array([
c.message_to_parent(self) for c in self.children
])
return incoming_children_messages.prod(axis=0)
先验是某些事件在开始时就已经知道的概率,例如,下雨的概率为 20%。如果先验未知,则使用以下公式进行计算。先验会给出相应变量的无条件概率。因此,我们还需要包括条件概率。
π
(
X
)
=
∑
W
P
(
X
∣
W
)
∏
K
ϕ
(
K
→
X
)
\pi(X)=\sum_W P(X \mid W) \prod_K \phi_{(K \rightarrow X)}
π(X)=W∑P(X∣W)K∏ϕ(K→X)
我们的例子中还给出了条件概率。公式中的“P(X|W)”对应于此。此外,我们需要使用来自所有父方的传入消息,即公式中的“ϕ”。索引显示消息方向 - 从父“K”到当前变量“X”。使用这两个部分(条件概率和来自父的消息)是因为它们都提供有关变量概率的信息。一方面,我们看到给定某些父值的概率,另一方面我们看到这些父的消息。如果没有观察,这些信息对应于父的先验。因此,这里计算“X”的边际并摆脱条件变量。
Python伪码表示:
def priors(self):
parents_messages = [
p.message_to_child(self) for p in self.parents
]
return reduce(np.dot, [self.m.transpose()]+parents_messages)
信念是我们观察到某些事件后的后验概率。它基本上是可能性和先验的标准化产物。
β
(
X
)
=
α
L
(
X
)
π
(
X
)
\beta(X)=\alpha L(X) \pi(X)
β(X)=αL(X)π(X)
Python伪码表示:
def belief(self):
unnormalized = self.likelihood() * self.priors()
normalized = unnormalized/unnormalized.sum()
return normalized
为了计算变量的可能性,我们需要考虑来自变量子项的所有传入消息,这些消息由似然函数中的 lambda 表示。
λ
(
X
→
K
)
=
∑
x
∈
X
L
(
x
)
∑
k
∈
K
;
k
∈
u
P
(
x
∣
u
)
∏
i
≠
k
ϕ
(
Y
→
X
)
i
\lambda_{(X \rightarrow K)}=\sum_{x \in X} L(x) \sum_{k \in K ; k \in u} P(x \mid u) \prod_{i \neq k} \phi_{(Y \rightarrow X) i}
λ(X→K)=x∈X∑L(x)k∈K;k∈u∑P(x∣u)i=k∏ϕ(Y→X)i
这个公式相当混乱,但看一些 Python 代码会更容易理解。一般来说,我们从 P(X|U) 中边缘化 K,而 X 是发送者(子节点),K 是接收者(父节点),U 是 X 的所有父节点,包括 K。如果我们想象一个 X 的条件概率表,对于每个条目,我们取父节点的相应激活,并将相应的传入消息 ϕ 乘以不包含 K 本身的数值。然后,我们将该值乘以 X 的似然值。最后,我们将所有 K 值相同的值相加,剩下向量是从 X 到 K 的消息。
Python伪码表示:
def message_to_parent(self, parent):
likelihood = self.likelihood()
parents_priors = np.array([
p.message_to_child(self)
for p in self.parents if p != parent
])
parent_i = self.parents.index(parent)
stack = np.vstack([
np.dot(
self.m.take(r, axis=parent_i).transpose(),
parents_priors.prod(axis=0)
)
for r in range(parent.cardinality)
])
return np.dot(stack, likelihood)
计算父方发送给子方的消息有两种方法。要么将从其他子方收到的所有消息与当前节点的先验相乘,要么将当前节点的信念除以相应子方发送给父方的消息。
κ
(
X
→
K
)
=
α
∏
C
\
K
λ
(
C
→
X
)
π
(
X
)
=
α
β
(
X
)
λ
(
K
→
X
)
\kappa_{(X \rightarrow K)}=\alpha \prod_{C \backslash K} \lambda_{(C \rightarrow X)} \pi(X)=\alpha \frac{\beta(X)}{\lambda_{(K \rightarrow X)}}
κ(X→K)=αC\K∏λ(C→X)π(X)=αλ(K→X)β(X)
我们认为这个公式称为 Kappa,其索引告诉我们消息的方向(从 X 到 K)。
如果我们看一下信念公式,就会发现这个公式是似然值和先验值的乘积。然而,似然值是所有传入消息的乘积。因此,信念除以来自 K 的传入消息,结果是所有传入消息(除了我们除以的消息)与先验值的乘积。这样,我们就可以解释两种计算 Kappa 的方法之间的相等性。给子方发送消息背后的直觉与给父方发送消息类似。您要考虑所有传入消息,然后将聚合发送到下一个节点。
Python伪码表示:
def message_to_child(self, child):
children_messages = []
for c in self.children:
if c != child:
children_messages.append(c.message_to_parent(self))
if len(children_messages) > 0:
unnormalized = (children_messages * self.get_priors())
unnormalized = unnormalized.prod(axis=0)
message = unnormalized/unnormalized.sum()
return message
return self.get_priors()