[Rust] Ownership, Scope, Transfer Ownership 과 Borrowing

Scope, Ownership, Transfer

Rust에서 사용하는 영역, 소유, 소유권 이전, 복사, 빌림을 요약해 보는 것이 목적이다. 소유(Ownership)은 Java, Go와 같은 백그라운드에서 실행하는 garbage collector가 없다는 것이다.

Rust Ownership의 주요 키워드

  • Move: Trasnferring Ownership
  • Copy
  • Borrowing
  • Mutable Borrowing

Scope와 Ownership, Lifetime

  • 문맥에서 소유와 빌림(borrowing)은 다름을 명심하다.
  • 모든 변수는 반듯이 하나 소유해야 한다. 2개, 3개도 불허한다
    • 영역의 정의
      • 시작은 '{' (대괄호) 시작한다.
      • 끝은 '}' 또는 return 위치이다.
  • 소유 영역에서 벗어나면 파기된다.
  • lifetime은 변수가 생성되고, 삭제되는 기간을 이야기 한다.
  • 소유를 다른 영역으로 전송할 수 있다.

소유권 이전(Transferring Ownership)

  • 한 영역의 변수의 소유권을 다른 영역으로 이전(moving)하는 것이다.

복사(Copying)

  • 원시 타입같은 경우는 소유권 이전보다 복사를 한다.
    • 예) interger, floating-point, bytes(스택의) 
  • Ownership에 의존하지 않는 타입을 Copy trait이 있다.

빌림(Borrowing)

  • 만약 데이터를 이동(moving)한다면, 소유권이 새로운 변수로 바뀐다. 이 것은 다른말로 새로이 받은 변수가 영역이 삭제될 경우, 메모리상의 데이터가 삭제가 된다.
  • 소유권은 이전하지 않고, 값을 참조할 때 사용한다.
    • 참조 형태: 
      • mutable: 
        • 빌림 변수의 영역이 끝나면, 소유한 영역의 변수 값이 바뀔 수 있다.
        • mutable borrowing이 되어 살아 있다면, 다른 영역에서 immutable borrowing을 생성 할 수 없다.
      • immutable(기본): 일기 전용 변수이다.
    • 표현은 '&'를 같이 정의한다
  • 컴파일러는 모든 빌림 변수에 lifetime을 추적한다.

Rust에서 Rc와 Box

박스 변수

개발자는 변수를 스택(stack)에 할당할지, 힙(heap)에 할당할지 변수 타입을 먼저 결정 해야한다. 본 내용은 힙을 사용하는 경우이다. 

Rc<T>, Box<T>는 다음과 같다:

  • 공통
    • 힙 사용: 실제값이 저장되는 공간이다.
    • 힙 포인터 사용(usize): 힙 공간을 참조하는 변수가 스택 공간에 할당된다.
    • 재귀적 정의타입이여서 컴파일 시간에 크기를 알 수 없을 때 사용한다. 예) List
  • 차이점:
    • 데이터 소유권 개수
      • Box<T>는 1개
      • Rc<T>는 여러개
  • Box<T>: 
    • 변수 영역이 벗어나면 자동 해제가 된다. 컴파일 시간에 변수의 생명주기가 계산 가능하다.
    • 힙 내부 데이터 수정이 가능하다.
  • Rc<T>: 
    • 불변 참조(immutable reference): 
      • 힙 내부의 값을 수정할 수는 없다.
    • 여러 영역에서 소유권을 가질 수 있다:
      • 소유권이 동적으로 공유한다. 내부적으로 참조 횟수를 세고 있어서, 0이되면 할당된 메모리가 해제가 된다.
    • 순환 참조를 조심해야 한다. 대안으로 Weak<T>를 쓸 수 있다.

스택

부연하면, 스택을 사용할 경우, 짧은 생의 작은 크기를 보통 사용하기 위해서 활용한다. 그리고 FILO(first-in, last-out)의 특성을 가진다. 스택에 변수를 조작하면, 사용하는 변수의 크기를 이미 알 고 있어야 한다. 스택에 있는 변수를 함수의 인자로 넘길경우, 보통 값으로 주고 받는다. 즉, 스택에 값을 복제한다. 

큰 크기를 공간을 할당할때 사용하고, 순서가 없다. 또한 힙의 접근은 주소를 사용해서 한다. 보통 이 주소를 가지고 있는 변수를 포인터라고 한다. 하지만, 힙에 접근하기위한 포인터(레퍼런스)는 스택에 저장된다. 그러므로 컴파일 시간에 포인터 변수 자체의 크기는 알 수 있다. 다만, 힙에 저장된 데이터의 크기는 런타임 시간에 알 수 밖에 없다.

Generative Model을 왜 할까?

Generative Model의 요약

- generative model: joint probability distribution, $p_(x)$ 이다. 

- 조건이 있다면, conditional generative model, $p_(x|c)$ 이다.


활용 분야

1. 데이터 생성: 새로운 데이터 샘플을 생성할 때이다. 예를들면, 가상의 얼굴 사진을 만들때 사용한다.

2. Oulier 검출기, 데이터 압축, generative classifier, 모델 비교에 사용한다. 즉, 데이터의 분포를 추정함으로서 발생하는 모든 작업들이다.

3.Missing value 처리, Inpainting: 

    - Missing value: 관측 등의 손실로 일부의 데이터를 관찰하였을 때, 분포에 근거해서, 손실한 부분을 채우는 과정이다. 단순한 랜덤은 현실에 없는 데이터를 생성할 가능성이 있다.

    - In-painting: 영상 내에 가려진 부분을 채워넣는 작업이다. 예를들면, 2d영상으로부터 3d로 복원할 경우, 관측되지 않는 영역을 generative model로 채워 넣는 작업을 한다.

4. Latent 변수 추정 

    - 문서의 주제(latent variable)가 무엇인가? Bayesian Model에서 사용가능 하다. 관측된 데이터로부터 잠재 토픽을 추정하는 것이다. 예를들면, 문서의 토픽을 추정하는 것이다.

    - 두 영상의 latent 변수 2개를 추출하고, 이 중간의 영상은 무엇이 될까? 를 생성할 수 있다. 예를들면, 무표정 얼굴 사진과 여우를 섞은 모습은 어떻게 될 것인가를 할 수 있다.

5. Representation Learning: 수십 문장을 학습하여, 문장을 가장 잘 설명하는 latent 변수(=feature)를 학습하는 분야이다. 향후에 downstream(supervised task)에 사용된다.


관련 키워드

- GAN, AR, VAE(Variational autencoder), EBM, Normalizing flow, Diffusion Model, ELBO

[베이지안] Bayesian 확률에서 Belief 의미란

불확실한 상황에서  최적의 의사 결정 방법

베이지안 학파의 관점에서 나온 어원이다.

의사 결정을 한다는 것은 다음의 상황을 예측을 해야 한다는 것이다. 현재 관측된 데이터를 근거로 어떤 사건의 발생 확률(=불확실성)을 추정할 수 있다. 우리는 현재 추정된 확률을 믿을 수 밖에 없고, 그 믿음하에서 미래의 사건 예측하여 최적의 의사 결정을 해야 한다.

예를들면 동전 던지기 상황을 고려해보자. 동전의 앞과 뒤가 나올 확률이 반반이다. 다음 동전의 앞면이 나올 확률은 어떻게 될 것인가? 나는 다음의 시도에서도 동전은 앞과 뒤가 반반이 나올 확률을 믿어! 라고 할 것이다. 이때 믿음이 생겨야만 한다.

그래서 Belief이다.


[R] summarise에서 .groups의 인자 역할 - You can override using the `.groups` argument.

 


다음의 2개의 코드 블럭 차이를 보면 된다. R 스크립트에서 group_by()를 하다보면 다음의 에러를 자주 마주친다
"You can override using the `.groups` argument."
결과적으로는 group_by()의 상태를 summarize이후에 유지를 할 것인지, 삭제할 것인지에 옵션이다.

mtcars %>% 
    group_by(cyl, am) %>% 
    summarise(avg_mpg = mean(mpg))
 
`summarise()` has grouped output by 'cyl'. You can override using the `.groups` argument.
# A tibble: 6 × 3
# Groups:   cyl [3]
    cyl    am avg_mpg
  <dbl> <dbl>   <dbl>
1     4     0    22.9
2     4     1    28.1
3     6     0    19.1
4     6     1    20.6
5     8     0    15.0
6     8     1    15.4

summarise이후에 또 다른 summarise을 할 경우에 잘 발생한다. cyl과 am의 상태가 계속 유지가 되고 있다. 아래의 샘플은 첫번째 summarise()에서 .groups에 drop_last를 사용하였다. 이 옵션은 마지막 group 변수인 "am"만을 삭제한다. 그리고 두 번째 summarise()를 한 결과 cyl로만 묶어서 최소값을 찾은 결과이다.
mtcars %>% 
    group_by(cyl, am) %>% 
    summarise(avg_mpg = mean(mpg), .groups = "drop_last") %>% 
    summarise(min_avg_mpg = min(avg_mpg))
 
# A tibble: 3 × 2
    cyl min_avg_mpg
  <dbl>       <dbl>
1     4        22.9
2     6        19.1
3     8        15.0

Variational Inference에서 ELBO설명

Variational Inference에서 ELBO란?

Variational Inference(VI)

Variational Inference(이하, VI)에서 inference는 모델의 예측 과정을 이야기 하는 것이 아니다. VI에서 모델의 인자를 찾는 과정이다. 관측한 데이터로부터 사후 확률(Posterior, $p(\theta|D) $) 분포를 직접적으로 계산하고 싶을 때 VI기법을 사용한다. 보통은 사후 확률 분포를 직접 계산하지 않고 풀 수있는 분야도 있다. 또한 사후 분포 확률은 계산하기 어려운 확률로 알려져있다. VI는 이 문제를 극복하기 위하여, 데이터와 비슷하지만 다루기쉬운 확률 분포도를 도입한다. 비록 딱 맞지는 않지만 쉬운 확률 분포로 관측 데이터($D$)를 근사하여, 최적의 인자를 계산하는 기법이 VI이다. 이에 반하여, MAP(Maximum a Posterior)는 관찰 데이터 $D$로부터 사후 확률이 최대화되는 지점의 $\theta$인자를 구하는 것입니다. 즉, 데이터를 가장 잘 설명하는 모델의 인자를 하나 찾는 것입니다. Bayesian 관점에서 필요한 진정한 사후 확률과 가장 비슷한 $\theta$의 분포를 구하는 것과는 차이가 있습니다(그림 1 참조). 한 점인가? 분포도 인가?로 볼 수 있을 듯 하다. 

$\theta$의 잠재 변수의 분포도를 가지고 있다는 것은, 많은 장점이 있다. 예를들면 데이터를 현실성있게 생성할 수 있거나, 손실 데이터를 채울 수 있다. 그리고 잠재 공간에서 두 점의 어느 중간 지점의 데이터를 예측해 볼 수 있다. 또한 비정상 탐지 또는 불확실성 예측도 가능할 수 있다.

보통 VI기반하여 모델로 예측된 결과가 큰 관점에서는 맞지만, 세밀한 부분에서 뭉개지는 경향이 발생할 수 있다. 근사화의 한계가 있는 것으로보아서 항상 만능 키는 없는 듯 하다. 그러나 이 VI 기법도 모든 가능성이 순식간의 계산 가능한 컴퓨터가 발견되면 쉽게 해결될 지도 모른다. 정보가 부족하거나 계산 기법이 느릴 수록 여러가지 가정을 도입한다. 그리고 더욱더 증명 과정으로 코드구현이 복잡해진다. 이에 반하여 가정으로부터 디테일을 손실하는 것이 발생한다. 하지만 단순하지만 충분한 데이터와 강력한 계산 파워가 있다면, 이러한 가정은 가능한 사라지고, 디테일도 살려내는 추세인 것은 확실해 보인다.

Bayesian Theorem의 어려운 점

왜? posterior 추정이 어려운지 이해가되면, VI의 필요성도 이해가 될 것 같다. 먼저 Bayesian theorem의 문제를 살펴보자.

Marginal Probability, Evidence (Bayesian Theorem)

잠재변수 $Z$를 소거하여, $p(X)$를 설명한다. 보통은 잠재 변수($Z$)를 도입하여, 관찰된 데이터($X$)를 설명하려 할때 사용한다. 보통 Bayesian theorem에서는 관찰 데이터의 marignal probabilty density, $p(X)$를 evidence라고 한다. 추가적으로 $p(z)$는 piror라고 한다.

$$p(X) = \sum_{z \in Z}{p(X,z)} = \sum_{z \in Z }{p(X|z)p(z)} $$

Bayesian Theorem에서 evidence $p(X)$계산이 어려운 점

Evidence에서 모델의 의존적인 적분항이 보통 문제가 발생한다. 직접적인 계산이 불가능하거나, 지수적인 계산시간이 필요하다. 이 문제를 극복하기 위해서 VI는 근사할 수 있는 효과적인 방법을 제공한다. VI는 marginal probability density(또는 evidence)의 lowr bound를 잘 관찰하여 문제를 해결하였다. Posterior를 계산할때 evidence의 어려운 점을 Bayesian 공식($\ref{bayesian}$)과 연결하여 자세히 설명해 보자.

$$\begin{aligned} p(Z|X) &= \frac{ p(X|Z)p(Z) } {p(X)} \\  &= \frac{ p(X|Z)p(Z) } {\sum_{z \in Z }{p(X|z)p(z)}}  \end{aligned} \label{bayesian}$$

결론적으로 사후확률(posterior probability, $p(Z|X)$ = likelihood * prior / partition function)를 잘 계산하기 위해서는 $\ref{bayesian}$에서 분모의 합(적분)형태(partition function)를 풀어야 한다. 하지만, 이것은 계산하기가 힘들다. 이유는 잠재변수 $Z=(z_1, z_2, z_3, \cdots )$와 연계하여 실제 관측 대상의 확률 분포를 계산하기가 어렵기 때문이다(적어도 나의 연구 기간내에서...). 그래서 일부만 자료를 사용하는 샘플링 기법을 사용해서 얻으려고 한다(Monte Carlo). 하지만, 만약 잠재 변수($z$)가 기하 급수적으로 크다면, 샘플링 기법도 비효율적이게 된다. 샘플링의 헛 발이 많다. 그러나 미래에는 양자컴퓨팅 기술이 이러한 문제를 쉽게 해결을 할 수 있을 수도 있다. 기다려 볼까? 절대 마법의 비법 소스도 없다. 현실적으로, 지금은 못하니 다른 대안을 소개해본다. 특히 본 블로그에서는 VI기법으로 해결하는 방법을 소개한다.

자 이제 Generative Model($z \rightarrow x$)을 설계를 해보자! 어떤 어려움이 있을까?

주의하자, 분류기는 관찰된 라벨링 변수($y$)가 있다. 여기서 $z$는 잠재변수를 칭하여, 향후 문맥 이해에 방해가 되어 확실히 분류기에서의 $y$와는 구분을 지어보려 한다.

Marginal Inference를 사용하여 관측 데이터를 설명한다.

관찰을 통한 데이터 분포인 $p(X)$만 덜컥 주어지면, 우리는 더 이상 할 수 있는 선택지가 별로 없다. 그냥 그러한 것이다. 그러나 보통은 우리의 요구사항은 앞으로의 데이터(보지 못한 데이터)까지 예상했으면 한다. 즉, 학습을 통한 예측을 하고 싶을 것이다. 그러기 위해서는 통상적으로 관측데이터로부터 모델링을 하고, 이를 잘 적용되도록 모델을 일반화를 시켜야 한다. 이때 도입하는 방법이 정확한 값을 알 수 없지만 있다고 가정하는 잠재 변수 $z$ (예, 벡터, 지능, 건강, 임베딩 값 등)과 파라미터($\theta$, 입력-잠재변수-출력사이에 연산에 필요한 추가 인자들등)도입이다. 잠재변수는 활용 용도에 따라서 다양하기 때문에, 명확히 규정하기가 힘들 수 있다. 특히 변수가 난무하면 이해가 어려우니, 본 블로그에서는 대부분은 수식에서 $\theta$는 생략하고 $z$와 $\theta$를 통합하여 한 개($z$)로 전개한다. 잠재변수 도입하여 관측 데이터의 분포($p(X)$)를 모델링을 할때 많이 사용하는 기법이 marginal inference(model prediction)기법이다. 즉, 

$$p(X) = \sum_{z \in Z}{p(X,z)} \label{marginal_probability}$$

로 해서 $p(X)$ 확률 분포를 근사할 수 있다. $z$로 $x$를 설명하게 된것이다. 결국 하고자 하는 것은 $z \rightarrow x$로서, 원인과 결과의 관계를 모델링하는 것이다. 

그런데 잠재변수 $z$의 도입하여 해를 해결하려고 하였다. 그러나 그것조차 모델이 복잡하다보니 적어도 나의 인생동안에는 계산이 어렵겠네... ?

모델이 복잡하다는 것은 $z$의 차원이 크거나, $z$사이에 서로 의존도가 복잡하거나, 적분이 어렵거나, 최적화 과정 중에서 어딘가에 계산량이 매우 비싼 경우가 있기 때문이다. 이때 우리는 근사한 접근 방법을 도입하여 효율적으로 문제에대한 해(즉, 모델의 인자)를 찾고 싶어한다. 특히 사후 확률(posterior)는 NP-hard problem으로 알려있다. 이 어려움 때문에 일반적인 최적화 모델에서는 MAP(likelihood * prior), MLE(likelihood probability)와 같은 최대화 기법을 대신 사용한다. 그러나 우리는 진짜 사후 확률($p(Z|D)$ or $p(\theta|D)$)을 얻고 싶은 것이다. MAP, MLE가 아니다. 특히 MAP은 사후 확률을 계산하는 것이 아님을 명심하다. 그래서 MAP, MLE를 사용할 수 없다. 결론적으로, $Z$의 분포 자체를 얻는 VI와 데이터를 가장 잘 설명하는 인자 $z$를 얻는 MAP은 서로 다른 해를 가진다고 할 수 있다(그림 1참고). 
[그림1] 왼쪽: Variational inference의 과정. 오른쪽: MAP과 variational inference의 차이. 여기서 $D$는 관측 데이터 집합이다. $\xi$는 $q$의 분포를 표시하는 variational 인자이다. [참조]
어찌되었든 VI는 사후 확률($p(Z|D)$) 자체를 근사하는 기법이 있다. 기본 원리는 다루기 쉬운 $q(z)$로 posterior proability를 근사 시키자는 것이다. 즉, $q(Z) \approx p(Z|X)$ 되는 $q(Z)$를 잘 찾자. 여기서 $p(Z|X)$를 사후 분포(posterior probability)라고 한다. 즉, 관측 데이터로부터 모델($Z$)을 연결한 것이다. 실제적으로 VI는 global optimization을 찾을 수는 없을 수 있으나, 상당히 실용적이고, 병렬처리와 확장성이 잘 설계된 stochastic gradient optimization를 사용할 수 있다[참조].

다루기 쉽고 계산하기 쉬운 최적의 $q^{*}{(Z)} \approx p(Z|X) $를 찾자!

이 VI 개념을 설명하기 위해서 보통은 $q(Z)$(surrogate)와 $p(Z|X)$라는 두 분포함수를 가지고 이야기 한다. 여기서 $q(Z)$는 다룰 수 있는(계산하기 쉬운) 분포 함수의 $Z$의 분포이다. 보통은 가우시안 모델을 사용한다. 그리고 $p(Z|X)$는 관측 한 데이터($X$)가 주어졌을때 모델의 인자의 $Z$의 분포이다. 즉 두 $Z$의 분포가 교차되는 지점을 잘 찾으면 된다. 

보통의 최적화의 대상은 잠재변수 $Z$를 찾는 것이다. 하지만, VI는 $q(Z)$를 찾는 점이 특이한 점이다. $q(Z)$를 잘 고르면, 관측 데이터를 가장 잘 설명하는 분포와 우리가 다룰 수 있는 분포가 가장 비슷한 시점을 고를 수 있다. 즉, $p(Z|X)$와 $q(Z)$가 가장 오차가 작도록 최적의 $q^{*}(Z)$를 찾으면 된다. 수식으로 표현하면 다음과 같다:

$$q^{*}(Z) = \underset{q(Z) \in Q }{argmin} D_{KL}[q(Z) || p(Z|X)] $$

두 분포의 오차(거리)를 측정하는 기법으로 KL-Divergence, $D_{KL}$를 많이 사용한다. 여기서 최적의 $q^{*}(Z)$를 찾는 것은 VI라고 할 수 있다. 즉, $q^{*}(Z) \approx P(Z|X)$ 가 된다.

보통 $q(Z)$를 variational distribution라고 이야기 한다[https://en.wikipedia.org/wiki/Variational_Bayesian_methods]. 필자의 뇌피셜인데, variational distribution으로 근사하는 $q(Z)$로 모델 추론하기 때문에 variational inference라고 하는 듯 하다.

어떻게 $q^{*}(Z)$를 찾았을까? ELBO 최적화!

KL Divergence, $D_{KL}[q(Z) || p(Z|X)] $ 에서 우리는 참 사후 확률(true posterior probability)인 $$p(Z|X) = \frac{\bar{p}(Z,X)}{p(X)} \label{z_normal}$$의 분포를 모른다(이미 모델을 할때 $z\rightarrow x$라고 가정한다. 그래서 수식 $\ref{z_normal}$로 표현이 가능하다). 더 자세히 말하면, $p(Z|X)$를 구체적으로 값을 평가할 수 없다. 즉 계산을 할 수 없다. 수식 $\ref{z_normal}$에서 보면 특히 문제의 항인 $p(X)$는 실제 데이터의 모든 가능성 있는 빈도의 값을 알아야 한다. 일부 관측된 X의 실체 모양을 알 수 없으니 계산할 수가 없다. 그러나 상수인 것은 확실 할 것이다. 여전히 이거참 난감한 상황이다. 하지만 그동안의 현명한 분들이 다 솔루션을 찾았다. 그 과정을 소개해본다. 자 그럼, 수집한 데이터 $X$가 있기 때문에, 즉 $\bar{p}(Z,X)$을 곰곰히 활용해 보자. 결론적으로 말하면, evidence $p(X)$때문에 KL Divergence를 직접 계산을 피하고, ELBO를 최적화하려는 기법인 VI를 활용한다. ELBO를 최적화 하여, $q^{*}(Z)$를 찾을 수 있다.

$D_{KL}[q|| \color{Blue}p]$와 $D_{KL}[q || \color{Red} \bar{p}]$ 사이의 관계는?

주로 이 사이트를 참조했음을 밝힌다. 가장 납득력이 있고 쉽게 설명하였다. KL Divergence에 $p(Z|X)$대신 $\bar{p}(Z,X)$를 넣고 $p(Z|X)$때와 비교를 분석을 해보는 것이 목적이다. $D_{KL}$은 확률 분포를 인자로 거리를 계산하는 것처럼, 비슷한 수식모형으로 형식으로 관측된 데이터로 거리를 표현하는 $J(q||\bar{p})$를 생각해보자.
$$J(q||\bar{p}) = \sum_{z \in Z}{q(z)\log \frac{q(z)}{\bar{p}(z,X)}} $$ 
여기서 $\bar{p}(z,X)$는 수식 $\ref{z_normal}$에있다.
기계적으로 풀어 보자.
$$\begin{aligned}J(q||\bar{p}) &=&  \sum_{z \in Z}{q(z)\log \frac{q(z)}{\bar{p}(z,X)}} \\ &=& \sum_{z \in Z}{q(z)(\log \frac{q(z)}{p(z|X)}} - \log p(X)) \\ &=& D_{KL}(q||p) - \sum_{z \in Z}{q(z)\log p(X)} \\ &=& D_{KL}(q||p) - \log p(X)\cdot 1\end{aligned}$$
여전히 $p(X)$와 $D_{KL}(q||p)$은 계산 불가이다(원래 모르는 것이다). 그럼에도 불구하고 이제 항을 정리해 놓고, $q$와 $p$와 $\bar{p}$의 관계를 잘 살펴보면 다음과 같다: 
$$\color{Red} \log p(X) = D_{KL}(q||p) - J(q||\bar{p}) \label{ELBO_EQ}$$

ELBO의 명칭 유래

두 분포의 차이를 나타내는 $D_{KL}(q||p) \geq 0$ 이므로, 수식 $\ref{ELBO_EQ}$으로부터 최초 비교해보려던 $q$와 $p$와 $\bar{p}$의 관계는 
$$\log p(X)  \geq - J(q||\bar{p}) = \color{Blue} ELBO \label{LB}$$ 
이다. 여기서 수식 $\ref{LB}$의 $-J(q||\bar{p})$를 variational lower bound 또는 ELBO(evidence lower bound)라고 한다. ELBO라고 붙인 이유는 아무래도 $\ref{LB}$의 부등식때문일 것이다. $\geq$을 기준으로 좌측에는 evidence $p(X)$가 있고, 우측에 하한이 있기 때문이다.

수식 $\ref{ELBO_EQ}$으로부터 ELBO($-J(q||\bar{p})$)와 variational distribution($q(z)$), true posterior probability($p(Z|X)$)의 고찰

$\log p(X)$ 는 상수항으로 고정되있기 때문에, $-J(q||\bar{p})$를 최대화하는 것은 $D_{KL}(q||p)$를 줄이는 것과 같다. 결과적으로 $q$가 ture posterior probability($p$)에 근사하게 되는 것이다. 
[                 $\log p(X)$                ] = -->[ $D_{KL}(q||p)$]<-- + [<-------  $-J(q||\bar{p})=ELBO(q(z))$  ------->]

$ELBO(q(z))$를 최대화로 이끌면, $q(z)$가 최적의 $q^{*}(z)$를 찾아 간다.  결국 $ELBO(q(z))$만 최적화를 하면 $q^{*}(Z)$를 찾을 수 있는 것이다. 특히 $ELBO(q(z))$에는 모두 계산이 가능한 항들만 있다. 

Expectation으로 $ELBO(q(z))$를 표현해보자.

먼저 mpatacchiola.github.io/blog 를 참조하였음을 밝혀둔다. 
$$\begin{aligned}-J(q||\bar{p}) &= -\sum_{z \in Z}{q(z)\log \frac{q(z)}{\bar{p}(z,X)}} \\ &= E_{z \sim q(Z)}[-\log\frac{q(z)}{\bar{p}(z,X)}] \\ &= E_{z \sim q(Z)}[\log{\bar{p}(z,X)} - \log{ q(z)}]\end{aligned}$$ 
$ELBO(q(z))$를 expectation($E_{p(x)}[f(x)]$)관점으로 정리를 하면, 다음과 같다(표현이 축약되어서 이해하기 쉬울 수가 있다): 
$$E_{z \sim q(Z)}[\log{\bar{p}(z,X)} - \log{ q(z)}] = \log p(X) - D_{KL}(q(Z)||p(Z|X)) \label{ELBO_EXP} $$
사실 수식 $\ref{ELBO_EXP}$의 오른쪽 항은 관심이 없다. 이유는 이제 ELBO의 원리를 알았기 때문이다. 최적화 설계할 때는 오른쪽항은 잊어 버리자. 그리고 ELBO만 자세히 살펴보자.
$$\begin{aligned} ELBO(q(z)) &= -J(q(z)||\bar{p}(z,X))) \\ &= E_{z \sim q(Z)}[\log{\bar{p}(z,X)} - \log{ q(z)}] \\ &= E_{z \sim q(Z)}[\log{\bar{p}(z,X)}] - E_{z \sim q(Z)}[\log{ q(z)}] \\ &= E_{z \sim q(Z)}[\log{\bar{p}(X|z)}\bar{p}(z)] - E_{z \sim q(Z)}[\log{ q(z)}] \\ &= E_{z \sim q(Z)}[\log{\bar{p}(X|z)}] + E_{z \sim q(Z)}[\bar{p}(z)] - E_{z \sim q(Z)}[\log{ q(z)}] \\ &= E_{z \sim q(Z)}[\log{\bar{p}(X|z)}] + E_{z \sim q(Z)}[\bar{p}(z) - \log{ q(z)}] \\ &= E_{z \sim q(Z)}[\log{\bar{p}(X|z)}] + E_{z \sim q(Z)}[\frac{\bar{p}(z)}{ q(z)}] \\ &= \color{Red} E_{z \sim q(Z)}[\log{\bar{p}(X|z)}] + D_{KL}(q||\bar{p}(z)) \\ \end{aligned}$$

여기서 $\bar{p}(X|z)$항은 모델의 잠재변수로 관측된 데이터를 예측하는 확률이다. 즉 여러개의 가능성있는 $q(z)$가 있을 경우, 기존 보다 더 좋은 것을 찾으면 된다. 특히 2번째 항은 $\bar{p}(z)$로서 잠재변수 $z$의 사전확률이므로 사람이 잘 잡아주면 된다. 이것은 $q(z)$가 $\bar{p}(z)$ 멀어지면 패널티(penalty)를 더 준다고 생각하면 된다.
결론적으로 관측한 데이터 $X$를 대상으로 기존보다 더 설명을 잘하는 $q(z)$를 반복적으로 잘 찾으면 된다. 사실 이렇게 반복적 찾는 방법은 다양하지 않을까? 너무 많아서 나열하기가 힘들 수도 있다.

$ELBO(q(z))$를 최적화 하는 방법은?

다음의 2개의 기법을 언급한다:
  1. Gradient 기법으로
    1. REINFORCE 기법(policy gradient), Score Function 기법
    2. Pathwise 기법.
  2. EM 기법이 있다.

이미 기존 것들은 많이 소개가 되었다. 그래서 생략을 한다. 하지만, 많이 소개되지 않은 ELBO의 최적화를 policy gradient로 연결하고, 최적화 하는 방법을 소개할 것이다.

REFERENCES

  1. https://ermongroup.github.io/cs228-notes/inference/variational/
  2. https://ratsgo.github.io/generative%20model/2017/12/19/vi/
  3. https://pyro.ai/examples/intro_part_ii.html#A-Simple-Example
  4. https://mpatacchiola.github.io/blog/2021/01/25/intro-variational-inference.html
  5. https://www.cs.princeton.edu/courses/archive/fall11/cos597C/lectures/variational-inference-i.pdf
  6. https://gregorygundersen.com/blog/2021/04/16/variational-inference/
  7. https://fabiandablander.com/r/Variational-Inference.html
  8. https://jonathan-hui.medium.com/machine-learning-summary-fundamental-6562ff7448a#ee5a
  9. Roeder, Geoffrey, Yuhuai Wu, and David Duvenaud. "Sticking the landing: Simple, lower-variance gradient estimators for variational inference." arXiv preprint arXiv:1703.09194 (2017).


미분의 Chain Rule, Computational Graph Backward 그리고 Gradient Descent Optimizaton

자세한 Pytorch 기술관련 내용은 여기를 참조 바랍니다: https://github.com/fantajeon/DLPytorch1.2/blob/master/Chapter1.ipynb

Computational Graph란?

계산의 순서를 기록하기 위해서 비순환 그래프(DAG, directed acyclic graph) 사용한다. 이 그래프를 computational graph라고 한다. 다른말로는 입력 변수로부터 출력 변수까지 복잡한 계산 과정을 기록한 그래프 자료구조이다. 보통 이 그래프는 2개의 과정으로 구성된다:

  1. Forward 과정(생성,추론/예측): 
    1. 생성과정: 입력에서 출력까지 계산 과정을 모두 기억을 한다. 연산(+,-,ReLU등)이 호출될때마다 즉시 그래프가 만들어 진다. 
    2. 예측과정: 입력값의 출력값을 계산할때 사용한다.
  2. Backward 과정(미분): 편미분 계산을 위해서 기억한 모든 과정을 역순서로 전파한다. 특히 오차(Loss)에 근거하여 미분값(=chain rule)을 계산한다. 이건 error backpropagation algorithm 의 일부이기도 하다. 이 후에 backward 과정을 통해서 자동으로 계산된 미분값과 learning rate(수식 $\ref{gdopt}$의 $\eta$)와 함께 병합하여 모델 학습에 사용한다.

이렇게 입력부터 출력까지의 모든 연산 과정을 기록하고, computational graph를 통하여 chain rule에기반한 미분 값을 계산을 능동적으로 가능해진다. 즉, 모든 계산 과정을 코드에 기록할 필요가 없다. 당연한듯하지만, 예전에는 하나하나 미분된 방정식 계산하고, 직접 소스 코드에 한땀한땀 하드 코딩해서 모델을 학습했다. 그래서 미리 정해진 계산만 할 수 있었다. 모델을 만들고 학습 코드를 만들었다. 하지만 computation graph를 활용하여 순서가 뒤집어 진 것이다. 이 computational graph 덕분에 범용적인 학습 라이브러리가 먼저 배포되고, 모델이 나중에 생성하는 순서가 가능해졌다.

Computation Graph의 Backward 과정

Backward 과정은 미분의 chain rule을 구현한 과정이다. 입력(모델 파라미터)의 변화가 출력의 어떠한 영향을 주는지 computation graph의 미분 방정식을 계산할 수 있다. 미분의 의미는 출력이 원하지 않는 방향으로 높아 졌다면(오류가 증가했다면), 관련 입력(파라미터)을 반대 방향으로 감소하면 될 것입니다. "모델 학습"은 이 과정을 반복적으로 방향을 줄이거나 / 높이거나 한다는 것이다. 이 반복하는 과정을 수학적으로 표현하면 다음의 수식 $\ref{gdopt}$ 처럼된다. 수식 $\ref{gdopt}$는 이미 알고있는 gradient descent optimization 기법이다. 아랫 첨자 수열($t,t+1$)로 표현해서 반복과정을 표현을 많이 한다.

$$\begin{align} w_{t+1} = w_{t} - \eta \frac{\partial L}{\partial w}  \nonumber \\ b_{t+1} = b_{t} - \eta \frac{\partial L}{\partial b} \label{gdopt} \end{align}  $$

[수식 $\ref{gdopt}$] Gradient Descent Optimizatoin 과정


미방의 Chain Rule을 품은 Computational Graph기반 Backward

[그림 1] Computational Graph와 Backward의 도식화

핵심 Insight! $L$의 변화를 $w$까지 연결: $L=e^2=(y - f(g(h(......z(w)))))^2$

흔히 언급되는 손실함수(loss function)인 $L=(y-\hat {y})^2$에서 $w$를 학습하기 위해서는 $\frac{\partial L}{\partial w} = \textup{blackbox}$을 반듯이 계산해야 한다. 하지만, 현재까지의 지면상으로는 구체적으로 어떤 모양인기 언급한 적이 없어서 $w$와 $L$은 직접적인 연관을 지을 수 없다. 지금부터 연결 짓는 과정을 설명한다. 입력부터 여러 단계의 계산을 거친 최종 결과는(오류의 크기를 나타내는) 변수 $L$이다. 그럼 두 변수를 어떻게 연관을 지을까? 바로 답은 편미분방정식의 chain rule에 있다. Chain Rule은 각 단계별로 변화량을 누적시킨 과정이라고 볼 수 있다. 

$w$부터 여러단계를 거쳐서 $L$까지 이루어 진 것에 chain rule을 적용하면, $$\frac{\partial L}{\partial w} =  \frac{\partial z}{\partial w} \frac{\partial \hat{y}}{\partial z}   \frac{\partial e}{\partial \hat{y}} \frac{\partial L}{\partial e} \label{df_L}$$ 이렇게 계산된다. 위에 그림 1처럼 붉은색 원부터 보라색 점선을 따라서 초록색 원(모델 파라미터)까지 각 노드의 미분값을 곱하면 Chain Rule과 동일하다(=backward). 파란색 원은 고정된 값(훈련 데이터셋)이므로 미분값을 계산하지 않는다. 이 처럼 computational graph의 기록된 정보를 바탕으로 단순한 곱하기 과정을 누적하면 chain rule이 완성된다. 

이 미분식 $\ref{df_L}$는 입력의 변화량과 출력의 변화량의 관계를 설명하는 공식이다. 다시말하면, $L$값의 변화량을 보고 $w$와 $b$의 이동 방향을 결정할 수 있다. 이 원리를 활용하여 손실이 적은 최적의 모델 파라미터($w$와 $b$)를 찾을 수 있다.


$w$의 Chain Rule속의 $\frac{\partial Y}{\partial X}$꼴 구체화 실습 해보기

그림 1은 아래의 방정식의 computational graph이다:
$$z = xw \nonumber$$
$$\hat {y} = z + b \nonumber$$
$$e = y - \hat{y} \nonumber$$
$$L = e^2 \nonumber$$
그리고 이것을 미분해보겠습니다.
$$\frac{\partial z}{\partial w} =x \nonumber$$
$$\frac{\partial \hat{y}}{\partial z}=1 \nonumber$$
$$\frac{\partial e}{\partial \hat{y}} = -1 \nonumber$$
$$\frac{\partial L}{\partial e} = 2e \nonumber$$
$$\frac{\partial L}{\partial L} = 1 \nonumber$$
자 이제 Chain Rule로 계산된 값을 각각 교체해 본다. 그러면 다음과 같이 계산된다:
$$ \begin{align} \frac{\partial L}{\partial w} &= \frac{\partial z}{\partial w} \frac{\partial \hat{y}}{\partial z}   \frac{\partial e}{\partial \hat{y}} \frac{\partial L}{\partial e} \\ &= (x)(1)(-1)(2e)(1)=-x(y-\hat{y}) \label{error} \end{align}$$

이 수식 $\ref{error}$에서 $e$값은 마지막 예측한 값($\hat{y}$)과 참 값($y$)의 오류로 볼 수가 있다. 학습하려는 모델인 $wx + b$에서 이 오류($e$)가 입력인 $x$를 곱하여 만들어진다(수식 $\ref{gdopt}$의 첫번째 식, $w$ 학습이다). 이 수식의 의미는 강한 입력은 강한 오차를 학습에 반영한다. 

결론

Computational graph에서 오른쪽 맨 끝($L$)에 오류가 왼쪽의 입력단에 $w$, $b$까지 전파된 것을 볼 수 있다. 즉, backward과정을 자세히 살펴보았다.

MathJax와 TikJax 태그 삽입



<!--MathJax 3 start-->
<script>
MathJax = {
  tex: {
	inlineMath: [['$', '$'], ['\\(', '\\)']],
    displayMath: [ ['$$','$$'], ["\\[","\\]"] ],
    processEscapes: false,
  	tags: 'all',
  }
};
</script>
    
<script src="https://polyfill.io/v3/polyfill.min.js?features=es6">
<script async='async' id='MathJax-script' src='https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js'/>
<script async='async' id='MathJax-script' src='https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js'/>

<!-- MathJax 3 end -->

<!-- tikzjax start --> 
<link href='https://tikzjax.com/v1/fonts.css' rel='stylesheet' type='text/css'/>
<script src='https://tikzjax.com/v1/tikzjax.js'/>
<!-- tikzjax end -->
<!-- Highlight start -->
<link href='//cdnjs.cloudflare.com/ajax/libs/highlight.js/9.15.6/styles/atom-one-dark-reasonable.min.css' rel='stylesheet'/>

<script src='//cdnjs.cloudflare.com/ajax/libs/highlight.js/9.15.6/highlight.min.js'/>
<script src='//cdnjs.cloudflare.com/ajax/libs/highlight.js/9.15.6/languages/r.min.js'/>

<script>hljs.initHighlightingOnLoad();</script>
<!-- Highlight end --> 
https://tohtml.com/jScript/

Gaussian Mixture Model, GMM

왜 Gaussian Mixture Model (GMM)을 기법을 사용할까?

관측한 변수의 분포가 복잡하여 설명이 힘들때 혼합 모델(mixture model)을 사용하여 더 쉽게 설명하기 위해서이다. 이때 보통 잠재 변수(latent variable)를 도입하고, 보통 잠재변수의 분포 모델은 수학적/계산적으로 용이한 모델을 사용한다. 즉, 단순한 모델의 조합으로 복잡한 모델을 설명한다. 결국, 잠재변수의 형태를 사전에 정해야 한다. 예를들면 잠재변수는 gaussian 분포를 따른다고 가정한다. 이러면 Gaussian Mixture Model이 된다.

최종적으로 단순한 모델의 인자들이 추정이 끝이나면, 우리는 복잡한 모델을 잘 설명하는 인자를 추정하게 된 것이다.

잠재 변수($z$)로 복잡한 모델을 설명하기

아래의 그림에서 $p(X)$ 분포를 어떻게 모델링해야 하지? 

그림 1 $p(x)$
[그림 1] $p(x)$의 분포

$p(x)$를 보면 특징이 있다. 바로, 봉오리(peak)가 3개가 있다는 것이다($\mu$). 그리고 좌우로 퍼진 정도는 다르다($\sigma$). 그럼 3개의 가우시안 모델을 가지고 설명을 할 수 있다고 생각할 것이다. 그러면 잠재변수($z$)를 3개 놓고, $z$는 가우시안 분포를 따를 것이라 가정하자. 그러면 아래 그림2와 같이 잠재변수의 분포를 가우시안(gaussian) 모델로 그림 1을 설명할 것이다. 물론 오차는 존재한다.




[그림 2] 3개의 잠재변수 $z$로 설명한 $p(x)$ 분포.   여기서, $p(x|z_k)=g(x|\mu_k,\sigma_k)$의 의미는 $z_k$가 주어졌을때의 $x$의 분포이다.

Marginal $x$을 한다는 것은? 잠재변수 $z$의 소거. 그것의 의미는?

단순히 잠재변수를 제거하여 $z$를 고려하지 않겠다는 표현이다. 즉, $z$를 모두 더하여 소거한다. 직관적인 의미를 알아보면 3개의 가우시안 모델이 주어졌을때, 오직 $x$의 관점에서 분포를 알기 위하여 $z$를 선형으로 중첩한 것이다. 결론적으로 잠재변수의 분포 가정과 marginal 연산을 사용하여, 가우시안 모델로 복잡한 $p(x)$를 설명 할 수 있게 된다. 마치 그림 3과같이 3개를 그래프(녹색, 붉은색 그리고 하늘색)를 중첩(표시)해서 핑크색 $p(x)$를 만드는 것과 비슷하다. 이것의 수학적 의미로 잠재변수를 소거를 위한 "marginal한다"는 것과 비슷하다. 물론, 관측된(파란색) $p(x)$와 중첩된(핑크색) $p(x)$사에이는 오차가 있다.



수학의 언어로 모델링 과정을 살펴보자.

잠재변수와 $x$를 수학적 심볼로 해석을 해보자. 우리의 직관을 매우 정교하게 설명한 것뿐이다. 잠재변수($z$)가 있다고 가정하면, 두 변수를 결합한 확률은 단순하게 $$p(x,z) \label{p_x_z}$$ 결합확률 분포로 표현된다. 이것만으로는 $z$로부터 $x$를 표현할 수 없다. 여기서 중첩원리를 이용하자. 바로 marginal이다. 

그러면, $x$의 marginal은 
$$p(x) = \sum_{k=1}^{K} p(x,z_k)$$
가 될 것이다. 하지만, 아직도 $p(x,z_k)$를 우리가 아는 가우시안 모델로 연결을 시키기에는 역부족이다. Factorization기법[참조] 이용해보자. 여기서 $p(x,z_k)$는 $z$로부터 $x$가 생성되는 것을 가정한다면 factorization 원리의 따라서, $p(x,z_k) = p(x|z_k)p(z_k)$가 된다. 
그리므로  $$p(x) = \sum_{k=1}^{K} p(z_k)p(x|z_k) \label{factorization}$$

방정식 $\ref{factorization}$에서 무언가 구체화 됬다($z_k$로부터 $x$를 표현할 수 있다). $p(x|z_k)$는 그림 2에서 한 개의  가우시안 모델로 구체화를 할 수 있다(likelihood이다). Likelihood는 모델의 인자가 정해질 경우 $x$의 확률을 계산할 수 있다. 즉, 그림 2에서의 각 색별로 가우시안 분포를 계산할 수 있다. 수학적으로 자세히 써보면, 가우시안에 근거한 likelihood $p(x|z_k)$는 $N(x|\mu_k,\sigma_k)$가 된다. 잠재변수 $z_k$의 가우시안 모델이라고 할 수 있다.
$p(z_k)$는 $z_k$의 가중치가 된다. 더군다나 $x$와는 상관없는 확률이다. 단순한 더하기의 중첩이 아니라, 가운시안 모델, $p(x|z_k)$의 가중치의 합이다. 

최종적으로 
$$\begin{align}p(x) &=& \sum_{k=1}^{K} p(z_k)N(x|\mu_k,\sigma_k) \nonumber \\ &=& \sum_{k=1}^{K}w(z_k)*N(x|\mu_k,\sigma_k) \label{nzk} \end{align}$$

$p(x,z)$[$\ref{p_x_z}$]의 분포로부터 잠재변수 가우시안 분포[$\ref{nzk}$]까지 구체화를 하였다.

K-Clustering과 GMM의 관계는?

만약 $z_k$가 one-hot encoding일때를 칭한다. One-hot encoding은 1개만 1일고 나머지는 0인 배열을 가지는 것이다. 즉, $z_1$ = [1,0,0], $z_2$ = [0,1,0], $z_3$ = [0,0,1]이 된다. 그리고 K는 여기서 3이다.

그림 2에서 처럼  $x$위에 모든 점들은 likelihood( $p(x|\mu_k,\sigma_k)$ )가 가장 큰 값에 해당하는 색깔을 계산적으로 찾을 수 있다($k=\underset{k}{\operatorname{argmax}} \ {p(x|z_k)}$). 이는 결국 빨강, 녹색과 하늘색 중 하나에 귀속을 판정할 수 있다.

EM을 사용하여 GMM의 인자를 찾는다.

이 모델을 $z_k$를 찾는 방법은 보통 MLE(Maximum Likelihood Estimation)의 해법으로 EM(Expectation Maximization) algorithm을 많이 사용한다.
EM은 보통 2단계로 구성된다:
  1. E단계: $(\mu,\sigma)$를 고정하고, 모든 데이터의 $p(z_k|\mu,\sigma)$의 값을 계산한다.
  2. M단계: 가장 잘 설명하는 모델의 파라미터($\mu,\sigma$)를 새롭게 갱신한다.
  3. 1,2단계 반복, 단, 모델 파라미터의 변화가 작으면 종료한다.
자세한 EM은 다른 글에서 설명하기로 하자.



Importance Sampling이란?

복잡한 함수 $p(z)$를 가지는 $f(x)$의 기대값을 계산하고 싶지만, 수학적으로 계산하기 어려울 경우 사용하는 방법 중 하나이다. 특히 $p(z)$로부터 샘플을 추출하기가 어려울 경우 사용한다. 바로 $p(z)$ 대신 단순한 $q(z)$의 샘플을 사용하여 $f(z)$의 기대값을 계산한다. 비록 $p(z)$는 어려운 형태이지만,  모든 $z$값이 주어지면 $p(z)$의 값을 쉽게 계산할 수 있어야 한다. 더 자세히 말하면, $p(z)$보다는 $\tilde{p}(z) / Z_p$에서 $\tilde{p}(z)$이다. 이유는 $Z_p$(정규항)를 계산하기 어렵기 때문이다. 정규항이란 값을 정규범위 값으로 만들어주는 항이다. 모르기 때문에 그냥 퉁쳐서 역할상의 의미로 정규항이라고 칭한다. 여기서는 확률 분포이므로, 0~1까지의 값으로 만들어 준다.


Importance sampling 공식 살펴보기

$$E_{z \sim p}[f] \approx  \frac{1}{L} {\sum_{l=1}^{L}{  \color{Blue}\frac{p(z^l)} {q(z^l)}} f(z) } = \frac{1}{L} {\sum_{l=1}^{L}{  \color{Blue}w(z^l) }f(z) } \tag{1} \label{ef}$$

여기서 $\frac{p(q^l)}{q(z^l)}$이 샘플 $z^l$의 가중치(weight)라고 볼 수 있다. 즉, 가중치는 두 $p(z)$와 $q(z)$의 비율이라고 해석할 수 있다. 이 $q(z)$와 $p(z)$는 샘플로부터 근사적으로 계산할 수 있는 값이다. 즉, $w(z)$는 바로 계산이 가능하다. 단, $\sum_{l=1}^{L} w(z^{l}) = 1$이 되도록 정규화를 하면된다. 

Importance sampling 기법은 특별히 샘플 $z$를 버리거나 하지 않는다. 무조건 $f(z)$의 기대값[$\ref{ef}$]을 계산할 때 사용한다.

좋은 $q(z)$는 무엇일까?

  1. $p(z)$와 가능한한 모습이 비슷하면 좋다.
    1. 샘플링 낭비가 발생한다.
      1. Uniform sampling으로 하면 샘플$z^{l}$을 낭비할 수 있다.
      2. 모양이 다르면 $w(z^{l})$의 값이 0에 가까울 수 있다.
  2. 샘플링이 쉬운 확률 분포이면 좋다.
  3. 결과적으로 사용자의 안목이 중요하다.

[Rust] Ownership, Scope, Transfer Ownership 과 Borrowing

Scope, Ownership, Transfer Rust에서 사용하는 영역, 소유, 소유권 이전, 복사, 빌림을 요약해 보는 것이 목적이다. 소유(Ownership)은 Java, Go와 같은 백그라운드에서 실행하는 garbage collector가 없...