Graphical Probability Model

그래프 확률모형

여러 확률변수의 결합확률분포를 구해야 하는 경우를 생각하자. 예를 들어 A, B, C 3개의 확률변수가 있고, 각 확률변수는 모두 0, 1, 2 세 가지의 값만 가질 수 있는 범주형 확률변수이다. 이ㅍ때 A, B, C 의 결합확률분포는 다음과 같이 나타낼 수 있다. 이 때 우리가 알아야 하는 모수는 총 $3^3 -1 = 26$ 개다.

A B C P(A, B, C)
0 0 0 P(A=0,B=0,C=0)
0 0 1 P(A=0,B=0,C=1)
0 0 2 P(A=0,B=0,C=2)
0 1 0 P(A=0,B=1,C=0)
0 1 1 P(A=0,B=1,C=1)
0 1 2 P(A=0,B=1,C=2)
0 2 0 P(A=0,B=2,C=0)
0 2 1 P(A=0,B=2,C=1)
0 2 2 P(A=0,B=2,C=2)
1 0 0 P(A=1,B=0,C=0)
1 0 1 P(A=1,B=0,C=1)
1 0 2 P(A=1,B=0,C=2)
1 1 0 P(A=1,B=1,C=0)
1 1 1 P(A=1,B=1,C=1)
1 1 2 P(A=1,B=1,C=2)
1 2 0 P(A=1,B=2,C=0)
1 2 1 P(A=1,B=2,C=1)
1 2 2 P(A=1,B=2,C=2)
2 0 0 P(A=2,B=0,C=0)
2 0 1 P(A=2,B=0,C=1)
2 0 2 P(A=2,B=0,C=2)
2 1 0 P(A=2,B=1,C=0)
2 1 1 P(A=2,B=1,C=1)
2 1 2 P(A=2,B=1,C=2)
2 2 0 P(A=2,B=2,C=0)
2 2 1 P(A=2,B=2,C=1)
2 2 2 P(A=2,B=2,C=2)

베이지안 네트워크 모형

그런데 현실에서는 모든 확률변수가 서로 영향을 미치는 복잡한 경우보다, 특정한 몇 개의 확률분포들이 서로 영향을 미치는 경우가 많다. 예를 들어 확률변수 A, B, C가 각각 어떤 학생의

  • A : 건강 상태
  • B : 공부 시간
  • C : 시험 성적

을 나타낸 것이라고 하자. A, B, C는 $\{0, 1, 2\}$ 값을 가질 수 있고, 각각 하, 중 상을 의미한다.

건강상태 A는 공부시간 B에 영향을 미치고, 공부시간 B는 시험 성적 C에 영향을 미친다고 볼 수 있다. 하지만 건강 상태 A는 C와는 직접적인 관계가 없다. 이렇게 다수의 확률변수 중 특정 확률변수끼리 가지는 관계를 그래프로 표현한 것을 그래프 확률모형(Graphical Probability model)이라고 하고, 그래프 확률모형 중에서도 이렇게 인과관계가 확실하여 방향성 그래프로 나타낼 수 있는 것을 베이지안 네트워크 모형(Bayesian Network model)이라고 한다. A, B, C를 베이지안 네트워크모형으로 그리면 다음과 같다.

1
2
3
4
5
6
7
8
9
10
11
import networkx as nx
from IPython.core.display import Image
from networkx.drawing.nx_pydot import to_pydot

g1 = nx.DiGraph()
g1.add_path(["A", "B", "C"])
d1 = to_pydot(g1)
d1.set_dpi(300)
d1.set_rankdir("LR")
d1.set_margin(0.2)
Image(d1.create_png(), width=600)

이 그래프는 사이클(혹은 루프)이 없는 방향성 그래프, DAG(Directed Acyclic Graph) 모형에 해당한다. 방향성 그래프 모델에서는 원인과 결과가 되는 두 확률변수의 관계를 조건부 확률분포로 표현한다. 위 모델의 경우 A와 B의 관계를 $P(B|A)$ 로 나타내고, B와 C의 관계를 $P(C|B)$ 로 나타낸다.

그리고 전체 확률변수들간의 관계는 이러한 조건부 확률분포를 결합하여 아래와 같이 나타낸다.

B의 분포는 A의 값에 따라 달라지고, C의 분포는 B의 값에 따라 달라진다. 다만 유의할 점은, A와 C 사이에 직접적인 인과관계는 없지만 상관관계는 있을 수 있다는 점이다. 예를 들어 A-B, B-C간의 관계가 모두 양의 상관관계이면, A가 커졌을 때 B가 커지고 B가 커지면 C도 커지므로 결국 A와 C 사이에도 양의 상관관계가 성립한다.

결합확률분포를 이루는 요소들을 각각 표로 나타내면 다음과 같다.

A P(A)
A=0 P(A=0)
A=1 P(A=1)
A=2 P(A=2)
B P(B\ A=0) P(B\ A=1) P(B\ A=2)
B=0 P(B=0\ A=0) P(B=0\ A=1) P(B=0\ A=2)
B=1 P(B=1\ A=0) P(B=1\ A=1) P(B=1\ A=2)
B=2 P(B=2\ A=0) P(B=2\ A=1) P(B=2\ A=2)
C P(C\ B=0) P(C\ B=1) P(C\ B=2)
C=0 P(C=0\ B=0) P(C=0\ B=1) P(C=0\ B=2)
C=1 P(C=1\ B=0) P(C=1\ B=1) P(C=1\ B=2)
C=2 P(C=2\ B=0) P(C=2\ B=1) P(C=2\ B=2)

이 경우 우리가 알아야 하는 모수의 수는 총 14개로, 조건부확률을 사용하지 않았을 때보다 크게 감소한다.

pgmpy 패키지를 이용해 앞의 예제를 파이썬으로 구현해보자. 조건부확률 $P(A), P(B|A), P(C|B)$ 는 TabularCPD 클래스로 다음처럼 구현할 수 있다.

우선 A의 확률분포는 $P(A=0)=0.1, P(A=1)=0.6, P(A=2)=0.3$ 라고 하자.

1
2
3
4
from pgmpy.factors.discrete import TabularCPD

P_A = TabularCPD('A', 3, [[0.1, 0.6, 0.3]])
print(P_A)

이제 $P(B|A)$를 시각화해보자. 건강 상태가 나쁘면(A=0), 공부시간이 적거나(B=0), 보통이거나(B=1), 많을(B=2) 확률은 각각 50%, 30%, 20%라고 하자. 건강 상태가 보통이면(A=1), 공부시간이 적거나(B=0), 보통이거나(B=1), 많을(B=2) 확률은 각각 20%, 60%, 20%이다. 건강 상태가 좋으면(A=2), 공부시간이 적거나(B=0), 보통이거나(B=1), 많을(B=2) 확률은 각각 20%, 30%, 50%이다.

1
2
3
4
P_B_I_A = TabularCPD('B', 3, 
np.array([[0.5, 0.3, 0.3], [0.3, 0.6, 0.2], [0.2, 0.1, 0.5]]),
evidence=['A'], evidence_card=[3])
print(P_B_I_A)

마지막으로 $P(C|B)$를 시각화해보자. 공부시간이 적으면(B=0), 성적이 나쁘거나(C=0), 보통이거나(C=1), 좋을(C=2) 확률은 각각 80%, 10%, 10%이다. 공부시간이 보통이면(B=1), 성적이 나쁘거나(C=0), 보통이거나(C=1), 좋을(C=2) 확률은 각각 10%, 80%, 10%이다. 공부시간이 많으면(B=2), 성적이 나쁘거나(C=0), 보통이거나(C=1), 좋을(C=2) 확률은 각각 10%, 10%, 80%이다.

1
2
3
4
P_C_I_B = TabularCPD('C', 3, 
np.array([[0.8, 0.1, 0.1], [0.1, 0.8, 0.1], [0.1, 0.1, 0.8]]),
evidence=['B'], evidence_card=[3])
print(P_C_I_B)

이 조건부 확률들을 결합하여 하나의 베이지안 네트워크로 만들려면 BayesianModel 클래스를 사용한다. 생성자에는 노드를 연결한 그래프 정보를 넣고 add_cpds 메서드로 조건부확률을 추가할 수 있다. graphviz와 pydot을 이용해 네트워크모형을 시각화까지 해보면 아래와 같은 그래프가 나온다. 위에서 설정한 조건부확률분포가 모두 담겨있는 그래프이다.

1
2
3
4
5
6
7
8
9
10
11
12
from pgmpy.models import BayesianModel
from IPython.core.display import Image
from networkx.drawing.nx_pydot import to_pydot

model = BayesianModel([('A', 'B'), ('B', 'C')])
model.add_cpds(P_A, P_B_I_A, P_C_I_B)

d = to_pydot(model)
d.set_dpi(300)
d.set_margin(0.2)
d.set_rankdir("LR")
Image(d.create_png(), width=600)

이제 이 모형으로부터 여러가지 추론을 할 수 있다. 예를 들어 전체 결합확률분포함수를 찾고 그 함수로부터 A, B, C의 marginal 확률분포를 계산하면 A, B, C 의 어떤 값이 가장 확률이 높은지 알 수 있다. VariableElimination 클래스를 이용해 분석해보면 여기서 시험성적이 가장 좋을 확률, 그러니까 $P(C=2)$는 26.1%이다.

1
2
3
4
from pgmpy.inference import VariableElimination
inference = VariableElimination(model)
result = inference.query(variables=["C"])
print(result["C"])

베이지안 네트워크의 결합확률분포

베이지안 네트워크 모형을 만들기 위해서는 대상이 되는 확률변수를 노드로 생성하고, 인과관계가 있는 노드끼리 방향성이 있는 간선으로 연결해 그래프를 만들어주어야 한다. 이렇게 네트워크가 생성되면 확률변수들의 결합확률분포가 다음처럼 주어진다.

여기서 $Pa(X_i)$ 는 $X_i$ 의 부모노드이다.

예를 들어 $X_1, \cdots, X_6$ 의 관계가 위 그래프와 같다면 이 확률변수들의 결합확률분포는 다음과 같다.

조건부 독립

베이지안 네트워크를 만들 때 중요한 것은 확률변수간의 조건부독립 관계가 그래프에 나타나있어야 한다는 점이다.

조건부 독립(conditional independence)은 일반적인 독립과 달리 조건이 되는 확률변수가 존재해야 한다. 일반적으로 A, B 가 독립이 되는 정의는 $P(A, B)=P(A)P(B)$ 지만, 조건부 독립은 C라는 확률변수가 조건이 되어 아래와 같이 정의된다.

즉, C에 대한 조건부 결합확률분포가 조건부확률분포의 곱으로 나타난다.

A, B가 C에 대해 조건부 독립이면 다음 식도 만족한다.

주의할 점은 조건부 독립과 (무조건부)독립은 다르고, 서로 관계가 없다는 점이다. 즉, 두 확률변수가 독립이라고 해서 항상 조건부 독립이 되는 것도 아니고, 조건부 독립이라고 해서 꼭 독립이 되는 것도 아니다.

방향성 분리

방향성 분리(d-separation, directed separation) 정리는 방향성 그래프 모형에서 어떤 두 노드(확률변수)가 조건부 독립인지 아닌지 알아보는 방법이다. 다음과 같은 세 가지 간선(edge)결합을 알아야 한다.

  • 꼬리-꼬리 결합
  • 머리-꼬리 결합
  • 머리-머리 결합

1) 꼬리-꼬리 결합

우선 확률변수 A, B가 공통의 부모 C를 가지는 경우를 보자. 이 때 C에는 간선(화살표)의 꼬리가 두 개 붙어있게 되므로 C는 꼬리-꼬리(tail-to-tail) 결합이다.

이 때 A와 B는 독립은 아니지만 조건부 독립이다.

이런 상태를 “C가 A와 B 사이를 막고 있다(block)”고 한다.

2) 머리-꼬리 결합

다음으로는 인과관계인 확률변수 A와 B 사이에 C가 끼어있는 경우이다. 이 때 노드 C의 왼쪽에는 간선의 머리가, 오른쪽에는 간선의 꼬리가 붙어있기 때문에 머리-꼬리(head-to-tail)결합이라고 한다.

이 경우에도 마찬가지로 A와 B는 독립은 아니지만 조건부 독립이 성립하며, C가 A와 B 사이를 막고 있는 경우이다.

3) 머리-머리 결합

마지막으로 두 확률변수 A, B를 부모로 갖는 노드 C가 있다고 하자. 이러한 구조는 V-구조라고도 하며 C에 붙는 두 간선 모두 머리가 오기 때문에 머리-머리(head-to-head) 결합이라고 한다.

이 경우는 앞의 두 경우와 달리 A와 B가 (무조건부)독립이다.

하지만 A와 B는 조건부 독립은 아니다. 예를 들어 A가 늦잠을 자는 것을 나타내는 확률변수, B가 길이 막히는 것을 나타내는 확률변수, C가 지각하는 것을 나타내는 확률변수라고 할 때, 늦잠을 자는 것과 길이 막히는 것은 서로 독립이다. 그런데 일단 지각이 발생한 상황에서는 A와 B는 서로 독립이 아니며, 이 경우에는 반-상관관계를 갖는다. 즉, 늦잠을 자지 않았다면 길이 막혔을 가능성이 높아지고, 길이 막히지 않았다면 늦잠을 잤을 확률이 높아진다. 이러한 것을 explaining-out이라고 한다. 이는 C가 A,B의 바로 아래 자식이 아니라 아래 그림처럼 D를 거친 후손(descendent)인 경우에도 성립한다.

위 상황들을 정리한 것이 방향성분리(d-separation) 정리 이다. 이 정리에 따르면 A와 B가 C에 대해서 조건부 독립인 경우는 다음 조건이 만족될 때이다.

  1. C가 A, B 사이의 경로에 있는 꼬리-꼬리 혹은 머리-꼬리 결합이다.
  2. C가 A, B 사이의 경로에 있는 머리-머리 결합 혹은 그 자손이 아니어야 한다.

마코프 네트워크

확률변수간의 인과관계가 순환(cycle)관계를 이루어서 방향성이 있는 베이지안네트워크로 구현할 수 없는 경우도 있다. 이 때는 무방향성 그래프인 마코프 네트워크(Markov network)를 사용한다. 3 x 3 이미지의 경우를 예로 들 수 있다.

마코프 네트워크는 클리크(clique)로 구성되는데, 클리크를 구성하는 확률변수의 분포는 포텐셜 함수(potential function) 혹은 팩터(factor)로 나타낼 수 있다. 팩터는 결합확률분포에 양의 상수를 곱한 함수로, 결합확률분포에 비례하지만 모든 확률을 더해서 1이 되어야 한다는 조건이 빠진다.

마코프 네트워크의 확률분포

마코프 네트워크의 결합확률분포는 마코프 네트워크를 구성하는 모든 클리크의 팩터의 곱으로 나타난다.

  • $C$ : 클리크
  • $X_C$ : 클리크 안의 확률변수
  • $\psi_C$ : 클리크의 팩터함수
  • $\{C\}$ : 모든 클리크의 집합
  • $Z$ : 파티션 함수

위 그래프의 경우 9개의 확률변수의 결합확률분포를 다음처럼 나타낼 수 있다.

에너지 함수

팩터함수는 다음과 같은 형태로 표시할 수 있다.

이 식에서 $E(X)$ 를 에너지함수(energe function) 라고 한다. X의 확률이 높을수록 에너지함수의 값은 작아진다.

예를 들어 0, 1만을 값으로 갖는 베르누이 확률변수 $X_1, X_2$ 가 다음과 같은 에너지함수로 표현되는 경우,

팩터함수의 값을 구하면 다음과 같다.

여기서 알 수 있는 점은 $X_1,X_2$ 둘 다 같은 값을 가질 확률은 서로 다른 값을 가질 확률에 비해 높다는 것이다. 즉, 서로 양의 상관관계를 갖는다.

pgmpy의 DiscreteFactor 클래스를 이용해 위 그래프의 팩터함수를 구현할 수 있다. 예를 들어 의 팩터함수가

$X_{12} = 0$ $X_{12} = 1$
$X_{11}=0$ 10 1
$X_{11}=1$ 1 10

이면 다음처럼 구현한다.

1
2
3
4
5
from pgmpy.factors.discrete import DiscreteFactor

factor = DiscreteFactor(['X11', 'X12'], cardinality=[2, 2],
values=[[10, 1], [1, 10]])
model.add_factors(factor)

이미지 완성

마코프 네트워크의 예로 다음과 같은 두 종류의 5 x 5 이미지 데이터가 있다고 하자.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
n_char = 2
images = np.zeros((n_char, 5, 5))
idx = []
idx.append(np.array([
(0, 1), (0, 2), (0, 3), (1, 0), (1, 4), (2, 0), (2, 2), (2, 4), (3, 0), (3, 4), (4, 1), (4, 2), (4, 3),
]))
idx.append(np.array([
(0, 0), (0, 4), (1, 1), (1, 3), (2, 2), (3, 1), (3, 3), (4, 0), (4, 4),
]))

for k, idx in enumerate(idx):
for i, j in idx:
images[k, i, j] = 1

plt.figure(figsize=(6, 2))
for i in range(n_char):
plt.subplot(1, n_char, i + 1)
plt.imshow(images[i], cmap=plt.cm.bone_r)
plt.grid(False)
plt.xticks(())
plt.yticks(())
plt.title(i)

이 두 네트워크모형의 팩터함수를 다음과 같이 학습시킨다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from pgmpy.models import MarkovModel
from pgmpy.factors.discrete import DiscreteFactor

def get_factor(v1, v2, idx1, idx2):
p00 = p01 = p10 = p11 = 0
for k in range(num_images):
if images[k, idx1[0], idx1[1]] == 0 and images[k, idx2[0], idx2[1]] == 0:
p00 += 1
if images[k, idx1[0], idx1[1]] == 0 and images[k, idx2[0], idx2[1]] == 1:
p01 += 1
if images[k, idx1[0], idx1[1]] == 1 and images[k, idx2[0], idx2[1]] == 0:
p10 += 1
if images[k, idx1[0], idx1[1]] == 1 and images[k, idx2[0], idx2[1]] == 1:
p11 += 1
factor = DiscreteFactor([v1, v2], cardinality=[2, 2],
values=[[p00, p01], [p10, p11]])
return factor

model = MarkovModel()

$X_{11}$ 과 $X_{12}$ 의 결합확률 팩터를 보면 다음과 같다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
num_images = images.shape[0]
n1 = images.shape[1]
n2 = images.shape[2]
for i in range(n1):
for j in range(n2):
if j < n2 - 1:
v1 = "X{}{}".format(i + 1, (j + 1))
v2 = "X{}{}".format(i + 1, (j + 2))
model.add_edge(v1, v2)
factor = get_factor(v1, v2, (i, j), (i, j + 1))
model.add_factors(factor)
if i < n1 - 1:
v1 = "X{}{}".format(i + 1, (j + 1))
v2 = "X{}{}".format(i + 2, (j + 1))
model.add_edge(v1, v2)
factor = get_factor(v1, v2, (i, j), (i + 1, j))
model.add_factors(factor)

f = model.get_factors()[0]
print(f)

Share