목차

  • gradio란?
  • 사용법
  • 동작 방식
  • 어디서 든 내 모델을 사용할 수 있게 하기
  • 한계점

Gradio란?

Gradio는 Model을 웹으로 손쉽게 사용할 수 있게 해주는 라이브러리이자 플랫폼입니다.

각종 UI를 쉽게 그릴 수 있게 해주고, input을 받아 서버에서 inference 혹은 predict를 한 후 그 결과를 사용자들에게 돌려줍니다.

다른 웹 어플리케이션 서버들과 차별화된 점은 ML 모델이 자주 다루는 컴포넌트들이 이미 이쁜 UI로 구현이 돼 있다는 점이죠.

복잡한 커스터마이징은 어렵지만, 오디오, 비디오, 챗봇, 이미지, 텍스트, 확률 표시 등 머신러닝에서 자주 사용하는 기능들을 거의 코딩 없이 제공가능합니다.

사용법

먼저 gradio를 설치해 줍니다.

pip install gradio

아래가 기본적인 템플릿입니다.

 

 

이 템플릿을 실행 시켜보면 주소가 나오는데, 그 주소를 웹 브라우저에 복사 붙여넣기를 하면 다음과 같은 웹페이지가 뜹니다.

동작 방식

gradio는 기본적으로 fastapi를 기반으로 돌아갑니다.

복잡한 것들을 설명하기는 지면이 길어지니 주로 사용하는 예시들을 들어드리겠습니다.

12번째 줄은 함수가 들어갑니다. 정확히는 python의 callable이면 모두 들어갈 수 있습니다.

이에 대한 구체적인 구현은 4-7번째 줄에서 확인할 수 있습니다.

여기서 13번째 줄의 inputs 파라미터에는 어떤 종류를 넣을 것인지 결정할 수 있습니다.

여기서 inputs에 들어갈 수 있는 오브젝트들을 컴포넌트라고 합니다.

가장 기본적인 동작은 inference 함수의 parameter 명이 컴포넌트의 label 명이 됩니다.

즉 13번째 줄은 text 컴포넌트를 사용하겠다는 것이고, text의 label은 'my_input'이 됩니다.

조금더 자세히는 gr.components.Text 를 내부적으로 생성하게 되는데 자세한 설정을 하고 싶다면 이 컴포넌트를 직접 넘겨줘도 됩니다.

이왕이면 inference 함수의 parameter의 개수가 inputs의 개수가 일치하는게 좋습니다.

아래 예제를 보시면 더 이해가 쉽습니다.

 

 

이 코드는 아래와 같이 웹에서 랜더링 됩니다.

파라미터 명이 label이 되는 것을 확인할 수 있죠 (각 컴포넌트의 좌측 상단)

여러개의 output을 내보내고 싶다면 ,로 구분해서 (구체적으로는 tuple 형태로) return 해주면됩니다.

각 형의 어떤방식으로 랜더링할지는 outputs에 정해주면 됩니다.

구체적인 설정은 공식 document를 참고하시면 좋습니다.

https://gradio.app/docs/

 

Gradio Docs

Browse Gradio Documentation and Examples

gradio.app

어디서 든 내 모델을 사용할 수 있게 하기

아주 쉽습니다.

17 번 줄을 보시면 share=True 라는 코드가 있습니다.

이 코드를 입력하면 어디서든 접근 가능한 .gradio.live 로 끝나는 public url이 생성됩니다.

구체적으로는 터널링을 통해서 구현돼 있는데, 외부로 접근만 가능하다면 NAT 환경에서도 쉽게 사용이 가능합니다.

복잡한 것은 모르셔도 되지만, 그냥 어디서든 접근 가능하다 정도로 이해하시고 넘어가면 좋습니다.

한계점

유료 plan을 사용하지 않는 이상 저 URL은 3일정도만 유지 됩니다. 

직접 서버를 띄워서 사용하면 3일이상 사용할 수 있습니다.

클라우드에 EC2등에 띄워서 사용할 수 있습니다. 

하지만 EC2는 또 EC2 비용이 나가죠 ㅠㅠ

왜 디버깅하면서 이미지를 봐야하지?

제가 예전에 담당했던 업무중에 사람 얼굴의 특징점 (눈, 코, 입 등이라고생각하면 편하다) 가지고 기능을 짜야했던 적이 있었습니다. 지금도 그렇지만 그때는 텐서감수성이 떨어질 때라서, 현재 짜고 있는 코드가 맞게 동작하는지 잘 이해가 안가는 경우가 굉장히 많았습니다. 🙂

이미지 출처: https://towardsdatascience.com/facial-keypoints-detection-deep-learning-737547f73515?gi=529af36da233

이미지 출처: https://towardsdatascience.com/facial-keypoints-detection-deep-learning-737547f73515?gi=529af36da233

특히, 이게 단순 산술연산이 아니라 이미지상에서 어느 부분을 짜르거나, 어느 부분을 기준으로 뭔짓을 가해야하는 종류라면 정말 디버깅에 많은 시간이 걸립니다. (예를 들면 얼굴 부분만 자르기!)

짜도 그 이미지에 알맞게 적용됐는지, 머리속으로 상상은 해도 실제로 그렇게 짜졌는지 실행하기 전까지 불안에 떨어야 했죠.

따라서 디버깅 하면서 이미지를 보는 건 디버깅 시에 확실히 이점이 있습니다. 보면서 실수를 그때 그때 잡을 수 있으니까요. 특히 저 같은 뉴비 머신러닝러는 더더더더더더욱 그렇습니다.

이처럼 머신러닝, 특히 비전관련한 테스크를 하다보면 디버깅을 할 때 이미지 파일을 보면서 할 때 더편한 경우가 왕왕 있습니다.

대안들

중간중간 이미지를 보는 작업은 생각보다 편하지만, 단순하게 이미지를 로그처럼 찍는 방법은 생각보다 불편합니다. 이렇게 할 경우 폴더를 열어서 이미지를 켜봐야 하고, 코드에 삽입해서 작성함으로 그때 그때 모델을 실행해야하죠.

특히 후처리를 해야할 경우 매 실행마다 모델을 로드해야하기 때문에 더 오랜 시간이 걸리게 됩니다. 이것 때문에 코드하나 고치고 다시 재시작하고, 모델을 로드하는데 많은 시간을 소모하게 됩니다. 😢

다른 방법으로는 plt.show() 같은 짓을 코드에 삽입하는 겁니다. 그러면 scientific mode로 pycharm을 사용하면 되죠. 하지만 전 이 scientific mode를 좋아하지 않습니다. 솔직히 창도 번거롭게 많아지고 생각보다 기능이 불편했습니다. 제가 잘 사용하지 못하는걸까요.

플러그인

그래서 저는 주로 pycharm의 디버거를 사용하는데, 이 디버거에서 사용하기 매우 좋은 플로그인을 소개하고자 합니다.

바로 디버깅 중에 이미지를 보여주는 플러그인입니다.

https://plugins.jetbrains.com/plugin/14371-opencv-image-viewer

여기 들어가서 pycharm에 추가하면 파이참에서 dialog가 뜨는데 수락을하면 바로 pycharm에서 이 플러그인을 사용할 수 있습니다.

사용법

플러그인이 정상적으로 설치됐다면 디버깅을 원하는 지점에 break point를 등록 후 디버깅 모드로 실행합니다.

Watch에 등록된 image 형태의 numpy 배열을 우클릭했을 때, View as Image 라는 메뉴가 등록됐을 거에요.

image 형태의 numpy 배열이란 H x W x 1, H x W x 3 (BGR), H x W x 4 (BGRA) 형태의 배열입니다. $256 \times 256 \times 3$ 형태가 되겠죠.

근데 이 메뉴를 디버깅때마다 클릭하는 것은 좀 귀찮으니까 전 주로 단축키 alt+i 를 사용해서 디버깅합니다.

이미지 출처:  https://en.wikipedia.org/wiki/Image

그러면 이렇게 창이 뜨면서 디버깅중에 이미지를 볼 수 있습니다! 이렇게 이미지를 보면 그때 그때 이미지에 조작을 가하면서 프로그램이 정상동작하는지 볼 수 있습니다. 프로그램을 다시실행하지 않고도요!

기타 꿀팁

Color 변환

그런데 프레임워크별로 color format이 다릅니다. 예를들자면 matplotlib와 pillow의 경우, RGBA 형태로 로드하게됩니다. (PNG의 경우)

그러니까 이걸 그냥 그대로 보면 이렇게 원본 얼굴과 전혀 다른 형태로 이미지가 나오게 됩니다. pytorch도 RGB 형태로 BGR 형태로 바꿔줘야 올바른 색으로 볼 수 있죠.

즉 이런 경우 color foramt을 변경해줘야 하는데, cv2.cvtColor로 한줄코딩해주면 쉽게 변경해줄 수 있습니다.

matplotlib 으로 load한 경우

cv2.cvtColor(mat_image, cv2.COLOR_RGBA2BGRA) # matplotlib로 load한 경우

cv2 모듈의 색형식 변환 함수인 cvtColor를 통해서 변환해준 것입니다.

만약 Alpha channel이 필요없다면 cv2의 색형식 상수에서 A들을 빼주시면 됩니다.

pillow 로 load한 경우

cv2.cvtColor(np.array(pil_image), cv2.COLOR_RGBA2BGRA)  # pillow로 load한 경우

pillow가 return하는 image의 경우 numpy 형태가 아닙니다.

따라서 np.array로 변환해줘야 정상적으로 보입니다.

그리고 색변환을 수행해줘야하죠.

마무리

이게 뭐라고 상당히 글이 길어졌네요.

도움이 되셨으면 좋겠습니다.

다른 꿀팁이 있다면 공유해주시면 감사하겠습니다!

자 다시 GAN 포스팅을 작성할 시간이네요.

아 GAN은 쓸게 많아서 너무 괴로워요... PS쪽이 확실히 더 작성하기 편하네요.

익숙한 개념이라서 그런걸까요?

오늘 알아볼 내용은 GAN의 증명과 한계 그리고 해결법에 관한 내용입니다. 오늘 내용은 WGAN을 들어가기 앞서 필요한 GAN에 대한 증명과 한계들을 다뤄볼 예정입니다.

수학적인 내용을 최소한으로 준비했지만, 도저히 못덜어낸 수학들이 좀 많이 남았습니다.

작성한 글에 오류가 있다면 코멘트 달아주세요! 그럼 바로 시작하겠습니다.

복습

역시 복습을 안하고 갈수는 없겠죠?

복습에서는 이전 포스팅의 내용을 요약하도록 하겠습니다.

 

[논문 정리] GAN에 대해서 알아보자 (1) - 개념, 알고리즘, 소스코드 분석

GAN에 대해서 알아보자 - 개념, 알고리즘, 소스코드 최근에 GAN에 관해서 이것저것 많이 찾아볼 기회가 있어서 이것을 정리하고 블로깅하고자 합니다. 이번 블로깅에서는 최대한 수학적인 내용없

lifeignite.tistory.com

GAN의 Component

GAN은 두가지 component로 구성되어 있습니다. Generator와 Discriminator입니다. Generator는 실제 데이터와 유사한 분포의 데이터를 생성하기 위해 노력하는 Component죠. Discriminator는 Generator가 만든 가짜 데이터와 실제 Data를 구별해주는 Component였습니다. Discriminator로 머신러닝 네트워크를 사용하는 이유는 generator가 생성하는 분포와 실제 데이터 분포를 수식화 하기 힘든 매우 어려운 분포이기 때문입니다.

GAN의 Sampling

그리고, GAN은 latent space에서 latent vector z를 sampling합니다. 초기의 latent space는 아무런 의미를 가지지 않기 때문에 여기서 sampling된 latent vector z도 아무런 의미를 가지지 않습니다만, 학습을 통해서 latent vector와 특정 데이터를 연결하게 되면서 latent space가 의미를 가지도록 바꿔줍니다. 마치 사람들이 아무의미없는 것들에 의미를 부여하는것처럼 네트워크도 경험적으로 latent space에 의미를 부여하기 시작할겁니다. ㅎㅎ

GAN의 학습 Algorithm

GAN의 학습은 2가지 step으로 이루어졌었죠. 첫번째 스텝에서는 Generator를 고정시키고, Discriminator를 학습시킵니다. 두번째 스텝에서는 Discriminator를 고정시키고, Generator를 학습시킵니다. 그리고 고정시킴에 따라 Loss Function이 일부 변경되었었습니다.

전체를 요약해보자면

GAN의 전체 구조를 요약해보자면 GAN은 latent space에서 sampling 한 z로부터 Generator는 어떠한 데이터 분포를 만듭니다. 그리고 이렇게 생성한 Generator의 분포와 실제 데이터 분포를 Discriminator로부터 비교하게 함으로서 G가 잘생성하면 D를 혼내주고, D가 잘 구분하면 G를 혼내주는 방법으로 학습을 수행합니다.

아래와같은 구조를 가지고 있죠.

GAN과 JS Divergence

직관적으로 GAN이 왜 동작하는지는 쉽게 이해가 됩니다. 그런데 직관적으로만 ㅇㅋ 하고 가면 조금 찝찝하죠. 그래서 GAN이 동작하는 이유에 대해서 증명을 하나하고 넘어가겠습니다.

이 내용은 이 후 WGAN으로 넘어가기 위해서 반드시 필요하다고 생각해서 넣었는데, 생략하시고 싶으시면 바로 WGAN으로 넘어가시면 됩니다.

한줄 요약

최초 제안된 GAN의 Loss function은 JSD를 계산하게 되는데, JSD는 두 분포가 겹치지 않았을 때, 상수 값을 가져서 gradient 계산으로 optimal을 찾는 것이 쉽지 않다.

GAN의 동작 조건

GAN의 목표는 실제 데이터 분포인 $P_{data}$ 와 Generator 가 만든 가짜 데이터 분포인 $P_{g}$ 가 같아지는 것을 목표로 합니다.

즉 GAN의 Loss Function인 아래 식이 최적일 때, $P_{data}=P_g$ 임을 만족해야한다는 뜻이죠.

$$\min_{G} \max_{D} V(G, D)=\mathbb{E}_{x \sim P_{data}(x)}[\log D(x)]+\mathbb{E}_{z \sim P_{z} (z)}[\log (1-D(G(z))]$$

일단, 최적의 G에 대해서 생각할 거니까, G를 고정한 것과 같죠? 저번 시간에 봤던 식이 튀어나올 겁니다.

$$\max_{D} V(G, D)=\mathbb{E}_{x \sim P_{data}(x)}[\log D(x)]+\mathbb{E}_{x \sim P_g (x)}[\log (1-D(x)]$$

 


Term 정리

자 변수가 너무 많으니까 좀 정리해볼께요.

  • $x$: 이미지 ( high-dimensional vector)
  • $G$: generator 함수입니다.
  • $D$: discriminator 함수입니다.
  • $P_{data}$: 실제 데이터 셋의 분포입니다.
  • $P_g$: generator가 생성한 가짜 데이터의 분포입니다.
  • $\mathbb{E}$: 기대값이에요. 의외로 이 기호를 잘모르시는 분들이 많더라구요.
  • 기대값은 $p(x)$의 확률로 나타나는 어떤 함수 $f(x)$의 평균으로써 $$\mathbb{E}_{x \sim p}[f] = \int p(x)f(x)dx$$로 계산될 수 있습니다.

 


Loss Function 이 최적일 때의 D

자 변수를 정리해놓고 봤는데, 저희의 목적을 잊지 말죠.

저희가 확인하고 싶은 것은 이 Loss Function이 최적일 때, $P_{data}=P_g$ 가 되는지 확인하는 것이에요.

그럼, Loss Function이 최적일 때는 어떨까요? 바로 Loss Function $V$ 를 최대화하는 것입니다.

Loss Function을 최대화하는 최적의 (optimal) $D$를 $D^*$ 라고 할께요 . 이를 수식으로 나타내면 아래와 같은 거에요.

$$D^*=arg\underset {D}{\max}V(D,G)$$

그리고 우리의 Loss Function $V$의 식이 아래와 같았습니다.

$$\max_{D} V(G, D)=\mathbb{E}_{x \sim P_{data}(x)}[\log D(x)]+\mathbb{E}_{x \sim P_g (x)}[\log (1-D(x)]$$

$\mathbb{E}$ 를 풀어볼게요. 기대값은 $\mathbb{E}_{x \sim p}[f] = \int p(x)f(x)dx$ 로 풀리니까,

$$\begin{aligned}V &=\mathbb{E}_{x \sim P_{ data }}[\log D(x)]+\mathbb{E}_{x \sim P_{g}}[\log (1-D(x))] \\&=\int_{x} P_{data }(x) \log D(x) d x+\int_{x} P_{g}(x) \log (1-D(x)) d x \\&=\int_x\left[P_{\text {data }}(x) \log D(x)+P_{g}(x) \log (1-D(x))\right] d x\end{aligned}$$

단순하게 식에 대입하고, 적분 기호안쪽으로 모아둔 것 밖에 없는 식입니다! 정말 단순한 식이에요.

자 이제 저 식에서 $a=P_{data}$ 이고, $b=P_G(x)$ 라고 했을 때 위식을 정리해보면,

$$f(x) = alogx+blog(1-x)$$

꼴이 되게 됩니다. 이 함수를 그래프로 그려보시면, 위로 볼록한 모양이 되게 되는데, 0과 1 사이에서 미분 가능합니다. 저희는 이 식의 최대값을 구하고 싶은 거니까 이 식을 미분해볼께요.

그러면 아래와 같이 미분이 가능합니다. (고등학교 로그 미분 입니다!)

$$\begin{aligned} \frac{f(x)}{dx} = \frac{a}{x} - \frac{b}{1-x}&=0 \\ a(x-1)+bx &=0\\ (b+a)x-a &=0 \\ \therefore x =\frac{a}{a+b} \\ \end{aligned}$$

즉, $x$가 $\frac{a}{a+b}$ 일 때, 최대 값을 가진다는 뜻이죠.

최적의 D에 다시 $a=P_{data}$ 이고, $b=P_G(x)$ 를 넣고 정리해보자면,

$$optimal \space D^*= \frac{P_{data}(x)}{P_{data}(x)+P_G(x)}$$

이 되게 됩니다.

D가 최적일 때, Loss Function의 의미는?

그럼 $D^*$ 일 때, 식은 어떻게 될까요?

한번 $a=P_{data}$ 이고, $b=P_G(x)$ , $D^*=\frac{a}{a+b}$ 로 넣고 정리 해볼께요.

$$\begin{aligned} &\max _{D} V(G, D)=V\left(G, D^{*}\right) \\ &=\int_x\left[P_{\text {data }}(x) \log D^*(x)+P_{G}(x) \log (1-D^*(x))\right] dx\\&=\int_x[\text{log}(a\frac{a}{a+b}) + \text{log}(b(1-\frac{a}{a+b}))]dx \\&=\int_x[\text{log}(\frac{1}{2}a\frac{a}{(a+b)/2}) + \text{log}(\frac{1}{2}b\frac{b}{(a+b)/2})]dx\\&=-2\text{log}2 + \int_x\text{log}a\frac{a}{(a+b)/2}dx + \int_x\text{log}b\frac{b}{(a+b)/2}dx \end{aligned}$$

자! 여기서 KLD와 JSD가 등장하게 됩니다. KLD에 대해서는 아래 포스팅을 참고하세요!

 

Entropy, Cross Entropy, KL-Divergence에 대해 알아보자

Entropy 엔트로피는 머신러닝에서 정보량을 나타내는 방법으로 사용된다. 정보의 량이라는게 되게 추상적으로 들리는데, 생각해보면 되게 간단한 개념이다. ML상에서 굉장히 많이 사용되는 개념

lifeignite.tistory.com

어쨌든 KL Divergence는 아래와 같이 정의됩니다.

$$KL(P \parallel Q)= \sum_xP(x)log\frac{P(x)}{Q(x)}$$

이식을 사용해서 풀어보자면,

$$\max _{D} V(G, D)=V\left(G, D^{*}\right) \\ =-2\text{log}2 + \int_x\text{log}a\frac{a}{(a+b)/2}dx + \int_x\text{log}b\frac{b}{(a+b)/2}dx\\ =-2 \log 2+\int_{x} P_{\text {data }}(x) \log \frac{P_{\text {data }}(x)}{\left(P_{\text {data }}(x)+P_{G}(x)\right) / 2} d x \\ +\int_{x} P_{G}(x) \log \frac{P_{G}(x)}{\left(P_{\text {data }}(x)+P_{G}(x)\right) / 2} d x \\ =-2 \log 2+\mathrm{KL}\left(\mathrm{P}_{\text {data }} \| \frac{\mathrm{P}_{\text {data }}+\mathrm{P}_{\mathrm{G}}}{2}\right)+\mathrm{KL}\left(\mathrm{P}_{\mathrm{G}} \| \frac{\mathrm{P}_{\text {data }}+\mathrm{P}_{\mathrm{G}}}{2}\right)$$

그리고, Jensen-Shannon Divergence(JSD)는 아래와 같이 정의가 됩니다.

$$\begin{aligned}\operatorname{JSD}(P \| Q)=\frac{1}{2} KL(P \| M) &+\frac{1}{2} KL(Q \| M) \\M &=\frac{1}{2}(P+Q)\end{aligned}$$

JSD는 KL의 개량형 버전이라고 생각하셔도되는데, KL이 대칭성이 성립하지 않아 거리로 사용할 수 없던 것을 대칭적으로 만들어줘 거리로 사용할 수 있게 됩니다.

모양이 JSD 처럼 변했죠? 그럼 아래와 같이 정리 할수 있게 됩니다.

$$\max _{D} V(G, D)=V\left(G, D^{*}\right)\\=-2 \log 2+\mathrm{KL}\left(\mathrm{P}_{\text {data }} \| \frac{\mathrm{P}_{\text {data }}+\mathrm{P}_{\mathrm{G}}}{2}\right)+\mathrm{KL}\left(\mathrm{P}_{\mathrm{G}} \| \frac{\mathrm{P}_{\text {data }}+\mathrm{P}_{\mathrm{G}}}{2}\right) \\=-2 \log 2+2 J S D\left(P_{\text {data }} \| P_{G}\right)$$

오우 정신 사납습니다. 결론은 저희의 Loss function은 JSD로 귀결되게 됩니다.

그리고 JSD는 두 분포사이의 거리로 사용할 수 있음으로, 따라서 위의 수식을 최소로 만드는 것은 두 분포사이의 거리를 최소로 만드는 것이란 거죠!

WGAN의 등장

자 이제부터 쉬워져요. 왜냐면 여기서 수학을 다뺏거든요.

한숨돌리시고 갑시다!

JS Divergence는 학습에 적합하지 않다!

WGAN에서 주장하길 좋은친구아저씨가 제안한 loss의 JSD는 학습에 적합하지 않다는 것이었습니다. 이걸 이해하기 위해서는 SUPP를 이해하셔야하는데, 간단히 설명할께요.

지지 집합 SUPP

SUPP는 support라고 읽는데, 한국어로는 지지집합이라고 합니다. 멋진 위키를 참고하자면

수학에서, 함수의 지지집합(支持集合, 영어: support 서포트[*]) 또는 받침은 그 함수가 0이 아닌 점들의 집합의 폐포이다.

X가 위상 공간이고, ${\displaystyle f\colon X\to \mathbb {R} }$이 함수라고 하자. 그렇다면 ${\displaystyle f}$의 지지집합 ${\displaystyle \operatorname {supp} f}$는 다음과 같다.
${\displaystyle \operatorname {supp} f=\operatorname {cl} {x\in X\colon f(x)\neq 0}}$
여기서 ${\displaystyle \operatorname {cl} }$ 폐포 연산자다.

인데, 사실 어려운말 다 쳐내고, 이해하시면 좋은 것 단하나 바로 0이 아닌 점들의 집합 입니다. 콤펙트하고 머시기하고 유계 집합 머시기 이런것들이 있는데 사실 저도 잘모릅니다. 수학과가아니라서요. 개념상 0이 아닌 점들의 집합으로 이해하고 넘어가셔도 큰문제가 없습니다.

그림으로 표현해보겠습니다. 두 분포 $\mathbb{P}_r$ 과 $\mathbb{P}_g$ 가 있다고 하겠습니다. 그 분포가 아래와 같이 있다고 가정해볼께요.

여기서 $SUPP \space \space \mathbb{P}_r$ 는 B 공간이 되고, $SUPP \space \space \mathbb{P}_g$ 는 A공간이 된다는 겁니다. 그야말로 0이 아닌 점들의 집합입니다.

이 때, $\mathbb{P}_r(A)=0, \mathbb{P}_r (B)=1$ 이 될 것입니다. 반대로, $\mathbb{P}_g(A)=1, \mathbb{P}_g (B)=0$ 이 되겠죠?

분포의 거리 측정

자 그럼 조금더 직관적인 설명을 위해서, 두 확률 변수를 2차원상의 분포라고 가정해서 생각해보겠습니다.

두 분포가 겹치지 않는다는 사실은 명확하죠. 서로 $0$ 과 $\theta$ 사이에서만 움직이니까요.

여기서 $\theta$ 는 다양한 값을 가질 수 있습니다.

하지만, $\theta \neq0$ 인 경우를 제외한 어떤 경우에도 두 분포가 겹칠 수는 없습니다. 이 경우 두 분포 중 하나가 0이 아닌 값을 가질 때는 다른 분포에서는 무조건 0인 값을 가지게 될 것입니다.

식으로 나타내면

$$\left\{\begin{array}{ll}P_{0}(x) \neq 0 & \Rightarrow P_{\theta}(x)=0 \\P_{\theta}(x) \neq 0 & \Rightarrow P_{0}(x)=0\end{array}\right.$$

이 떄 이상적으로 분포의 거리를 측정하는 지표라면 이 때 두 분포사의 거리는 어떻게 측정되는 것이 좋을까요?

직관적으로 두 분포사이의 최단거리(직선거리) $\theta$ 에 따른 값을 가지는 것이 적합할 것입니다. 예를들면 $\text{distance}(\mathbb{P}_0,\mathbb{P}_\theta)=\theta$ 와 같이 나타낼 수 있으면 좋겠네요.

KL의 경우

자, 그럼 KL divergence는 어떻게 두 분포사이의 거리를 측정할까요?

$$\mathrm{KL}\left(\mathbb{P}_{\theta} \| \mathbb{P}_{0}\right)=\int_{\left\{x: P_{\theta}(x) \neq 0\right\}} \log \left(\frac{P_{\theta}(x)}{P_{0}(x)}\right) P_{\theta}(x) \mu(d x)$$

$P_\theta>0$ 인 곳에서, $P_{0}(x)=0$ 이 되니까 아래와 같이 쓸수 있겠네요.

$$\log \left(\frac{P_{\theta}(x)}{P_{0}(x)}\right)=\infty$$

그럼 KL은 아래와 같이 계산됩니다.

$$\mathrm{KL}\left(\mathbb{P}_{\theta} \| \mathbb{P}_{0}\right)=\int \infty \cdot P_{\theta}(x) \mu(d x)=\infty$$

즉, $KL$ 값은 무한대의 값을 가지게 됩니다.

$\theta$ 가 0 이아닌 어떤 값이든, 모든 상황에서 무한대의 값을 가지게 되죠.

$$\mathrm{KL}\left(\mathbb{P}_{0}\|  \mathbb{P}_{\theta} \right)=\mathrm{KL}\left(\mathbb{P}_{0} \| \mathbb{P}_{\infty}\right)=\left\{\begin{array}{ll}\infty & : \theta \neq 0 \\0 & : \theta=0\end{array}\right.$$

JSD의 경우

이 경우는 매우 간단합니다. 한번 보시죠.

$$\begin{aligned}\mathrm{KL}\left(\mathbb{P}_{0} \| \mathbb{P}_{m}\right)=\int_{P_{0} \neq 0} \log \left(\frac{P_{0}(x)}{P_{0}(x) / 2}\right) P_{0}(x) \mu(d x)=\log 2 \\\mathrm{KL}\left(\mathbb{P}_{\theta} \| \mathbb{P}_{m}\right)=\int_{P_{\theta} \neq 0} \log \left(\frac{P_{\theta}(x)}{P_{\theta}(x) / 2}\right) P_{\theta}(x) \mu(d x)=\log 2\end{aligned}$$

그러므로 $\theta\neq0$ 이면 JS는 $\text{log}2$ 의 값을 가지게 됩니다. 따라서

$$\operatorname{JS}\left(\mathbb{P}_{\theta}, \mathbb{P}_{0}\right)=\left\{\begin{array}{ll}\log 2 & : \theta \neq 0 \\0 & : \theta=0\end{array}\right.$$

가 된다는 것이죠. 따라서 마찬가지로 $\theta$ 의 값과 상관없이 상항 같은 상수값을 나타내게됩니다.

GAN에서의 의미

이는 GAN을 학습하는데 큰 문제가 됩니다. Loss Function은 가까우면 가깝다고, 멀면 멀다고 명확히 말을 해주어야 이에 따른 gradient를 계산할 수 있습니다. 하지만 두 분포의 SUPP이 겹치지 않는다면 '두 분포가 완전히 다르다.' 라는 정보만 줄 뿐 어떻게 가깝게 만들지에 대한 정보 즉 gradient를 계산할 수 없다는 뜻입니다.

즉, D가 너무 깐깐하게 두 분포를 판단해서, 만약 두 분포가 겹치지 않는다면 두분포를 어떻게 가깝게 만들지에 대한 gradient를 계산할 수 없게 된다는 거죠.

이는 사실 일만적으로 이미지 생성과 같은 높은 dimension의 문제를 푸는 GAN에서는 이러한 분포가 겹치지 않는 문제가 더 심하게 발상할 것이기 때문에 GAN의 학습 성능이 떨어지는 등 꽤 크리티컬한 문제로 다가올 것이었습니다.

이 문제를 해결하러 왔다.

이 문제를 해결하기 위해서 2가지 방법을 소개해드리도록 하겠습니다.

  • 분포를 건드리는 방법
  • 분포의 거리 측정 방식을 개선하는 방법

당연히 우리가 앞으로 할 짓은 후자입니다만, 전자 쪽은 매우 간단하고 직관적으로 이해가 쉬우니까 한번 보고 가실께요.

노이즈를 통한 해결

Support가 겹치지 않는 것이 문제였습니다. 때문에 기존의 이미지에 노이즈 n을 추가해주면서 아래 그림처럼 두 분포의 Support영역을 넓혀 겹칠수 있도록 만들어주는 것입니다. 이렇게 두 분포를 겹치게 만들어주면 JSD를 사용해서 문제를 해결할 수 있습니다.

하지만 이러한 해결방법은 생성된 이미지가 굉장히 흐릿하게 나오는 등 문제가 발생하는 등 성능이 좋지 않게 나왔다고 합니다.

JSD가 아닌 다른 형태의 거리 측정 방식 사용

JSD는 두 분포가 겹치지 않을 때 상수가 나와 gradient를 계산하기 힘든 문제가 있었습니다.

따라서 저자는 분포가 겹치지 않아도 두 분포의 거리를 측정할 수 있는 방법인 Earth Mover Distance를 사용할 것을 제안합니다. 그리고 이제부터가 WGAN의 시작이죠.

WGAN은 다음 포스팅에서 다루도록 하겠습니다. 길이 너무 길어져서 여기서 한번 끊고 가도록 하겠습니다.

Reference

논문을 검색하고, 성능을 찾아보고, 또 비교하고 하는 작업은 사실 굉장히 피곤한 작업입니다.

특히 인력을 갈아 넣지 않는이상 혼자서 한 분야의 모든 논문을 서베이하고, 성능을 비교분석하는 것은 정말 어려운 일이죠. 

거기다 각종 지표들, 그리고 데이터셋들에 대해서 검색하고 읽을 만한 논문을 선정하는 작업자체가 굉장히 번거롭고 어렵습니다.

ML 계열에서 이런 것을 대신해줄 수 있는 사이트가 있습니다. 

 

 

Papers with Code - The latest in Machine Learning

Papers With Code highlights trending Machine Learning research and the code to implement it.

paperswithcode.com

해당 사이트에서는 각 영역별로 세부 task를 나누고

 

사이트에서 여러 데이터셋, 지표등을 활용해서 논문의 성능을 보여줍니다. 

뿐만아니라 여기 등록된 대부분의 paper는 코드까지 포함하고 있어서 쉽게 코드를 참고할 수 있어서 더욱 좋죠.

각 데이터셋 별로 최고의 SOTA를 달성한 논문과 코드를 리스트업해줍니다.

논문을 찾기 막막하거나, 완전히 새로운 도메인의 연구를 시작했다면 정말 큰 도움이 될 사이트입니다.

사실 이런사이트가 없었다면 서베이부터 읽어야 했을 것이고, 심지어 서베이가 나오지 않는다면 매번 새 논문들을 뒤적거려야 했겠죠... 

알맞는 지표나 데이터셋 차즌ㄴ데에도 활용할 수 있습니다.

다들 연구 화이팅

GAN에 대해서 알아보자 - 개념, 알고리즘, 소스코드

최근에 GAN에 관해서 이것저것 많이 찾아볼 기회가 있어서 이것을 정리하고 블로깅하고자 합니다. 이번 블로깅에서는 최대한 수학적인 내용없이 GAN에 대해서 깊게 이해해보고자하는 시간을 가지려고 합니다. 사실 제가 수학을 잘하지도 못하구요...

틀린 내용이 있을 수 있으니 말씀 주시고, 이제 시작해봅니다 ^ㅇ^

오늘의 내용은 크게 3개로 나누어져 있습니다.

  • Generative Model에 관한 내용
  • GAN의 개념
  • GAN의 학습 알고리즘

Introduction

GAN은 일단 생성 모델입니다. 특히 초기 GAN의 연구에서는 랜덤하게 그럴듯한 이미지를 생성하는 것부터 시작했죠. 머신러닝이 생성을한다... 딱 생각했을 때부터 쉽게 상상히 안가는 일입니다. 그만큼 풀고자하는 문제가 어렵다고 볼 수 있습니다.

파인만 아조씨는 What I cannot create, I do not understand. 라는 말을 했습니다. 대우명제로 보자면, 내가 이해했다면 생성할 수 있다고 볼수 있겠죠? ㅎㅎ 아무튼 실제로 그렇게 잘 되지는 않지만, 그만큼 생성한다는 것 자체가 쉽지 않은 영역이라는 것을 말씀드리고 싶었습니다.

무엇인가를 생성한다는 것은 일반적으로 Unsupervised Learning (UL)에 속합니다. 특히 GAN처럼 실제로 있는 것을 생성하는 것이 아니라, 랜덤하게 그럴 듯한 것들을 만드는 것은 그야말로 답이 없는 문제를 푸는 것과 같습니다. 답이 있는 Supervised Learning (SL)에 비해서 조금 더 생각하기 어려운 문제를 풀게 되는 것이죠.

머신 러닝 계열에서 이런 문제를 풀수 있는 대표적인 솔루션으로 크게 3가지를 들 수 있습니다.

  • Boltzmann machine
  • Auto-encoder (AE)
  • Generative Adversarial Network (GAN)

Auto-encoder

모두 다 어려운 영역입니다만, 직관적으로 이해하기 쉬운 AE의 개념에대해서 먼저 간단히 설명하고 넘어가겠습니다. 그림부터 보고 넘어가시죠.

image from https://blog.keras.io/building-autoencoders-in-keras.html

AE는 어려운 UL 문제를 SL 문제로 바꿔서 푼 것이라고 이해하시면 쉽습니다. 데이터 그 자체를 label로 활용한 것이죠.

  • Original Input = $x$
  • Encoder = $f$
  • Compressed representation (latent space) = $z$
  • Decoder = $g$

라고 했을 때, 일반적으로 Auto encoder는 다음과 같이 표현할 수 있습니다.

$$z=f(x), x=g(x) \rightarrow x=g(f(x))$$

즉, Encoder에게는 이미지를 잘 압축하는 방법을, 디코더에게는 잘압축된 표현 방식 (latent space)를 보고 원래 이미지를 재생성해내는 방식을 배우게 하는 것으로 동작하게 됩니다.

Generative Adversarial Network

그렇다면 GAN은 어떨까요? GAN의 기본 구조는 Auto Encder의 뒷부분을 때놓았다고 생각하셔도 좋습니다. 바로 아래와 같이요!

어떤 임의의 표현 $z$ 로부터 이미지를 생성할 수 있는 Decoder, 다른말로 Generator라고 하죠. 이 Generator로부터 이미지를 생성해냅니다.

이 구조만으로는 사실 학습이 불가능하겠죠. 원래 데이터의 분포를 알 수가 없을 테니까요. 여기서 등장하는 것이 바로 Discriminator입니다.

image from https://blog.keras.io/building-autoencoders-in-keras.html

Discriminator는 한국어로는 구별자정도로 해석될 수 있을 것 같습니다. 이 Discriminator는 실제로 구별을 하는 친구이기 때문에 이런 이름이 붙었죠. 정확히 말씀드리자면, 실제 데이터 (Real Data)로부터 데이터를 몇개 뽑고, Generator가 생성한 데이터 몇개를 또 뽑습니다. 그리고 Discriminator에게 이 두개를 섞어서 주죠. Discriminator의 역할은 주어진 데이터가 실제 데이터인지 우리가 생성한데이터인지를 구별시킵니다.

Generator의 주된 목표는 Discriminator가 구분하지 못할 정도로 실제 데이터와 유사한 데이터를 만드는 것입니다. Discriminator의 주된 목표는 Generator가 만든 것과 실제 데이터를 잘 구분해 내는 것이죠.

제설명이 부족한것 같아 GAN의 저자이신 우리의 좋은 동료.. Good fellow 아저씨께서 드신 비유를 한번 살펴 보도록 하겠습니다. 바로, 지폐위조범과 경찰입니다.

image from https://www.slideshare.net/thinkingfactory/pr12-intro-to-gans-jaejun-yoo

가짜 돈을 찍어내는 지폐위조범과, 진짜 돈과 가짜돈을 구별해야하는 경찰의 역활을 생각해볼까요?

  1. 지폐위조범이 가짜돈을 찍어 냅니다.
  2. 그럼 경찰이 이것을 보고 가짜(Fake)인지 진짜(Real)인지 구별합니다. 처음 찍어낸 돈의 품질이 좋지 않을 테니 쉽게 구분할수 있겠군요.
  3. 이를 보고 지폐 위조범은 더 돈을 정교하게 찍어낼 것입니다.
  4. 그러면 또 그것을 보고 경찰이 구별할 것입니다. 조금은더 정교해질 것이니 경찰도 최대한 가짜를 잘 구별하고자 노력할 것입니다.

이것을 계속 반복하다보면 경찰이 지폐위조범이 만든 돈을 잘 구분하지 못하는 순간이 올 것입니다. 즉 진짜 데이터와 가짜 데이터가 구분이 안가는 순간이 오는 것이죠.

그렇습니다! 이제 우리는 드디어 가짜 지폐를 잘 만들어내는 생성자를 가진 것입니다!

이것이 바로 직관적으로 GAN이 동작하는 이유입니다.

GAN의 개념

자 여기까지가 abstract 였고, 쬐끔 더 디테일하게 이해해 보도록 할까요?

Generator

자 세상의 모든 것으로 변할 수 있는 것들이 모여있는 공간을 생각해봅시다. 우리는 이러한 공간을 latent space 라고 부릅니다. 한국어로는 잠재 공간으로 불리고 있습니다. 모든 것으로도 변할 수 있는 것들이 모여 있으니까, 우리가 가지고 있는 실제 데이터 셋으로도 변할 수 있겠죠?

어떻게 이렇게 변화시킬지는 모르겠지만, 어떤 신적인 존재가 그렇게 변화시킨다고 해보도록 할께요. 그림으로 그려보자면 다음과 같은 것입니다.

하지만 신은 언제나 저희 곁에 있지만 실제로 보이지 않습니다 😢 어쩔수가 없군요. 이 신의 역할을 대체할 가짜 신을 만들어 보겠습니다. 바로 저희의 생성자 Generator $G$ 입니다.

자 문제가 있군요. 우리가 $G$ 를 가짜신으로 만드려면 저 신이 어떻게 latent를 우리가 원하는 데이터로 바꾸는지알아야 하는데, latent의 정체도, GOD의 정체도 모릅니다. 😖

하지만, 저희는 그 결과물을 알고 있습니다. 그럼 이렇게 뽑은 결과물들로부터 신이 어떻게 저렇게 멋들어진 데이터를 뽑아낼 수 있었는지에 대해서 추론해볼 수 있을 것입니다.

이런 방법을 바로 sampling 이라고 합니다. 여러번 sampling 함으로서 원래 분포가 어떤지 추론해보는 것이죠.

자 이렇게 뽑아 놓고 보니 서로 생긴 분포들이 매우 다르게 생겼다는 것을 알수 있습니다. 이런 우리의 가짜신 $G$ 가 영 신노릇을 잘 못하는 것 같네요. 🤢

어쩔 수 없습니다. 그럼 이 두 분포를 최대한 똑같게 만들 수 밖에요.

이 두 분포를 똑같이 만든다면, 우리의 $G$ 는 신처럼 실제 데이터를 생성해 낼 수 있을 것입니다!

Sampling

사실 제가 GAN을 이해하는데 있어서 이 Sampling이 잘 와닿지 않았습니다. 야매로 공부해서그런지 이런쪽 머리가 잘 굴러가지가 않네요. 슬픈일입니다.

GAN에서 sampling을 수행할 때, latent space에서 Normal Distribution으로부터 벡터를 몇개 뽑아와 이를 generator에게 던져줍니다.

네? Normal Distribution으로 부터 뽑는다구요?... 라는 생각을 하게 됩니다. 실제 GAN의 코드를 보면 정말로 random을 호출해서 normal distribution으로 값 N개를 뽑게 됩니다. 어떻게 이런 것이 가능한 걸까요?

아래와 같이 이해하시면 그래도 납득이 가게 됩니다. (제 사견입니다.)

저희가 학습 시키는 Generator는 이 latent space로부터 생성된 latent vector를 던져 주면, 어떤한 이미지(어떤 종류라도 상관없지만요)를 생성하도록 교육 됩니다.

사실 초기에는 latent space는 아무런 의미를 가지지 않는 공간이죠. 즉, 정말로 Normal distribution를 따르는 랜덤한 공간입니다. 하지만 학습을 진행하다보면, Generator가 latent space에서 sampling한 latent vector와 특정 이미지들을 연결시키기 시작합니다. 아 latent vector의 첫번째 원소는 이런의미였겠군, 두번째 원소는 저런 의미였겠군 하구요.

실제로 저희가 Generator에게 latent space를 던져주면서 이런 데이터를 만들어야 해! 하고 계속 훈육(?)을 시키다 보면, Generator로는 정말 완전히 자기맘대로 랜덤한 데이터를 마음대로 뱉는 것이 아니라 latent vector에 따라 다른 이미지를 생성하게 됩니다.

사실 사람도, 아무의미없는 것을 몇번 보면 거기에 의미를 부여하곤 하잖아요? 사람들의 징크스 같은 것들처럼요. 인문학적 겜성 1그램 추가하자면, 되게 사람같은 네트워크들이네요. 😉

Discriminator

자 그럼 어떻게 이 두개의 분포를 똑같이 만들어줄수있을까요? 사실 두 분포는 간단한 숫자나 수식이 아닙니다. 매우 복잡하고, 어떻게 생겼는지조차 그리기 어려운 그런 어려운 분포를 따르고 있겠죠 😢 따라서 if문 한줄이나 가벼운 수식 하나로 쉽게 분류할 수는 없을 것 입니다.

그래서 도입한게 바로 구별자 Discriminator 입니다. Discriminator는 두개의 서로 다른 분포가 얼마나 다른지 학습하고, 그 두개의 분포를 명확히 구분할 수 있는 어떤 종류의 함수라도 올수 있습니다! 하지만, 저희는 마법의 머신러닝이 있으니까요. 이 머신러닝 네트워크를 활용할 것입니다.

Discriminator의 주요한 목적은 두개의 분포를 명확히 구분하는 것입니다. 그 구분한 만큼 Generator에게 벌을 줘야하기 때문이죠. 이미 매우 오래전부터 이렇게 두개의 분포를 명확히 구분시킬 수 있는 유명한 Loss가 있죠. 바로 Binary Cross Entropy Loss 입니다. tensorflow나 pytorch에도 이미 구현되어 있는 loss죠? Cross Entropy의 수식은 아래와 같습니다.

$$BCELoss(P,Q)=-(P(x)\cdot\text{log }Q(x) + (1-P(x)) \cdot \text{log }(1 - Q(x)))$$

Cross Entropy에 대해서 조금 더 알고 싶다면 아래 포스팅을 참고해주세요!

 

 

 

Entropy, Cross Entropy, KL-Divergence에 대해 알아보자

Entropy 엔트로피는 머신러닝에서 정보량을 나타내는 방법으로 사용된다. 정보의 량이라는게 되게 추상적으로 들리는데, 생각해보면 되게 간단한 개념이다. ML상에서 굉장히 많이 사용되는 개념

lifeignite.tistory.com

GAN의 Loss는 위의 Loss를 조금 수정해서 사용합니다. 현재는 CE를 이용해서 두 분포의 차이를 구할 수 있고, 그 분포차이를 바탕으로 Generator를 혼내주면서 $G$의 학습을 돕는다고 생각하시면 좋습니다.

그래도, Loss Function이 어떻게 생겼는지는 구경하고 가야겠죠?

GAN의 Loss를 보고 minmax problem이라는 말들을 많이 합니다. 아이 수식자체에 minmax가 들어가 있으니까요. 이 부분을 자세히 집고 넘어가면 너무 글이 길어지고 어려워질 것 같아서 이쯤에서 다음 포스팅으로 넘기도록 하겠습니다.쬐금 귀찮은 증명들이 사아알짝 들어가 있거든요. 이 Loss들을 조금 자세히 이해하시려면 Cross Entropy (CE)에 대해서 조금 자세한 이해를 필요로 합니다!

학습 알고리즘

아래 직접 작성해서 colab으로 실행할 수 있도록 코드를 공개해 두었습니다. 같이 보시면서 코드의 흐름을 파악하시면 좋습니다.

 

choiking10/ML-tutorial

하나하나 구현을 하는게 아니라 한두가지의 시나리오를 가지고, 여러가지 ML 모델을 적용 혹은 개선시키는 작업을 수행해봐야겠다. - choiking10/ML-tutorial

github.com

소스코드를 보고 있다고 가정하고 글을 작성하도록 할께요.

Overview

자 이제 복잡한 내용은 조금 치우고, 어떻게 학습이 이루어지는지 확인하도록 하죠.

Loss에서 Generator의 역할을 $D$ 가 구별할 수 없을 정도로 데이터를 잘 만드는 것입니다. 그리고 Discriminator의 역할은 $G$ 가 만든 것을 완벽히 가짜라고 구별해 내는 것이죠. 이렇게 GAN은 $G$ 가 잘만들면 $D$ 가 혼나고, $D$ 가 잘구별하면 $G$ 가 혼나는 구조로 동작하게 됩니다. 하지만 $G$ 랑 $D$를 모두 생각하기 매우 버겁지 않나요? 사실 이 둘을 모두 한꺼번에 학습시키란 쉽지 않습니다.

그래서 실제 학습을 시킬 때는 두가지 step을 반복하면서 학습을 수행하게 됩니다. 서로 서로가 고정되어있다는 가정하게 업데이트를 진행하게 되는데, 그에 따라서 Loss Function이 살짝 바뀌게 되는 것도 구경하게 되실 겁니다.

어떻게 따로 따로 업데이트할까요? 간단하게 코드 레벨로 보시고 가실께요.

D = Discriminator(image_size, hidden_size).to(device)
G = Generator(latent_size, hidden_size, image_size).to(device)

criterion = nn.BCELoss()
d_optimizer = torch.optim.Adam(D.parameters(), lr=0.0002)
g_optimizer = torch.optim.Adam(G.parameters(), lr=0.0002)

모델이 두개니까, 각 모델의 파라미터를 뽑은 후 서로 다른 optimizer로 업데이트를 수행하는 모습입니다. 어려운 코드는 아니에요!

optimizer의 종류에 대해서는 다음에 한번더 다뤄보도록 할께요!

Step 1. Fix G, Update D

첫번째는 $G$를 멈춰 놓고 $D$를 업데이트 하는 것입니다.

엇...? G를 멈췄다구요?

그러면 G는 변하지 않는다고 생각해도 되겠군요. 그럼 Loss가 이렇게 변할 수 있습니다.

G는 상수니까 골치 아픈 G들을 치환해버렸습니다.

z로부터 뽑는게 아니라 이제 G로부터 뽑는걸로 간단하게 생각해서 뽑으면 되겠군요.

사실 이부분은 소스코드에 영향을 주지 않지만, Loss Function이 이렇게 변할 수 있다는 사실을 이해하고 넘어가면 좋습니다.

소스코드로도 보고 가실께요.

# ---- D ----
z = rand_z()

# real
real_outputs = D(images)
d_loss_real = binary_cross_entropy(real_outputs, real_label)
real_score = real_outputs

# fake
fake_images = G(z)
fake_outputs = D(fake_images)
d_loss_fake = binary_cross_entropy(fake_outputs, fake_label)
fake_score = fake_outputs

# loss
d_loss = d_loss_real + d_loss_fake

# backprop
reset_grad()
d_loss.backward()
d_optimizer.step()

제가 위에서 데이터를 섞어서 D에게 보여준다고 했었죠?

사실 D 입장에서는 섞여서 들어오는지 한꺼번에 들어오는지 번갈아가면서 들어오는지 이런 들어오는 것에 대한 패턴을 알기 어려울 것입니다.

weight를 업데이트하지 않았거든요 아직! 업데이트 하기전까지 넣어준건 한꺼번에 넣어준 것이라고 봐도 무방합니다. (라고 이해했습니다 헷)

# real
real_outputs = D(images)
d_loss_real = binary_cross_entropy(real_outputs, real_label)
real_score = real_outputs

저 images 변수에는 실제 데이터셋으로부터 뽑은 이미지들이 들어있습니다.

실제 데이터셋에서 뽑아서 D가 실제 데이터가 실제 데이터라고 (1이라고) 판단했는지 검사합니다. BCE loss가 쓰인 것을 볼 수 있군요. real_outputs에는 모두 1로된 데이터가 들어가 있습니다. (GT라고 생각하셔도 될것같네요!)

z = rand_z()

# real
# ...

# fake
fake_images = G(z)
fake_outputs = D(fake_images)
d_loss_fake = binary_cross_entropy(fake_outputs, fake_label)
fake_score = fake_outputs

latent space에서 latent vector z를 뽑고 G에게 넘겨줍니다. 그럼 G는 fake images들을 생성할 것이고, 이를 D에 넘겨주죠. 수식으로 보자면 $D(G(z))$ 에 해당하겠군요. BCE loss가 똑같이 쓰인 것을 볼 수 있군요. fake_outputs에는 모두 0로된 데이터가 들어가 있습니다.

# loss
d_loss = d_loss_real + d_loss_fake

# backprop
reset_grad()
d_loss.backward()
d_optimizer.step()

전체 Loss는 fake일때와 real일때의 loss값을 d_loss에 더해주고 backward를 수행해줍니다. 아직 모델의 파라미터에는 업데이트가 이루어지지 않았습니다!

그리고 d_optimizer.step() 을 함으로서 discriminator의 파라미터만 업데이트가 되었군요.

Step 2. Fix D, Update G

자 마찬가지로 D를 멈춰두고 G를 업데이트 해 봅시다. Step 1처럼 Loss 에 변화가 있을 거에요.

뭔가 커다란 것이 없어 졌습니다. 바로 $\mathbb{E}{x \sim P{\text {data }}(x)}[\log D(x)]$ 자체가 날아가버렸네요. Gradient를 계산하는 행위는 미분과정을 통해 이루어집니다. 그리고 고등학교 때 달달외웠던 미분의 가장 기본적인 성질은 상수는 날아가버린다는 거죠.

그리고 우리는 D를 고정했고, G가 없는 모든 항들은 상수 취급됩니다. 그러니까 굳이 계산할 필요가 없다는 거죠! 코드를 Step1 보다는 조금더 간단하게 작성할 수 있겠네요.

# ---- G ----
z = rand_z()
fake_images = G(z)
outputs = D(fake_images)
g_loss = binary_cross_entropy(outputs, real_label)

reset_grad()
g_loss.backward()
g_optimizer.step()

첫번째 항이 날아가버렸으므로, real data 자체를 D에 넣을 필요가 없어집니다. 그 외에는 Step 1과 같게 되겠습니다.

역시 눈에 띄는건 g_optimizer를 통해서 generator의 gradient만 업데이트 해주었다는 사실입니다.

마무리

한줄 정리

latent space에서 sampling 한 z로부터 Generator는 어떠한 데이터 분포를 만듭니다. 그리고 이렇게 생성한 Generator의 분포와 실제 데이터 분포를 Discriminator로부터 비교하게 함으로서 G가 잘생성하면 D를 혼내주고, D가 잘 구분하면 G를 혼내주는 방법으로 학습을 수행합니다.

사족

휴, 드디어 벼루고 벼뤘던 GAN의 시작을 끊게되었네요.

GAN은 크게 4부작으로 작성될 예정입니다.

다음 글 링크는 아래에 있습니다. 

 

 

[논문정리] GAN에 대해서 알아보자 (2) - 증명과 한계 그리고 WGAN의 등장

자 다시 GAN 포스팅을 작성할 시간이네요. 아 GAN은 쓸게 많아서 너무 괴로워요... PS쪽이 확실히 더 작성하기 편하네요. 익숙한 개념이라서 그런걸까요? 오늘 알아볼 내용은 GAN의 증명과 한계 그리

lifeignite.tistory.com

하나하나가 정말 작성하기 매우매우 귀찮군요.

다음부터는 주제를 좀 쓰기 쉬운걸로 작성해야겠어요. ㅎㅎ...

Reference

노션으로 보시면 더 편할 수 있습니다.

www.notion.so/AdaIN-Arbitrary-Style-Transfer-in-Real-Time-With-Adaptive-Instance-Normalization-9fe8b5fb60154380b6fbe3147e0afe9e

 

[논문 정리] AdaIN (Arbitrary Style Transfer in Real-Time With Adaptive Instance Normalization)

어떤 사람이 이 글을 보는게 좋은가?

www.notion.so

어떤 사람이 이 글을 보는게 좋은가?

  • 기본적인 ML지식을 갖추고 있으신 분
  • Style Transfer에 관심이 있으나, 이제 막 시작해서 알고싶으신 분
  • AdaIN의 수식에 대해서 궁금하신 분
  • AdaIN 소스코드의 간단한 리뷰가 궁금하신 분

요약

최초의 Neural Style Transfer를 제안한 Gatys의 Style Transfer 방식은 다양한 Style을 Arbitrary하게(그 때 그 때 새로운 스타일을) 적용할 수 있는 반면에, 굉장히 느린 속도로 style transfer를 수행한다는 단점이 있었다.

이를 극복하기 위해 Feed-forward 방식으로 Style Transfer를 수행하는 방식들이 제안되었는데, 이들은 Gatys 방식의 비해서 빠른속도의 Style Transfer가 가능했으나, 한정적인 몇가지 미리 학습된 Style 에대해서만 Style Transfer가 가능했다.

이에 비해 AdaIN 방식은 빠른속도로 추론이 가능하면서 동시에 Arbitrary하게 새로운 스타일을 적용할 수 있는 방식이다.

위 표의 Method를 설명해보자면 Gatys - 최초 논문, Ulyanov - Instance Normalization(IN), Dumoulin - Conditional Instance Normalization(CIN), our - Adaptive Instance Normalization (AdaIN) 이다.

실험 결과를 보면 Gatys는 많은 스타일에 대해서 적용할 수 있지만, 속도가 굉장히 느린 것을 확인할 수 있다. 이에 비하여 Feed-forward방식의 IN 과 CIN은 빠른속도의 추론이 가능했지만, 스타일이 한정적이다. AdaIN은 이러한 방식들과는 차별화되게 빠른 속도의 추론이 가능하면서 동시에 무한한 스타일을 생성해 낼 수 있다는 장점을 가지고 있다.

Architecture

AdaIN의 네트워크 구조는 아래와 같으며, 인상 깊은 점은 녹색의 VGG의 pre-trained 모델을 통해서 Encoding을 수행하며, 이 encoder를 feature를 인코딩할 때, 그리고 Loss Function을 구할 때 사용한다는 것이다. 즉 Encoder는 학습 시키지 않는다는 점이 포인트다.

그러므로, 자연스럽게 이 네트워크 상에서 학습 시키는 것은 Decoder 뿐이며, 저자들의 표현을 빌리자면 이 Decoder는 AdaIN으로 생성된 feature들이 decoder를 통해서 image space로 invert 하는 법을 학습한다. 아직 설명하지 않았지만, AdaIN 내에서는 learnable parameter가 없다.

AdaIN Layer

그렇다면 AdaIN은 어떻게 생겼는가? 이를 알기 앞서서 Style Transfer의 개념에 대해서 간단하게 알고 있어야 한다. Style Transfer는 특정 이미지에서 Style을 뽑고, 다른 이미지에서 Contents를 뽑아서 이를 합성한다.

Style은 직관적으로 와닿는데 Contents는 무엇인지 잘 이해가 안갈 것이다. 간단하게 어떤 형태라고 생각하면 좋다. 나무의자가 있다면, 의자가 Contents, 나무가 Style이 될 것이다.

AdaIN에서는 Style과 Contents에 대한 정보를 VGG Encoder를 통해서 추출할 수 있다고 주장한다 사실 이러한 아이디어는 초기 연구인 Gatys 때부터 사용해왔었다.

아주 조금만 더 깊게 들어가서...초기 연구인 Gatys 방식에서는 VGG에서 나온 Feature들에 Gram Matrix를 사용해서 Style을 표현(representation) 있다는 것을 실험적으로 보였다. 이러한 Gram Matrix가 대표적인 feature space 상의 statistics를 추출해내는 방법인데, 이 이후 많은 연구가 이루어지면서 feature space상의 여러 statistics가 Style을 표현하는데 유용하다는 것이 실험적으로 많이 밝혀졌다.

statistics? 잘모르겠다고 생각한다면 이 논문에서는 그냥 평균(mean)과 분산(variance)라고 생각해도 무방하다. AdaIN은 feature space 상의 평균과 분산이 Style에 영향을 끼친다면, 이들을 뽑아서 즉석으로 교환해주는 방식을 택한 것이다. 식을 보자면 아래와 같다.

$$\operatorname{AdaIN}(x, y)=\sigma(y)\left(\frac{x-\mu(x)}{\sigma(x)}\right)+\mu(y)$$

이 식에서 $\mu$ 함수는 평균을 구하는 함수이고, $\sigma$ 함수는 표준 편차를 구하는 함수이다. 자주 사용되는 term이다.

Style Transfer의 경우, 내가 원하는 Contents를 담고 있는 이미지의 feature $x$ 에서, 이미지의 스타일을 빼주고, 내가 입히고 싶은 Style을 더해주는 방식으로 수행된다.

그런데, 위에서도 말했 듯 스타일은 feature 상에서의 statistics로 표현된다고 말했는데 그게 바로 평균과 분산이다. 따라서 식에서 보자면 $\left(\frac{x-\mu(x)}{\sigma(x)}\right)$ 는 Contents 이미지에서 Contents 이미지의 스타일을 빼준 것이고, $\sigma(y)\left(\frac{x-\mu(x)}{\sigma(x)}\right)+\mu(y)$ 는 이미지 y의 스타일을 입혀준 것이다. 이해가 됐으려나...

그리고 했깔릴 수 있는 부분인데, 이건 전부 Feature space상에서 이루어진다는 것이다! 그냥 이미지상에서 이루어지는 것이 아니다.

Formulation

노테이션을 정리해보자면

  • $T$= Style Transfer Network (인코더-AdaIN-Decoder)
  • $f$= Encoder (pre-trained VGG-19의 앞부분 (~relu4_1)
  • $g$= Decoder (학습시켜야하는 디코터)

라고 할 때, AdaIN layer를 통해서 생성되는 feature $t$ 는 아래와 같이 나타낼 수 있다.

$$t=\operatorname{AdaIN}(f(c), f(s))$$

랜덤하게 초기화된 디코더 $g$는 $t$를 image space로 보내는 방법을 트레인하며 스타일이 입혀진 이미지 $T(c,s)$를 생성한다.

$$T(c, s)=g(t)$$

Architecture Detail

  • checker-board effect를 감소시키기 위하여 decoder의 pooling layer를 nearest up-sampling 방식으로 교체.
  • $f$와 $g$에서 모두 reflection padding을 사용했다.
  • decoder에서 normalization 방식을 고르는 것이 중요했는데, 결론은 no normalization이 제일 좋았다.
  • 전처리: 두 이미지를 aspect ratio를 유지한체 512로 사이즈를 키웠고, 여기서 256 by 256로 crop한다. 우리의 네트워크는 fully convolutional 이기 때문에, 어떤 사이즈의 이미지가 온다고해도 적용 가능하다.

Loss

Loss는 다른 네트워크와 유사하게 다음과같은 로스를 사용한다.

$$\mathcal{L}=\mathcal{L}_{c}+\lambda \mathcal{L}_{s}$$

content loss는 target feature와 output image의 feature의 Euclidean distance를 구했다. 일반적으로 사용되는 content image의 feature response를 사용하는 대신에 AdaIN output $t$를 content target으로 삼았다. 이게 조금더 빨리 convergence가 이루어진다.

$$\mathcal{L}_{c}=\parallel f(g(t))-t \parallel_{2}$$

그냥 전체 아키텍처 오버뷰의 보라색 화살표의 식이다. $t$를 디코더에 넣고 다시 인코더에 넣은 후에 그 두개의 차이를 비교하는 것을 Contents Loss로 삼겠다는 간단한 식이다.

AdaIN 레이어는 오직 style features의 mean과 standard deviation를 transfer하기 때문에 style loss는 이러한 statistics를 match시켜야한다. 따라서 아래와 같은 스타일 로스를 사용하는데,

$$\begin{array}{r}\mathcal{L}_{s}=\sum_{i=1}^{L}\left\|\mu\left(\phi_{i}(g(t))\right)-\mu\left(\phi_{i}(s)\right)\right\|_{2}+ \\\sum_{i=1}^{L}\left\|\sigma\left(\phi_{i}(g(t))\right)-\sigma\left(\phi_{i}(s)\right)\right\|_{2}\end{array}$$

$\phi_i$.는 VGG-19의 i번째 레이어이다. 스타일로스에서 사용한 레이어는 relu1 1, relu2 1, relu3 1, relu4 1 이다. 이 역시 간단하게 설명하자면, 원래 스타일 $s$ 를 인코더에 넣었을 때의 $i$ 번째 feature $\phi_i(s)$ 의 평균과 $t$ 를 디코더에 넣고 이를 다시 encoder에 넣었을 때의 $i$번째 Feature $\phi_i(g(t))$ 의 평균($\mu$)과 표준편차($\sigma$)를 최소화 시키는 방법으로 스타일 로스를 구한것이다.

Code

사실 이렇게 길게 설명했는데, 코드로는 아래와같이 씸플하다.

def adaptive_instance_normalization(content_feat, style_feat):
    assert (content_feat.size()[:2] == style_feat.size()[:2])
    size = content_feat.size()
    style_mean, style_std = calc_mean_std(style_feat)
    content_mean, content_std = calc_mean_std(content_feat)

    normalized_feat = (content_feat - content_mean.expand(
        size)) / content_std.expand(size)
    return normalized_feat * style_std.expand(size) + style_mean.expand(size)

AdaIN의 코드는 그냥 content_feature와 style feature를 수식 그대로 적용해 반환할 뿐인 함수다.

def forward(self, content, style, alpha=1.0):
        assert 0 <= alpha <= 1
        style_feats = self.encode_with_intermediate(style)
        content_feat = self.encode(content)
        t = adain(content_feat, style_feats[-1])
        t = alpha * t + (1 - alpha) * content_feat

        g_t = self.decoder(t)
        g_t_feats = self.encode_with_intermediate(g_t)

        loss_c = self.calc_content_loss(g_t_feats[-1], t)
        loss_s = self.calc_style_loss(g_t_feats[0], style_feats[0])
        for i in range(1, 4):
            loss_s += self.calc_style_loss(g_t_feats[i], style_feats[i])
        return loss_c, loss_s

encode_with_intermediate 함수는 중간중간 레이어를 추출해서 적용하는 함수고, encode 함수는 그야말로 vgg를 relu4_1까지 통화시킨 후 feature map을 반환하는 함수다.

주목할만한 점은 adain에 style_feature의 마지막 것만 들어가는건데, 생각해보면 당연하다. shape이 마지막껏만 content feature와 맞기 때문이다. 그리고 이래도 style의 mean과 variance를 adaIN을 통해 transfer할 수 있다.

그리고 생성된 $t$ 를 디코더를 통해 이미지로 만들고, 이를 다시 encoder에 집어넣어서 contents loss와 style loss를 구해서 반환해준다.

논문의 전체 번역본과 소스코드 주소는 아래와 같다.

[번역] Arbitrary Style Transfer in Real-Time With Adaptive Instance Normalization

naoto0804/pytorch-AdaIN

notion으로 보면 더 편합니다.

www.notion.so/Batch-Normalization-0649da054353471397e97296d6564298

 

Batch Normalization

Summary

www.notion.so

목차 

  • Summary
  • Introduction
  • Background
    • Normalization
    • Covariate Shift
  • Batch Normalization
    • Algorithm
    • Learnable Parameter gamma, beta
    • Inference with Batch Normalization
      • 모집단 추정방식
      • Moving average 방식
  • Undertstanding Batch Normalization
    • BN이 ICS를 제거하는가?
    • 왜 성능이 좋은가?
      • Optimization Landscape
      • Lipschitz
  • 사족들
  • Reference

 

Summary

  • 역할
    • 각 레이어의 입력 feature의 분포를 re-centering & re-scaling
  • 장점 → 경험적으로 높은 성능을 낸다고 널리 알려져 있음 & 이유에 대해서는 아직도 연구 진행중
    • Faster training (learning rate 를 더 높게 설정할 수 있음)
    • Stable training (less vanishing & exploding gradients)
    • Regularization (less overfitting, more generalized)
  • 단점
    • 큰 mini-batch size 필요 → Mini-batch가 전체 dataset과 비슷한 분포를 가져야함.
    • RNN에서 사용 불가능
  • 잘 동작하는 이유
    1. 아마도 Internal covariate shift를 제거했기 때문이다.
      • 2018 논문: ICS때문은 아니다! 실험적, 수학적으로 증명
    2. Objective function의 solution space를 smoothing 했기 때문이다.
      • ㄱㄱ

Introduction

머신러닝에서 Batch Normalization은 빼놓을 수 없는 주제 중 하나이다. 많은 네트워크들이 관습적으로 Batch Normalization을 자신들의 연구에 끼얹어 보고는 하는데 오늘은 이 Batch Normalization에 대해서 알아보고, 그와 관련된 개념들을 정리해보고자 한다.

Batch Normalization (BN)은 처음 2015년도에 네트워크상의 Internal covariate shift(ICS)를 제거하기 위한 목적으로 제안되었다. 거의 대부분의 네트워크에 BN을 추가했을 때, 추가하지않았을 때보다 굉장히 빠른속도로, 안정적이게 학습을 수행할수 있다는 사실이 실험적으로 밝혀졌었다. 하지만 2018년도에 그 이유가 ICS를 제거했기 때문이 아니라 그저 solution space를 smoothing 했기 때문이라는 주장이 제안되었고, 실험적, 수학적으로 이것이 사실이라고 밝혀졌다.

BN은 크게 3가지 장점이 있다.

  • 네트워크를 vashing & exploding gradient를 감소시키고 안정적으로 학습시킬 수 있도록 도와준다.
  • 또한, overfitting을 감소시키며 네트워크가 더 쉽게 generalize 할수 있다. 즉, Reglarization 효과를 가진다.
  • 이 덕분에 높은 learning rate로 학습을 시킬 수 있으며, 이는 학습을 빠르게 convergence에 이를 수 있게 만들어준다.
  • 하지만, 이러한 BN에도 몇가지 단점들이 있다.
  • 일반적으로 머신러닝을 수행할 때, 한꺼번에 데이터셋 전체를 학습 시킬 수 없어서 작은 mini-batch단위로 학습을 시킨다. BN은 mini-batch 단위로 Normalization을 수행하기 때문에 mini-batch size가 성능에 영향을 준다. 구체적으로는 큰 mini-batch ize를 필요로 한다. 그 이유는 Mini-batch가 전체 dataset와 비슷한 분포를 가져야 하기 때문이다. (어쨌든 mini-batch도 전체 데이터셋에서 sampling 하는 것이니까 mini-batch에도 먹힌다.)
  • 몇가지 네트워크에서는 BN을 사용하기 어렵다. 대표적인 예로 RNN와 같은 sequental 데이터를 처리하는 경우를 들 수 있다.

Background

Normalization

Batch Normalization은 이름에서부터 그렇듯 학습을 수행할 때 Normalization을 수행하는 방식이다. 때문에 이러한 Normalization과 관련된 내용을 알아보고자 한다.

데이터 전처리 중 Normalization의 목적은 모든 Feature들이 동일한 scale을 가지도록 하는 것을 말한다. 여기서 여러가지 Normalization 방식들이 있지만, 가장 유명한 Normalization 방식은 Min-MaxStandardization이다.

여기서 머신러닝에서 주로 사용되는 Normalization 개념은 Standardization이다.

Standardization

Standardization의 경우, 처리하고자하는 데이터상의 mean과 standard deviation을 구하고, 각 feature data에서 mean을 빼고, standard deviation을 나눠주는 방식으로 shift and scaling을 수행한다.

참고로 머신러닝상에서는 일반적으로 mean와 standard deviation 함수는 각각 $\mu$, $\sigma$로 주로 표현되면, 따라서 새로운 데이터 $x'$는 아래의 식으로 구해진다.

$$x' = \frac{x-\mu(x)}{\sigma(x)}$$

Standardization을 왜 shift and scaling이라고 하는가?

아래의 그림을 보면 직관적으로 알 수 있다. 원본의 데이터 분포가 아래와 같다고 했을 때, $\mu(x)$를 각 데이터에 대해서 빼주었을 때, 아래의 그림의 zero-centered data가 되는데 그 모습이 데이터 전체가 mean을 중심으로 하도록 이동시킨것과 같기 때문에 shift 라고 말한다. scaling은 이 데이터에 대해서 표준 편차로 나눠주게 되는데, 이를 통해서 전체의 퍼져있는 정도를 각 feature (가로, 세로)마다 유사하도록 맞춰주기 때문이다.

Whitening

이러한 Standardization 방식보다 더 좋다고 알려진것이 whitening이다. whiteing은 아래의 두가지를 만족시키도록 데이터를 변환시켜준다.

  • PCA를 통해서 각 feature들을 재가공한다. 이를 통해서 Feature간의 correlation을 감소시킨다. 즉, 중복(redundant)되는 정보를 제거한다.
    • Decorrelated data
  • 모든 Feature들이 동일한 scale을 가지도록 한다. (Standadization와 같다)
    • whitened data

PCA는 주성분분석이라고해서 각 성분을 분석해서 성분간 중복을 제거해줄 수있는 기술이다. 그리고 이러한 주성분분석은 굉장히 많은 연산량을 요구하는데, 그 때문에 높은 차원, 그리고 많은양의 데이터에 대해서 이러한 주성분 분석을 수행하기에 많은 어려움이 따른다.

그개념을 여기서 설명하기에는 너무 길기 때문에 언젠가 시간이나면 포스팅하도록 하겠다.

PCA는 데이터의 경향성을 찾아서, 새로운 축을 만들어주는 것과 같이 동작하는데, 중간의 그래프가 어떻게 변하는지 위의 standardization과 비교해서 보면 그 차이점을 볼 수 있을 것이다.

 

Whitening은 여러 논문에서 중간중간에 한번씩 끼얹어보는 방식으로 쓰이는걸 많이 보았다. 머신러닝의 성능향상에 영향을 미친다는 증거로 볼 수 있는데, 여기에 대해서는 완벽히 하는 것이 아니라 다음에 더 조사해보고 추가 포스팅을 하겠다.

Covariate Shift

Corvariate Shift는 대표적으로 어떤 데이터셋을 뽑았을 때, Train 데이터의 분포와 Test 데이터의 분포가 달라서 발생하는 문제이다.

 

이 그래프를 보자. 실제 데이터의 분포가 빨간색그래프인 True function을 따른다. 그런데 만약 우리가 Train Dataset을 sampling 할 때 파란색 점처럼 샘플링을 했다고 해보자. 그러면 네트워크를 녹색의 Learned function처럼 학습을 수행할 것이다. 즉, 실제는 빨간색 그래프인데, 학습된 모델은 녹색그래프처럼 나와서 검은색 데이터, Test Dataset을 제대로 예측하지 못하게 된다. 이를 다르게 말하자면 Train Dataset과 Test Dataset의 분포가 서로 달랐기 때문에 발생한 문제이다.

  • When the distribution of the inputs used as predictors (독립변수, 예측변수, covariates) changes between training and production stages

Internal Covariate Shift(ICS)는 이러한 Covariate Shift가 Multi layers network에서 각 internal layer들 사이에서도 발생한다는 것이다. 다시말해보자면, 각 layer에서 받아들이는 input의 분포가 학습 과정중 계속 변함으로 input으로 이전과 전혀다른 분포가 들어왔을 때, 잘 학습을 못하게 만들 수 있다는 것이다.

 

layer 1의 output은 layer2의 인풋이 된다. layer2의 아웃풋은 layer3의 인풋이 된다. multi layers network의 경우 위와같이 한 레이어들의 output들이 다른 레이어의 인풋으로 들어가게 된다. 이 때, 학습 과정에서 layer2의 분포가 갑자기 이전과 다른 형태의 분포로 변한다면 layer3는 전혀 다른 분포가 들어왔기 때문에 마치 covariate shift의 결과처럼 네트워크가 정확한 True Function을 예측하는데 방해가 될 것이라고 생각한 것이다.

Purposed Method (Batch Normalization)

이러한 ICS를 제거하기 위해서 각 레이어를 거칠 때마다 데이터의 분포를 standardization 시켜주는 Batch Normalization을 제안했다.

Algorithm

 

저 파랑색 화살표가 바로 Batch Normalization이다. 이러한 Batch Normalization은 Activation 을 거치기 전에 적용되게 되는데, 논문에서는 아래와 같은 알고리즘으로 기술되어 있다.

 

간단히 알고리즘을 풀어보잠녀 어떤 mini-batch의 사이즈가 m인 데이터 $x$들을 받았을 때,

mini-batch의 mean을 구하고,

$$\mu_{\mathcal{B}} \leftarrow \frac{1}{m} \sum_{i=1}^{m} x_{i}$$

variance를 구해서

$$\sigma_{\mathcal{B}}^{2} \leftarrow \frac{1}{m} \sum_{i=1}^{m}\left(x_{i}-\mu_{\mathcal{B}}\right)^{2}$$

normalization 해준다.

$$\widehat{x}_{i} \leftarrow \frac{x_{i}-\mu_{\mathcal{B}}}{\sqrt{\sigma_{\mathcal{B}}^{2}+\epsilon}}$$

그런데 저기 이상한게 들어가 있다. 바로 $\epsilon$이거 . 이건 그냥 아주 작은 값을 가지는 상수라고 보면 된다. 왜냐면 표준편차가 0이 되면 무한대로가버리기 떄문에 이를 방지하기 위해서 저렇게 입실론을 더해주는 방식으로 구현한다.

그런데 그다음에 이상한 게 하나 더있다. 바로 learned parameter $\gamma, \beta$

Learnable parameter $\gamma, \beta$

BN은 Activation Layer 이전에 위치한다. 때문에 Normalization을 수행함으로서 Activation Layer의 non-linearity를 감소시킬 수 있다는 우려가 있다.

 

Normalizaation을 거치고 그대로 Actication layer (tanh)의 input으로 사용할 경우, 정의역이 대부분 빨간색 점선 박스 쪽으로 이동할 것이다. 그리고 그래프에서 보면 알겠지만 해당 정의역에서의 Activation Function Graph의 모양이 매우 linear한 성질을 띄는데 이럴 경우 non-linearity가 떨어진다는 문제가 발생하는 것이다. (이게 왜문제인지 모른다면? ⇒ activation 파트를 다시 공부하고 오자. 이것도 곧 포스팅할 예정...)

따라서 Non-linearity를 높이기 위해서 데이터의 분포를 적절하게 scale & shift를 시킨다.

$$y_{i} \leftarrow \gamma \widehat{x}_{i}+\beta \equiv \mathrm{B} \mathrm{N}_{\gamma, \beta}\left(x_{i}\right)$$

scale 해주는 것이 $\gamma$이고, shifting 해주는 것이 $\beta$다.

Inference with Batch Normalization

이제 학습을 완료했다. 그런데 문제가 하나가 있는게, 학습이 끝난 시점, 즉 production 시점에서 더이상 mini-batch의 mean & standard deviation 을 쓸 수 없다는 것이다. (학습할 때 무엇으로 평균을 쓸 것인가?...)

따라서 mini-batch의 mean & standard deviation을 대체할 방법이 필요한데 이 때 크게 2가지 방식이 있는데, 둘 모두 Training set의 mean & standard deviation을 사용해서 유도한다.

모집단 추정방식

고등학생때 한번쯤 해봤을 것이다! 신승범 선생님께서 (m-1)이 그냥 좋다고 했던 기억이 있다...

$$E\left[x^{(k)}\right]=E_{B}\left[\mu_{B}^{(k)}\right]$$

$$\operatorname{Var}\left[x^{(k)}\right]=\frac{m}{m-1} E_{B}\left[\sigma_{B}^{(k)^{2}}\right]$$

Moving average 방식

$$\begin{array}{l}\hat{\mu} \leftarrow \alpha \hat{\mu}+(1-\alpha) \mu_{\mathcal{B}}^{(i)} \\hat{\sigma} \leftarrow \alpha \hat{\sigma}+(1-\alpha) \sigma_{\mathcal{B}}^{(i)}\end{array}$$

디테일한 설명은 생략하겠다. 모집단 추정방식은 학습 시에 평균과 분산들을 모두 기억하고 있어야 하기 때문에 비효율적이고, 주로 Moving average 방식으로 구현된다고 한다.

시간이 나면 한번 다뤄보도록하겠다. 그런데 날지모르겠네...

Understanding Batch Normalization

이 이후부터는 How Does Batch Normalization Help Optimization 논문의 내용이다.

일단 확실히 BN이 성능하나는 훌륭하다. 하지만 여전히 BN paper의 ICS가 제거되었는지 명확한 실험 및 수학적 설명이 부족하다는 것이다. 그리고 아래와 같은 결론을 내었다.

  • BN은 ICS를 제거하지 않는다.
  • ICS가 있다고 하더라도 학습에 나쁜영향을 주지 않는다.
  • BN이 성능이 좋은 이유는 solution space를 smoothing 했기 때문이고, 이는 다른 방식으로도 같은 효과를 얻을 수 있다.
    • 대표적인 다른방식에는 dropout과 같은 방법들이 있으며, regluarization 등도 이에 해당될 것이다.

BN이 ICS를 제거하는가?

저자는 VGG네트워크를 VGG without BN , VGG + BN , VGG + BN + Noise 로 나누어 실험을 수행한다.

 

그래프가 난해하다. depth 축은 학습의 진행도(step? 명시안되어있는 것 같다 time이라고 써져있던데)이고, 하나의 슬라이스가 분포도를 나타내는 것으로 보인다. 여기서 주목할만한 것은 BN을 적용시킨 것이 더 ICS가 높다는 것이다. (각 스텝별로 데이터의 분포가 확실히 다르다. 즉 ICS가 발생한 것이다) 그럼에도 불구하고 아래와 같은 실험 결과가 나왔다.

 

VGG를 그냥 사용한 것이 가장 성능이 안좋았으며, VGG+BN+Noise가 그다음으로 좋았고, 역시 VGG+BN을 사용한것이 가장 좋았다. 주목할만한 것은 Noise를 추가해서 ICS를 추가적으로 일으켰음에도 불구하고, BN없이 사용한 VGG보다 성능이 좋았으며, 이는 ICS가 학습의 성능에 큰영향을 주지 못한다는 반증이다.

그렇다면 왜 성능이 좋은가?

Optimization landscape

바로 solution space를 smoothing 하기 때문이라는 것이다.

BN은 Loss function의 Lipschitzness를 impove하는데, 이는 Gradient가 stable하게 감소할수 있도록 만들어 준다. 즉 high learning rate에서도 stsable하게 learning하도록 만들어준다. 즉 soulution space를 아래와 같이 smoothing한다!

 

Lipschitz

Lipschitz 함수란 두 점 사이의 거리가 일정비 이상으로 증가하지 않음. 즉 미분 계수가 상수 K를 넘지 않는 함수다.

인데, Lipschitness를 개선했다는 것은 gradient가 급격히 커지는 것을 방지할수 있다는 것이다.

$$\left|\frac{f(x)-f(y)}{x-y}\right|<=K$$

 

위 처럼 loss가 안정적으로 줄어들고, Gradient predictiveness또한 좋아졌다. (gradient의 방향성? 정도? 그런의미인듯 정확히는 모르겟따)

수학수학한건 다루지 않는다.

 

사족들

일반적으로 tensorflow의 shape은 BHWC, pytorch는 BCHW다. B는 배치 C는 채널 HW는 Height와 Width다. 이를 수식으로 명식적으로 나타내면

$$\mu_{c}(x)=\frac{1}{N H W} \sum_{n=1}^{N} \sum_{h=1}^{H} \sum_{w=1}^{W} x_{n c h w}$$

$$\sigma_{c}(x)=\sqrt{\frac{1}{N H W} \sum_{n=1}^{N} \sum_{h=1}^{H} \sum_{w=1}^{W}\left(x_{n c h w}-\mu_{c}(x)\right)^{2}+\epsilon}$$

 

$$\mathrm{BN}(x)=\gamma\left(\frac{x-\mu(x)}{\sigma(x)}\right)+\beta$$

이렇게 될 것이다. 보면 group-by C인것을 알수있다. 왜 group by C인가? 에대해서 한번쯤 생각해보는 시간이 있었는데, 사실좀만 생각해보면 당연한 것이다.

우리가 채널이라고 부르는 것은 사실 서로다른 feature들이다. 매우 일반적인 경우 0번 채널(feature)는 키, 1번 채널(feature)는 몸무게다. 그럼 당연히 normalization을 수행할 때, 키는 키끼리, 몸무게는 몸무게 끼리 normalization을 수행해야한다. 당연히 채널로 묶어서 normalization을 수행해야하는것이고, BN도 channel로 묶어서 normalization을 수행하는 것이다.

진짜 끝 

Reference

Andrew Ng님의 강의자료

Batch Normalization: Accelerating Deep Network Training by Reducing Internal Covariate Shift

How Does Batch Normalization Help Optimization?

'ML > 기초 이론' 카테고리의 다른 글

Entropy, Cross Entropy, KL-Divergence에 대해 알아보자  (5) 2021.02.06

 

논문 정리본은 아래에서 확인하실 수있습니다. 본 포스팅은 단순 번역으로 직역한 내용들을 담았습니다.

 

 

[논문 정리] AdaIN을 제대로 이해해보자

노션으로 보시면 더 편할 수 있습니다. www.notion.so/AdaIN-Arbitrary-Style-Transfer-in-Real-Time-With-Adaptive-Instance-Normalization-9fe8b5fb60154380b6fbe3147e0afe9e [논문 정리] AdaIN (Arbitrary Styl..

lifeignite.tistory.com

Notion으로 보시면 더 편합니다.

www.notion.so/Arbitrary-Style-Transfer-in-Real-Time-With-Adaptive-Instance-Normalization-5f8d9aba82ea4a83a797a48371f6cecb

 

Arbitrary Style Transfer in Real-Time With Adaptive Instance Normalization

Abstract

www.notion.so

 

Abstract

Gatys는 content image를 다른 이미지의 스타일로 rendering하는 알고리즘을 개발했다. 그리고 이를 style transfer라고 부른다. 하지만, gatys의 framework는 느린 반복적 최적화 과정을 요구하기 때문에 현실적인 적용이 힘들었다. 이에 FFNN을 통해서 빠른 속도로 이에 근접한 성능을 내는 Neural style transfer 알고리즘이 제안되었다. 하지만, 이러한 speed의 증가는 style의 종류를 한정시키고, 각각의 새로운 스타일에 독립적으로 adapt 시키기 어려웠다. 이에 우리는 간단하지만, 효율적인 독립적인 실시간 style transfer를 제안한다. 우리 방식의 key는 AdaIN layer인데, 이 adaIN 레이어는 contents feature의 mean과 variance를 style features와 align한다. 이 방식은 매우 빠른 속도의 추론을 가능하게하며, 동시에 pre-defined된 style-set의 제약을 없앴다. 추가적으로 우리의 접근법은 content-style trade-off, style interpolation, color & spatial controls 등의 유연한 user control을 하나의 FFNN을 통해 가능하게 한다.

Introduction

이미지는 style과 contents로 어느정도 분리할 수 있다. 때문에 이미지의 contents를 유지한체 style을 바꿀수 있는데, 이를 우린 style transfer라고 부른다.

대충 gatys 방식의 한계 - 느림

대충 기존 FFNN 방식의 한계 - 스타일이 한정됨

우리의 접근방식은 새로운 스타일은 실시간으로 독립적인 transfer를 수행할 수 있다. gatys 방식(최적화기반)의 유연성과 feed-forward 방식 (FFNN 방식)와 유사한 속도를 결합해서. 우리의 방식은 Instance Normalization (IN) 방식에서 movitation을 받았는데, IN방식은 NN style transfer상에서 놀랍도록 효율적이다.

instance noramlization 의 성공을 설명하기 위해서, 우리는 새로운 해석을 제안하는데, 그것은IN이 feature statistics를 정규화함으로서 style normalization을 수행한다는 것이다. 이는 feature statistics를 정규화 하는것이 style information을 유도할 수 있다는 기존의 연구들로 비롯된 것이다. 이러한 우리의 해석에 motivation을 얻어서, 우리는 IN을 간단하게 확장한 AdaIN을 제안한다.

AdaIN은 Contents input과 Style input이 주어졌을 때, 간단하게 content input의 mean과 variance를 style input의 mean과 variance와 match되도록 조정한다. 전체적인 실험해서, Through experiments, we find AdaIN effectively combines the content of the former and the style latter by transferring feature statistics. 디코더 네트워크는 AdaIN output을 image space로 inverting 함으로서 마지막 stylized image를 생성하는 법을 배운다.

우리의 방식은 input을 독립적인 새로운 스타일로 변환하는 유연성을 희생하지 않고도 Gatys방식보다 3배이상 빠르다. 그리고 유저컨트롤이가능하다.

Related Work

Style Transfer

초창기의 style transfer

  • Style transfer는 non-photo-realistic rendering으로부터 비롯되었으며, 이것은 texture synthesis와 transfer와 연관이 깊다.
    • 이러한 이전 접근 방식에는 linear filter response상에서의 histogram matching방식이나 non-parametric sampling등이 포함된다.
    • 이러한 방식들은 일반적으로 low-level statistics에 의존하며, 자주 semantic structure를 잡아내는데 실패했다.

feature statistics를 이요한 style transfer

  • 하지만 Gatys는 DNN의 Convolutional layers 상에서 feature statistics를 매칭함으로서 굉장히 인상깊은 style transfer 결과를 최초로 발표했다.
  • 최근에는 몇가지 개선점들이 발표되었는데,
    • Li와 wand는 local patterns를 찾기 위한 (enforce) deep feature space상에서의 MRF framework를 제안했다.
    • gatys는 color preservation, spatial location, scale of style transfer를 조절할 수 있는 새로운 방법을 제안했다.
      • Controlling Perceptual Factors in Neural Style Transfer - CVPR 2017 인데 별로 주목은 못받은듯?
    • Ruder는 시간상의 제약을 도입함으로서 video style transfer의 퀄리티를 향상시킬 수 있는 방법을 제안했다.
  • 초창기의 방식 (gatys 2016)은 느려서 On-device processing 은 실용적인 사용이 어려웠다. 공통적인 제 2의 방식은 optimization process를 FFNN으로 대체하는 것인데, 이는 Gatys와 같은 object function을 최소화 하는 방식이다. 이러한 FFNN방식은 최적화 방식 보다 훨신 빠른 속도를 보여줘 이를 real-time application에서 사용 가능한 정도 수준까지 끌어올렸다.
    • Wang et al. 은 multi-resolution architecture를 제안했다.
    • Ulyanov는 IN이다. (생성된 smaple의 quality와 diversity를 개선시키는 방법을 제안)
  • 하지만 이러한 Style Transfer방식은 각각의 network들이 특정 fixed style에 tied되어있다는 한계점을 가지고 있었다.
  • 이를 해결하기 위해(to address this problem), Dumoulin et al은 CIN을 제안해서 하나의 네트워크로 여러개의 스타일을 제공할 수 있게 되었찌만 여전히 32개라는 encoding된 몇몇개만 제공 가능했기 때문에 한계점이있다.
  • 매우 최근에는 Chen and Schmidt 이 FF방식의 arbitrary style transfer를 제안했는데, 이는 style swap layer를 이용한 것이다. content와 style 이미지의 feature activation이 주어졌을 때, style swap layer는 content feature를 가장 가깝게 매칭된 style feature로 patch-by-patch방식으로 대체한다. 하지만 이방식은 굉장히 높은 computational bottleneck을만든다. 거의 512 x 512 인풋 이미지에 대하여 style swap을 수행하는데만 95%에 이르는 계산을 사용한다. 우리의 AdaIN은 이방식에 비해서 거의 수배에서 수십배 빠르다.

Style transfer의 Loss Function

  • Gatys는 feature상에서 Gram matrix를 이용하여 second-order statistics를 매칭시키는 Loss를 사용하였다.
  • 다른 효과적인 Loss function들도 많이 제안되었는데, MRF loss, adversarial loss, histogram loss, CORAL loss, MMD loss, distance between channel-wise mean and variance. 등이 있다.
  • 뭐 근데 이런애들은 결국 하나의 공통적인 목표가 있는데 그건 바로 style image와 synthesized image상의 어떤 feature statistics를 match시키는 것을 목표로한다.

GAN

  • GAN도 cross-domain image generation을 통해서 style transfer를수행할 수 있다.

3. Background

3.1 Batch Normalization

$$\mathrm{BN}(x)=\gamma\left(\frac{x-\mu(x)}{\sigma(x)}\right)+\beta$$

$$\mu_{c}(x)=\frac{1}{N H W} \sum_{n=1}^{N} \sum_{h=1}^{H} \sum_{w=1}^{W} x_{n c h w}$$

$$\sigma_{c}(x)=\sqrt{\frac{1}{N H W} \sum_{n=1}^{N} \sum_{h=1}^{H} \sum_{w=1}^{W}\left(x_{n c h w}-\mu_{c}(x)\right)^{2}+\epsilon}$$

Batch Normalization은 아래 포스팅에서 자세히 다룹니다.

lifeignite.tistory.com/47?category=460775

 

Batch Normalization을 알아보자

notion으로 보면 더 편합니다. www.notion.so/Batch-Normalization-0649da054353471397e97296d6564298 Batch Normalization Summary www.notion.so 목차 Summary Introduction Background Normalization Covariate..

lifeignite.tistory.com

3.2 Instance Normalization

기존의 feed-forward stylization method에서는 각각의 Convolutional layer상에서 BN layer가 포함되어 있다. 놀랍게도 Ulyanov 아저씨가 BN을 IN으로 바꾸기만 했서 높은 성능 향상을 달성했다.

$$\mathrm{IN}(x)=\gamma\left(\frac{x-\mu(x)}{\sigma(x)}\right)+\beta$$

IN은 다음과 같이 생겼는데, BN과 마찬가지로 $\gamma,\beta$는 학습되어야 하는 파라미터다. 식자체는 BN과 똑같지만 다른점은 바로 group by c 뿐만 아니라 group by (b, c)해서 정규화 한다는 것이다.

바로 아래처럼!

$$\mu_{n c}(x)=\frac{1}{H W} \sum_{h=1}^{H} \sum_{w=1}^{W} x_{n c h w}$$

$$\sigma_{n c}(x)=\sqrt{\frac{1}{H W} \sum_{h=1}^{H} \sum_{w=1}^{W}\left(x_{n c h w}-\mu_{n c}(x)\right)^{2}+\epsilon}$$

BN은 $\sigma_n$이었는데 IN은 $\sigma_{nc}$다. 또한 BN과의 가장 큰 차이점은 바로 BN은 batch와 inference시에 사용하는 statistics가 다르지만, IN은 바뀌지 않는다.

맨날보는 그림

 

 

3.3 Conditional Instance Normalization

affine parameter인 $\gamma,\beta$ 하나만 학습시키는대신에, 각각의 style s에 따라서 서로다른 파라미터인 $\gamma^s,\beta^s$를 학습시키는 CIN이 제안되었다(Dumoulin 아조씨).

$$\operatorname{CIN}(x ; s)=\gamma^{s}\left(\frac{x-\mu(x)}{\sigma(x)}\right)+\beta^{s}$$

트레이닝시에 style image를 각각의 index s로 묶고, 랜덤하게 선택해서 style s에 따라서 파라미터를 학습시켰다. (set S는 실험에서 32개로) Content image는 CIN layer상에서 $\gamma^s$와 $\beta^s$를 사용하는 style transfer network에 의해서 처리된다. 놀랍게도, 네트워크는 같은 convolutional parameters를 사용하며 다른 affine parameters를 IN layer에서 사용함으로서 완벽하게 다른 스타일을 생성해낼 수 있었다.

이는 기존의 CIN이 없는 일반적인 네트워크를 사용할 경우와 비교해서 CIN 레이어는 추가적으로 2FS개의 파라미터를 사용한다. 여기서 F: 총 feature map의 개수며, S: 총 스타일의 개수이다. 따라서 style 의 개수가 증가할 때 선형적으로 파라미터의 개수가 증가하며, 스타일의 개수가 큰 모델의 경우 굉장히 challenging하게 된다. 또한, 이러한 방식은 독립적인 새로운 스타일에 대해서 트레이닝 없이 적용할 수 없다.

4. Interpreting Instance Normalization

비록 IN의 큰 성과에도 불구하고, 왜 style transfer가 잘 작동하는지에 관해서는 여전히 알수 없다. IN의 성공은 content image의 contrast에 invariance하게 IN 동작하기 때문이라고 말한다. 하지만 IN은 feature space에서 존재하고, 그러므로 이것은 픽셀 상에서 간단한 contrast normalization보다는 큰 영향을 줄 수 있을 것이다. 아마도 더 놀라운 것은 IN의 affine parameters가 output images의 style을 완벽하게 바꿀 수 있다는 사실이다.

DNN의 feature statistics는 이미지의 style을 capture할 수 있다고 알려져 있다. Gatys도 second-order statistics를 optimization의 objective로 삼았고 Li et al.도 다른 statistics를 매칭하는 방식을 보여줬다. 이러한 관찰에 모티브를 받아서, 우리는 instance normalization이 mean과 variance라고 이름붙여진 feature statistics를 normalizing함으로서 style normalization의 형태를 수행한다고 주장한다. 비록 DNN은 image descriptor로서 사용되지만, 우리는 generator network의 feature statistics가 생성된 이미지의 style을 컨트롤한다고 믿는다.

우리는 IN 모델과 BN모델을 각각 학습시켜서 style loss를 비교해봤다. 우리는 이 때 (a) 원본 이미지, (b) contrast normalized image, (c) pre-trained 모델을 활용하여 style normalized를 수행한 image에 대해서 학습을 수행했다. 놀랍게도 IN에 의한 개선이 contrast에 대해서 정규화한 이미지에 대해서 꽤 차이가 났따. 하지만 style을 normalized한 실험에서는 IN과 BN의 차이가 크게 나지 않았다. 우리의 결과는 IN이 style normalization의 한종류로 동작한다고 주장한다.

BN는 single image의 feature statistics가 아니라 sample의 배치의 feature statistics를 정규화 하기 때문에, 직관적으로 배치상의 서로 다른 스타일들을 하나의 스타일 근처로 정규화 시킨다고 이해할 수 있다. 이는 성능을 하락시킬 수 있다.

이러한 이해는 여러 연구들에 공통적으로 적용시킬 수 있으며 CIN도 마찬가지로 쉽게 이해할 수 있다.

5. Adaptive Instance Normalization

만약IN이 인풋을 하나의 단일 스타일, 특히 affine parameters에 의해 구체화되는 하나의 스타일로 정규화 한다면, adaptive affine transformations를 활용하여 독립적으로 주어진 스타일을 적용하는 것도 가능하지 않을까?

이에 따라 우리는 IN을 간단하게 확장한 Adaptive InstnaceNormalization (AdaIN)을 제안한다. AdaIN은 Content input x 와 style input y를 받았을 때, 간단하게 channel-wise mean and varinece of x를 channel-wise mean and varinece of y로 alighn한다. BN, IN, CIN과달리 AdaIN은 Affine parameter가 없다! 대신 affine parameter를 adaptively 계산한다.

$$\operatorname{AdaIN}(x, y)=\sigma(y)\left(\frac{x-\mu(x)}{\sigma(x)}\right)+\mu(y)$$

여기서 우리는 간단하게 content input을 $\sigma(y)$와 $\mu(y)$로 조정한다. IN과 유사하게, 이러한 statistics는 spatial locations을 across하여 계산된다.

직관적으로, 우리가 feature channel을 특정한 스타일의 brushstrokes 을 detect하자고 하자. 이러한 stock는 이 feature를 위한 high average activation 를 생성할 것이다. AdaIN에 의해서 생성된 output은 contents image의 spatial structure를 유지하면서 이 feature를 위한 같은 high average activation를 가지게 될 것이다. 이러한 brushstrokes feature는 Fedd-forward decoder를 통해 iamge space로 inverted될 것이다. 이러한 feature channel의 variance는 더 미묘한 style imnformation을 encode할 수 있을 것이고, 이것은 또한 AdaIN output으로 전달되고, 최종적인 output image에 전달될 것이다.

정리하자면, AdaIN은 channel-wise mean and variance라는 feature statistics를 transfering함으로서 feature space상에서 style trasnfer를 수행한다.

 

style transfer algorithm의 overview. 우리는 style, contents image를 인코딩하기위해 fixed VGG-19네트워크의 첫 몇개의 레이어를 사용하였다. AdaIN layer는 style trasnfer를 feature space 상에서 수행한다. decoder는 adaIN output을 image spaces상으로 invert하는 방법을 learning한다. 우리는 같은 VGG encoder를사용해 content loss와 style loss를 구했다.

6. Experimental Setup

6.1 Architecture

우리의 style transfer network T는 content image c와 arbitrary style image s를 input으로 받아서, content image에 style image를 합성한다. 우리는 간단한 encoder-decoder architecture를 사용했으며, encode $f$는pre-trained VGG-19의 앞 몇개 부분(upto relu4_1)까지 사용했다. feature 이미지 상에서 content와 style image를 인코딩한 후에 우리는 두 인코딩 된 feature map을 AdaIN 레이어에게 주고, mean과 variance를 맞춘다.

$$t=\operatorname{AdaIN}(f(c), f(s))$$

랜덤하게 초기화된 디코더 $g$는 $t$를 image space로 보내는 방법을 트레인하며 스타일이 입혀진 이미지 $T(c,s)$를 생성한다.

$$T(c, s)=g(t)$$

Architecture Detail

  • checker-board effect를 감소시키기 위하여 encoder의 pooling layer를 nearest up-sampling 방식으로 교체.
  • $f$와 $g$에서 모두 reflection padding을 사용했다.
    • IN레이어는 각각의 셈플들을 하나의 스타일로 정규화하고 BN은 셈플들의 배치를 하나의 스타일로 centered시킨다. 두 normalization방식은 모두 원하지 않는방식인데, 왜냐하면 우리는 decoder가 굉장히 다른 스타일로 이미지를 생성하기를 위하기 때문이다.
    • 따라서 우리는 normalization을 사용하지로 결정했고, IN/BN이 모두 정말로 performance를 떨어트리는지 보여주겠다.
    • decoder에서 normalization방식을 고르는 것이 중요했는데, 결론은 no normalization이 제일 좋았다.
  • MS-COCO를 컨텐츠 이미지로, WIkiArt 데이터셋을 스타일이미지로 나머지는 baseline network 셋팅을 따라했다. (어 키페이퍼가 patch-by-patch 였네?...) 대충 8만개쯤 된다.
  • adam optimizer사용 batch size는 8로 content-style image pair로
  • 전처리: 두 이미지를 aspect ratio를 유지한체 512로 사이즈를 키웠고, 여기서 256 by 256로 crop한다. 우리의 네트워크는 fully convolutional 이기 때문에, 어떤 사이즈의 이미지가 온다고해도 적용 가능하다. 

Loss

우리는 다른 네트워크와 유사하게 다음과같은 로스를 사용한다.

$$\mathcal{L}=\mathcal{L}{c}+\lambda \mathcal{L}{s}$$

content loss는 target feature와 output image의 feature의 Euclidean distance를 구했다. 우리는 일반적으로 사용되는 content image의 feature response를 사용하는 대신에 AdaIN output $t$.를 content target으로 삼았다. 이게 조금더 빨리 convergence가 이루어졌기 때문이다.

$$\mathcal{L}{c}=|f(g(t))-t|{2}$$

우리의 AdaIN 레이어는 오직 style features의 mean과 standard deviation를 transfer하기 때문에 우리의 style loss는 오직 이러한 statistics를 match시켜야한다. 우리는 gram matrix loss가 유사한 결과물을 생성한다는 사실을 알았지만, 우리는 IN statistics를 match 시켰는데, 왜냐하면 이게 개념상 더 깔끔하기 대문이다. 이 스타일 로스는 Li et al.에 의해서 발견된 스타일로스이다.

$$\begin{array}{r}\mathcal{L}{s}=\sum_{i=1}^{L}\left|\mu\left(\phi_{i}(g(t))\right)-\mu\left(\phi_{i}(s)\right)\right|_{2}+ \sum_{i=1}^{L}\left|\sigma\left(\phi_{i}(g(t))\right)-\sigma\left(\phi_{i}(s)\right)\right|_{2}\end{array}$$

$\phi_i$.는 VGG-19의 i번째 레이어이다. 스타일로스에서 사용한 레이어는 relu1 1, relu2 1, relu3 1, relu4 1 이다.

Result

7.1. Comparison with other methods

Speed

Titan X GPU를 썼을때 저정도 속도가나온다고하네요

Quality

 

7.2. Additional experiments

필요한것들은 위에 적어놧음

7.3. Runtime controls

Figure 9. 이 좀 재밌는 부분인데, Gatys가 2017년도에 color control이 가능한 style transfer를 제안했음. content image의 color를 유지하기위해서 첫번째로 style image의 color distributiond을 content image의 color distribution을 맞춰 준다음 style transfer를 수행한다는 방법임. (color distribution을 맞춰준다는건 color histogram을 맞춰준다는 건가... )

Reference

Arbitrary Style Transfer in Real-time with Adaptive Instance Normalization

 

혹시 수식 깨진 부분 있으면 말씀주세요 (notion에서 옮기다보니 수식이 종종 깨집니다.)

 

Entropy

엔트로피는 머신러닝에서 정보량을 나타내는 방법으로 사용된다. 정보의 량이라는게 되게 추상적으로 들리는데, 생각해보면 되게 간단한 개념이다.

ML상에서 굉장히 많이 사용되는 개념인데, 이해하지않고, 아 그냥 쓰면되지 하고 넘어가기에는 너무 찜찜해서 정리하기로 한다.

정보량의 예

다음과 같은 예를 들어보자.

A:커피 내기를 했는데,모자를 쓴 친구가커피를 샀어!

B:커피 내기를 했는데,옷을 입은 친구가커피를 샀어!

Question: 어떤사람이 커피를 샀는가?

A, B 라는 두 문장이 있었을 때 당신은어떤사람이 커피를 샀는지알고싶다면, 두 정보 중에 어떤 것이 더 값어치가 있다고 생각하는가?

단순하게 생각해보면,모자를 쓴 사람 보다 아무옷을 입은 사람이 더 많다. 이말인 즉슨모자를 쓴 사람에 대한 정보로누가 커피를 샀는지특정하기 쉽다는 것이다. 그러므로 우리는,A 정보가 당신에게 더 값어치가 있다. 이 값어치가 바로 정보량이다.

  • 여기서 알수 있는 사실은 더 특정하기 쉬운 정보들이 바로 더 정보로서의 값어치가 높다고 볼 수 있다.
  • 이를 다시 생각해본다면, 정보가 흔할수록 정보량이 적고, 정보가 흔하지 않을 수록 정보량이 크다.
  • 즉, 정보량과 흔한 정도가 반비례 관계라고 생각할 수 있다.

이를 조금더 구체적인 수치로 나타내서 측정할 수 있다면?

정보량의 계산

정보량을 어떻게 정량적으로 나타낼 수 있을까?

위의 예를 조금 변형해서 아래와 같이 바꿔보자.

A:커피 내기를 했는데,빨간 옷을 입은 친구가 커피를 샀어! $P(red)$

B:커피 내기를 했는데,파란 옷을 입은 사람이 커피를 샀어! $P(blue)$

라고 했을 때, 손쉽게 확률로 표현할 수 있다. 다음과 같이 정의해보자.

$P(red) = \frac{people_{red}}{people_{all}}$; 커피 내기를 한 사람들($people_{all}$) 중,빨간 옷($people_{red}$)을 입은 사람을 고를 확률.

$P(blue) = \frac{people_{blue}}{people_{all}}$;커피 내기를 한 사람들($people_{all}$) 중,파란 옷($people_{blue}$)을 입은 사람을 고를 확률.

직접 숫자로 표현해본다면, 전체 커피 내기를 한 사람들의 숫자가 10명이라고 했을 때, 빨간 옷을 입은 사람이 3명, 파란 옷을 입은 사람이 7명이라고 한다면,

$$P(red) = \frac{3}{10}=0.3 \ P(blue) = \frac{7}{10} = 0.7$$

와같이 계산이 될 수 있을 것이다. 그렇다면, 빨간 옷을 입었다는 정보와 파란옷을 입었다는 정보 중 어떤 것이 더 정보로서 가치가 있을까? 직관적으로 생각했을 때, 내가 누가 내기에 걸렸는지를 알기 원한다면 더 사람을 특정하기 쉬운, 즉 빨간 옷을 입은 사람에 대한 정보가 더 가치가 있다. 이를 통해서, 정보량은 그 사건(색)이 발생할 확률에 반비례한다는 사실을 알 수 있다.

이 때,어떤 변수 $x$가 나올 확률 $P(x)$에 대하여 정보량 $I(x)$을아래와 같이 정의 할 수 있다.

$$I\left( x \right) =-\log { P(x) }$$

여기서 $-log(x)$함수는 아래와 같은 그래프를 가지는데, 확률은 0과 1사이의 값을 가지므로, 정확히 반비례하는 관계를 나타내는데 사용할 수 있음을 알 수 있다.

위의 예제를 이 식을 통해서 계산해보면,

$$I(red) =-log \frac{people_{red}}{people_{all}} = -log \frac{3}{10}=1.203...$$

$$I(blue) =-log \frac{people_{blue}}{people_{all}} = -log\frac{7}{10} = 0.356...$$

따라서 1.203 > 0.356 반비례하는 관계를 잘 표현하는것을 수치적으로 볼 수 있다. 어찌 됐든, 이식은 정보량에 대한 개념을 수학적으로 잘 모델링 할 수 있다고 볼 수 있는 것이다.

Entropy의 정의

예제를 바꿔보자. 우리는 이제 빨주노초 바지를 입고있는 10명이서 가위바위보를 했다는 사실을 알았다고 하자. 그리고 빨4명 주3명 노2명 초1명 라는 사실을 알고 있다고 하자. 자 그러면 이 정보를 누군가에게 전달하고자 할 때, 어떻게 할 수있을까? 인간의 언어로는...1번사원은 빨간색, 2번사원은 주황색, 3번사원은 또 빨간색 ....

오마이갓 너무 길어진다. 우리는 컴퓨터를 사용할 수 있으므로, 이 정보를 이진화해서 보내보면 더 효율적으로 보낼 수 있지 않을까? 즉, 빨: 00 주: 01 노: 10 초: 11 로 정하고, 1번 사람부터 10번 사람까지 000100... 로 인코딩해서 보내는 방식이 있을 것이다. 이렇게 할 경우는 총 2바이트 * 10명 해서 총 20바이트면 이 바지 색에 대한 정보를 동료에게 전할 수 있을 것이다.

하지만, 여기서 더 줄일수는 없는 걸까? 사전 확률을 알고 있을 때, 어떻게 하면 이 정보들을 최소한의 비트를 사용해서 이 정보를 전송할 수 있을까? 이 때, 섀넌이라는 아저씨가 이 방식으로 인코딩할 비트수를 정하면 제일 효율적이다! 라고 증명한 방식이 바로 Entropy다.Entropy형태로 인코딩을 하는 것보다 효율적인 방식은 있을 수 없다!

섀넌은 특정 확률분포 $P(X)$를 따르는 확률 변수$X$가 있을 때, 확률 변수 $X$의 사건 $x$를 $p(x)I(x)$의 길이로 인코딩 하는 것이 기대값을 최소로 만들 수 있음을 보였다. (고한다) 이 때의 확률 변수 $X$의 기대값을엔트로피라고한다.

이 경우에는 그렇게 인코딩해야 최적의 길이(가장 짧은 길이의 바이트로)로 정보를 전달할 수 있다는 것이고, 그 길이가엔트로피다.

여기서 기대값 $\mathbb{E}[X]$ 란, 위키피디아의 힘을 빌어서

확률론에서, 확률 변수의 기댓값(期待값, 영어: expected value)은 각 사건이 벌어졌을 때의 이득과 그 사건이 벌어질 확률을 곱한 것을 전체 사건에 대해 합한 값이다. 이것은 어떤 확률적 사건에 대한 평균의 의미로 생각할 수 있다.

$$\mathbb{E}[X] =\sum_i x_iP(x_i)$$

로 나타낼 수 있다. 기대값에 대한 식은위키피디아에 가면 계산하는 방식을 찾아볼 수 있다.

즉, 각 사건의 벌어졌을 때의 이득($x_i$)은? 바로 정보량($I\left( x \right)$)이다. 그럼 바로 정의가 나와버리지.

어떤 확률 변수 $X$가 특정 확률 분포 $P(X)$를 따를 때,엔트로피$H(P)$는

$$ H(P)=H(x) =\mathbb{E}_{X \sim P}[I(x)] =\mathbb{E}_{X\sim P}[-logP(x)]=-\sum_xP(x)logP(x) $$

자 어려운 용어들이 많이 나왔는데, 위의 예에서 일일히 대입해보자면,

확률 변수 $X$ = 바지의 색(빨R,주O,노Y,초G)

확률 분포$P(X)$는 아래와 같다.

$$P(x) = \begin{cases} 0.4 & \quad \text{if } x \text{ is } R\\ 0.3 & \quad \text{if } x \text{ is } O\\ 0.2 & \quad \text{if } x \text{ is } Y\\ 0.1 & \quad \text{if } x \text{ is } G \end{cases}$$

조금더 설명하면, 여기서 어떤 사건이 발생할 확률은 $P(R)$와 같이 나타날 것이고, 빨간색의 옷에 해당하는 값, 즉 정보량은 $I(R)=-log(P(R))$가 될 것이다.

따라서, 우리의 엔트로피는 아래와 같다.

$$H(x) = -(P(R)logP(R)+ P(O)logP(O)+ P(Y)logP(Y)+ P(G)logP(G))$$

중요한점은 어떤 확률 분포가 있을 때, $H(P)$ 보다낮은 기대값을 가질 수 없다는 점이다!즉 머신러닝 관점에서 확률 분포 $P$를 알고 있다고 했을 때, $H(P)$ 는global minimum이다.

참고로 이 엔트로피는 재밌는 성질을 가지는데, 바로, 확률 분포가 균등할 때 최대값을 가진다는 것이다. 반대로, 확률 분포가 균등하지 않는다면 최소값을 가지게 된다.

확률분포가 균등할 경우 특정 확률 변수중에서 뭐가 나타날지 결정하기 어렵다. 즉불확실성이 높다. 반면에 특정 사건들이 높은 확률로 등장할 수 있다면 그 사건들로 결정될 확률이 높다. 즉결정적이므로 불확실성이 낮다. 이러한 특징 때문에 엔트로피는 불확실성에 대한 측정이 가능하다.

특정 확률변수가 어떤 확률 분포를 따를 때, 그것의 정보량의 총량을 나타내 줄 수 있으면서 동시에 불확실성에 대한 측정이 가능한 것이다. 그런데, 이 특징은 (내가 생각하기에) 정보이론적인 관점인거 같고 머신러닝에서는 중요하지 않다.

정리

정보가 발생할 수 있는 어떤 사건들의 확률 변수를 $X$라고 하고, 이 확률 변수에 따른 확률 분포를 $P(X)$라고 하자.

정보량

정보량은 어떤 정보가 가지는 가치를 정량화한 값이다.

확률 변수 $X$의 특정 사건 $x$의 정보량 $I\left( x \right)$ 는 아래와같이 계산될 수 있다.

$$I\left( x \right) = -\log { P(x) }$$

이 정보량은 아래와 같은 특징을 가진다.

  • 정보가 흔할수록 정보량이 적고, 정보가 적을수록 정보량이 크다.
  • 덜 자주 발생하는 사건은 더 높은 정보량을 가진다.

Entropy

엔트로피는 정보를 표현하는데 필요한 (평균) 최소 자원량(0 or 1의 개수)을 의미한다.

엔트로피는 모든 사건 정보량의 기대값으로 계산된다.

따라서 다음의 식으로 계산이 가능하다.

$$ H(P)=H(x) =\mathbb{E}_{X \sim P}[I(x)] =\mathbb{E}_{X\sim P}[-logP(x)]=-\sum_xP(x)logP(x) $$

이 엔트로피는 다음과 같은 특징을 가진다.

  • 엔트로피는 사건의 분포가 결정되기 쉬운 확률분포를 따를 경우 엔트로피는 낮다.
  • 엔트로피는 사건의 분포가 균등한 확률분포를 따를 경우 엔트로피가 높다.
  • 만약 이 엔트로피가 연속일 경우, 확률이 가우시안 분포를 따를때 앤트로피가 가장 높다.
  • 중요한점은 어떤 확률 분포가 있을 때, $H(P)$ 보다낮은 기대값을 가질 수 없다는 점이다!즉 머신러닝 관점에서 확률 분포 $P$를 알고 있다고 했을 때, $H(P)$ 는global minimum이다.

feat. 엔트로피는 불확실성의 양을 나타낼 수 있다.

Cross Entropy

의미와 정의

자, 이제 조금더 어려운 문제를 풀어보자. 일반적으로 실제 확률 분포 $P$는 알기 쉽지 않다. 당장 오늘 커피를 먹고싶다가도 먹기싫다가도 갑자기 내기를 하고 있는 우리들은 미리 이 확률 분포를 미리 아는 것은 쉽지 않을 것이다. 하물며, 머신러닝에서 매우 복잡한 문제를 모델링하는 것은 더더덛더ㅓ욱 이 확률분포 $P$를 알기 쉽지 않을 것이다.

그래서, 대충 우리가 아무렇게나 추정을 해보자. 그럼 내가 생각한 확률 분포를 $Q(x)$라고 했을 때,

$$Q(x) = \begin{cases} 0.25 & \quad \text{if } x \text{ is } R\\ 0.25 & \quad \text{if } x \text{ is } O\\ 0.25 & \quad \text{if } x \text{ is } Y\\ 0.25 & \quad \text{if } x \text{ is } G \end{cases}$$

라고 생각해본다. 정확한 정보량은 $-\log { P(x) }$이겠지만, 나는 $Q(x)$라고 잘 못 예측했다. 하지만 이런 방식으로 예측했기 때문에 이 확률분포에 대한 정보량은 어쨌든$-\log { Q(x) }$일 것이다.

다시말해, 실제확률분포가 $P(x)$일 때, $Q(x)$라고 잘못 예측한다면 정보량이 $-\log { Q(x) }$라고 측정되었을 것이므로 잘못 예측한 엔트로피는 실제 확률과 우리가 추정한 값(정보량)의 곱의 합으로 나타나게 된다.

실제 엔트로피는 $E(P)$로, 우리가 실제확률분포가 $P(x)$일 때, $Q(x)$라고 잘못 예측한 엔트로피를 $CE(P, Q)$로 나타낼 때,

$$E[P] =-\sum_xP(x)logP(x)$$

$$CE(P, Q) =-\sum_xP(x)logQ(x) $$

로 계산되게 된다.

Binary Cross Entropy

어 내가 보던 Binary Cross Entropy랑은 조금 다른데요?

CE에서 두 분포가 확률 변수를 단 두개만 가진다고 하자. 예를들면 동전의 앞면과 뒷면.

앞면이 나올 확률이 $P(x)$ 라고 한다면 뒷면이 나올 확률은 $1-P(x)$가 된다.

반면, 앞면이 나올 것으로 예측한 확률 $Q(x)$라고 한다면 뒷면이 나올 것이라고 예측한 확률은 $1-Q(x)$가 된다.

이걸 정리해보자면

$$CE(P, Q) =-\sum_xP(x)logQ(x) $$

$$BCE(P, Q) = -(P(x)logQ(x)+(1-P(x))log(1-Q(x)))$$ 

 

 

KL-divergence

의미와 정의

자꾸 KLKL 거리는데 KL 머길래?... 라고 했을때 되게 심플하다. 일단 divergence는 차이라는 의미로 해석하면 되는데, 주로 두 분포의 차이를 계산할 때 주로 사용된다.

아까 위에서 설명했드시, 실제 확률분포 $P$를 알고 있을 때, 엔트로피 $E(P)$는 이보다 더 좋아질 수 없는global minimum이다. 일반적으로 머신러닝에서 찾고 싶은 것은 바로 우리가만든 모델의 분포 $Q(x)$를 실제 분포 $P(x)$에 근사하게 만드는 것이다. 따라서,

$$\begin{aligned}E(P) &= -\sum_xP(x)logP(x) \\ CE(P,Q) &= -\sum_xP(x)logQ(x) \end{aligned}$$

일 때, 내가 생각한 확률분포와 실제 확률분포와의 차이를 나타내기 위해서 간단하게 $ CE(P,Q)  - E(P)$를 구해주면 된다. 그리고 이를 KL-divergence라고 부르며, $KL(P \parallel Q)$ 로 표기한다.

$$\begin{aligned} KL(P \parallel Q) & = CE(P,Q)  - E(P) \\ & = -\sum_xP(x)logQ(x)  -(- \sum_xP(x)logP(x)) \\ & = \sum_xP(x)logP(x) - \sum_xP(x)logQ(x) \\ & = \sum_xP(x)(logP(x)-logQ(x)) \\ & = \sum_xP(x)log\frac{P(x)}{Q(x)} \end{aligned}$$

정리한 식에서 만약 Q(x)가 P(x)의 분포를 알아냈을 때, 즉, Q(x)와 P(x)가 완전히 같아졌을 때, log 안쪽 식이 1이 되면서 $KL(P \parallel Q)$ 값이 0이 되고, 이것이 바로 최소가 된다.

사족

그런데 잘 생각해보면, binary classification에서 KL-divergence를 쓰지 않고 CE에 대해서만 최적화 해주는 것을 볼 수 있다. 그건 왜그러할까?

사실 쉬운 문젠데, 우리가 최적화하고 싶은 값은 바로 $Q(x)$이다. 즉 $Q$입장에서 보면 $P$는 상수다. 따라서 $CE$에 대해서만 최적화해주면 되기때문에 $CE$를 쓰는것이다

참고로, KL-divergence는차이일 수는 있지만,거리는 아니다. 왜냐하면 A에서부터 B까지 의 거리를 잰다고했을 때, A부터 B까지의 거리는 B부터 A까지와의 거리와 같아야하는데, KLD는 그렇지 않다. 딱봐도...

$$KL(P \parallel Q)=  \sum_xP(x)log\frac{P(x)}{Q(x)} \neq \sum_xQ(x)log\frac{Q(x)}{P(x)} = KL(Q \parallel P)$$

KL과 유사한데, 대칭성이 있어 거리로 사용할 수 있는 Divergence가 있는데, 그게 바로 JSD이다. 이건 추후 포스팅에서 다루겠다.

Reference

#4.0. 정보량을 나타내는 엔트로피 (Entropy)

엔트로피(Entropy)와 크로스 엔트로피(Cross-Entropy)의 쉬운 개념 설명

정보 엔트로피

정보이론 기초

왜 크로스 엔트로피를 쓸까?

Wolfram|Alpha: Making the world's knowledge computable

가장 쉬운 KL Divergence 완전정복!

[딥 러닝] 4강. 정보이론 기초, Cross-entropy와 KL-divergence

notion은 tistory와 연동이 안된다 ㅠㅠ

망했다. 연동이 제대로안되네. 수정하기도 함들고

https://www.notion.so/Cross-Entropy-5f028e1291744e50b8b91644105807be

실제 수정이나, 변경사항은 여기에 남기도록 하겠습니다.

 

수정 내역

  • (2021-03-30) KLD 수식 전개 과정에서 P(x) Q(x)를 바꿔쓴 것 수정
  • (2021-04-03) BCE 섹션추가
  • (2024-03-17) KLD 수식 전개 과정에서 CE-E를 E-CE로 풀이한 것을 수정

 

'ML > 기초 이론' 카테고리의 다른 글

Batch Normalization을 제대로 이해해보자  (0) 2021.03.13

+ Recent posts