멀티모달학습은 2종류이상의 data사이변환을 위해 생성모델을 훈련시킨다. 이번시간에는 총 4가지의 txt2img, Visual Language Model에 대해 살펴볼 것이다. - [DALL∙E 2]_ OpenAI - [Imagen]_ Google Brain - [Stable Diffusion] _ Stability AI & CompVis & Runaway - [Flamingo] _ DeepMind
txt2img는 txt프롬프트가 주어졌을때, 최신 img생성에 초점을 맞춘다.
[추가적 과제]: - 두 Domain간 전환방법 학습 - 정보손실없이 txt2고화질img이 되도록하는 공유표현을 학습. - 이전에 본 적 없던 개념과 style결합
1. DALL∙E 2
prev.
DALL∙E 2는 txt2img모델로 2022년 4월에 출시되었다. 사람에게만 있다 여겨진 창의성에 대한 AI의 역할에 대한 질문을 던진 전환점이 되었다 볼 수 있었다.
구조
구조에 대해 총 3가지를 살펴볼 것이다. 1. text Encoder: encoder통과후 txt embedding벡터 생성 2. Prior: txt embedding벡터를 img embedding벡터로 변환 3. Decoder: img 생성
한계점: attribute binding 주어진 txt prompt에서 단어간 관계(특히 속성이 객체와 어떻게 관련된지 이해해야하는 모델 능력)에 대해 DALLE는 GLIDE같은 이전모델보다 다소 어려움을 겪긴하나 다양하고 우수한 품질이 가능하다.
또한, txt를 정확하게 재현할 수 없는데, 이는 CLIP embedding이 txt의 고수준표현만 담을 수 있기 때문이다. 즉, 전체 단어를 구성할 만큼 충분히 이해하지 못한다.
text Encoder
목적:txt prompt를 embedding벡터로 변환 embedding벡터 = latent space내 txt prompt의 개념적 의미를 나타냄
∙ CLIP
DALL∙E 2는 OpenAI에서 만든 CLIP이란 기존 모델을 사용한다. CLIP논문: CLIP이란, '자연어 감독으로부터 시각적 개념을 효율적으로 학습하는 신경망'
CLIP은 contrastive learning으로 img와 txt설명을 매칭한다. 모델의 목표:이미지와 가능성있는 txt목록 중, 실제로 일치하는 설명을 찾는 것. 대조학습 핵심 아이디어: txt encoder와 img encoder(ViT VQ-GAN) 두가지 신경망을 학습: txt encoder: txt를 txt로 embedding img encoder: img를 img로 embedding 이후, cosine similarity로 모든 txt와 img embedding간 비교, 가장 일치하는 쌍을 최대화, 잘못된 쌍의 점수를 최소화하도록 훈련. 이때, CLIP은 판별능력을 기준으로 평가된다.
흥미로운점: 모든 라벨을 표현할 수 있는 공통영역으로 언어를 사용하기 때문에 Zeroshot prediction이 가능하다. (=즉 새로운 작업에 적용하기 위해 CLIP신경망을 재훈련할 필요가 없다.)
앞서 훈련된 txt encoder가중치를 고정. 이후 DALL∙E 2같은 더 큰 모델의 한 부분으로 사용한다.
prior
txt embedding을 CLIP Img embedding으로 이제 변환을 해야하며, 이를 위한 prior모델로 2가지방법을 시도했는데, Diffusion이 AR보다 성능 및 계산효율이 좋다는 사실을 발견했다.
∙ AR Model
AR모델은 출력토큰에 순서를 지정, 이전 토큰을 다음 토큰의 조건으로 삼아 순차적으로 출력을 생성한다. DALL∙E 2의 AR prior는 Encoder-Decoder Transformer로 CLIP txt embedding이 주어지면 CLIP img embedding재현을 돕는다.
input txt embedding은 transformer Encoder에서 처리되고 현재생성된 출력 img embedding과 Decoder로 공급되는 또다른 표현을 생성한다. 출력은 한번에 한 원소씩 생성되며 Teacher Forcing으로 예측된 다음 원소를 실제 CLIP img embedding과 비교한다.
다만, 순차적으로 생성되는 속성으로인해 AR모델은 계산효율이 좋지않다.
∙ Diffusion Model
Transformer와 함께 Diffusion모델은 선호되는 모델이 되었다. AR Prior는 DALLE1에서 사용되었으나, DALLE2부터는 Diffusion prior가 사용되게 되었다.훈련 시, txt와 img embedding들은 서로 단일벡터로 연결된다. 이후 img embedding과 random noise가 구별되지 않을때까지 1000번이상의 time step을 거친다. 이후 이전 time step의 noise가 추가되기 전 img embedding을 예측하도록 Diffusion Prior를 훈련한다.
이때, Loss function은 전체 Denoising단계에 걸친 MSE이다:
Decoder
txt prompt와 prior가 출력한 img embedding을 기반으로 최종 img를 생성한다.
DALLE2: 초기 txt prompt 정보를 전달하는데 CLIP embedding을 사용 GLIDE: Denoising U-Net구조를 사용해 하나의 Diffusion Model로 훈련, 전체 생성모델을 처음부터 학습시킴.
DALLE 2 Decoder도 Denoising으로 U-Net을 사용하고 txt encoder로 transformer를 사용한다.
다만, GLIDE와 차이점은 추가로 예측된 CLIP img embedding을 조건으로 사용한다는 점이다.
UpSampler를 사용하기에 고차원 img처리를 위한 대규모 모델구출의 필요성은 없다. 즉, UpSampler만으로 마지막 단계까지 작은 img로 작업이 가능하여 parameter절약 및 훈련과정을 효율적으로 유지할 수 있다.
2. Imagen
prev.
Imagen 또한 txt encoder와 Diffusion model Decoder를 사용한다.
구조
Frozen Text Encoder는 T5-XXL이다. 이는 CLIP과 달리 img가 아닌 txt로만 학습되었기에 multi-modal모델은 아니다. 다만 txt encoder로 잘 작동하기에 아래 사실을 확인할 수 있었다. Txt Encoder를 잘 확장 >> Diffusion모델 Decoder 확장
DALL∙E 2처럼 Imagen의 Decoding Diffusion model은 txt embedding을 사용하는 U-Net구조를 기반으로 한다. 이때, 생성된 img를 64×64에서 1024×1024 pixel로 변환하는 Super-ResolutionUpsampler모델도 UpSampling과정을 가이드하는데 Txt embedding을 사용하는 Diffusion모델이다.
DrawBench
Imagen논문의 또 다른 Contribution으로 txt2img를 평가하는 txt prompt모음이다. 위와 같은 방식으로 두 모델간의 비교를 진행한다. 이때, 두 모델간 비교를 위해 각 모델에 DrawBench txt prompt를 전달, 평가자에게 출력을 제공, 2개의 평가지표를 평가한다: ∙ Alignment: 어떤 img가 caption을 더 정확하게 설명하는가? ∙Fidelity: 어떤 img가 더 진짜처럼 보이는가?
다만, DALL∙E 2는 Imagen에는 없는 추가기능을 제공하는데, CLIP을 이용하기에 img를 입력으로 받아 img embedding을 생성할 수 있다. 즉, DALL∙E 2는 img편집 및 변형기능을 제공할 수 있으나 Imagen은 순수 txt모델이기에 img입력방법이 없다.
3. Stable Diffusion
prev.
마지막으로 살펴볼 txt2img Diffusion모델은 그 유명한 Stable Diffusion이다. Stable Diffusion은 Hugging face 🤗에 코드와 모델가중치가 공개되었다는 점에서 전술한 두 모델과 차별화된다. 즉, 독점적인 API를 사용하지 않고 누구나 자신의 하드웨어에서 이 모델을 사용할 수 있다는 점이다.
Architecture
Stable Diffusion은 기본생성모델로 Latent Diffusion을 사용한다는 점에서 전술한 두 모델구조와 차이가 있다. LDM(Latent Diffusion Model)논문에서의 핵심아이디어: "Diffusion모델을 AE로 감싸서 Diffusion과정이 img자체가 아닌, img의 latent space representation에서 작동한다는 것."
이 획기적인 방법은 전체 img에서 작동하는 U-Net모델보다 Denoising U-Net모델을 상대적으로 가볍게 유지할 수 있다.
AE에서 img detail을 latent space로 encoding latent space를 high resolution img로 다시 decoding 즉, Diffusion모델은 Latent space에서만 수행되기에 train과정의 속도 및 성능이 크게 향상된다.
다만, Stable Diffusion 2부터는 처음부터 맞춤형으로 훈련된 CLIP모델인 OpenCLIP을 사용한다.
4. Flamingo
prev.
전술한 세 종류의 txt2img모델과 달리 Flamingo는 다음과 같다: "visual data stream이 주어지면, txt를 생성하는 멀티모달 모델" 이를 VLM(Visual Language Model)이라 한다.
Architecture
가장 핵심구성요소에 대해 알아보자: ∙ Vision Encoder ∙ Perceiver Resampler ∙ Language Model
Vision Encoder
전술한 DALL∙E 2, Imagen 같은 순수 txt2img모델과의 첫번째 차이점이다. 바로 🦩는 txt와 visual data(img, video, ...)의 조합을 사용가능하다는 점이다.
∙ 역할: CLIP의 img encoder와 유사하게 입력에 포함된 visual data를 embedding 벡터로 변환하는 것.
Vision Encoder는 Pretrained NFNet(Normalizer-FreeResNet)을 사용한다. CLIP: ViT사용 Vision Encoder: ResNet사용
Vision Encoder는 CLIP논문에 소개된 것처럼 Contrastive Loss를 사용해 img-txt쌍에 대해 훈련한다. 훈련 후 weight는 frozen되기에 🦩모델의 추가훈련이 vision encoder의 가중치에 영향을 주지는 않는다.
Vision Encoder의 출력은 feature의 2D grid로 Perceiver Resampler로 전달전에 1D벡터로 펼쳐진다. cf) Video는 각 snapshot을 vision encoder에 독립적으로 통과시킨 후 여러 feature grid를 생성.
Perceiver Resampler
BERT같은 기존 encoder transformer의 메모리 요구사항은 input sequence length에 따라 제곱으로 늘기에 정해진 token수로 제한해야한다. 하지만 Vision Encoder의 출력은 가변길이 벡터이기에 매우 길 수 있다.
Perceiver 구조는 긴 input seq를 효율적으로 처리하기위해 특별히 설계되었다. 전체 input_seq에 대해 self-attention대신, 고정 길이의 latent 벡터와 input_seq로 cross attention을 수행한다. Q: latent 벡터 K, V: input_seq와 latent 벡터를 연결한 것
Language Model
LM은 예측 txt를 출력하는 Decoder transformer스타일의 블록을 여러개 쌓아 구성된다. LM은 대부분 pretrained Chinchilla모델에서 가져오는데, GPT등장이후, GPT를 많이 사용한다.
🦩논문의 Main Contribution: 언어데이터(Y)에 포함된 비전데이터(X)를 친칠라에서 어떻게 처리하는가?
1. Vision data를 <image>태그로 대체, <EOC>태그를 사용해 txt를 chunk로 나눈다. 각 chunk에는 최대 하나의 img가 포함되며, 이 img는 항상 chunk의 시작부분에 있다. 즉, 뒤따르는 txt는 해당 img와만 관련있다 간주한다. seq의 시작부분도 <BOS>태그로 표시된다.
2. seq가 토큰화된 후 각 토큰에는 앞선 img인덱스에 해당하는 인덱스(phi)가 다음과 같이 부여된다: chunk에 앞의 img가 없다면 0 이때, txt token(Y)이 마스킹을 통해 특정 chunk에 해당하는 img토큰(X)과만 cross attention되게 강제할 수 있다.
∙ [Masked Cross Attention 역할]: 파란색 LM층은 친칠라의 frozen층이므로 훈련과정에서 update되지 않는다. 보라색 GATED XATTN-DENSE층은 플라밍고의 일부로 학습되며 언어와 시각정보를 혼합하는 Masked Cross Attention과 FFN층을 포함한다.
이 층은 Cross Attention과 FFN의 출력을 2개의 서로다른 tanh게이트에 통과시키며, 이 게이트는 모두 0으로 초기화된다. 결과적으로 신경망이 초기화될 때, GATED XATTN-DENSE층이 기여하는 바가 없고, 언어정보가 바로 전달된다.
alpha게이트 parameter가 신경망에 의해 학습되어 훈련이 진행됨에 따라 ivsion data의 정보를 점차 혼합시킨다.
ex).
각 예제를 보면 진정한 멀티모달 스타일로 플라밍고가 txt와 img의 정보를 혼합함을 확인할 수 있다.
1번 예) 단어 대신 img를 사용, 적절한 책을 제안해 prompt를 계속 진행 2번 예) video frame을 보여주며 🦩는 작동결과를 정확하게 식별 마지막 3개 예) 모두 🦩를 대화형으로 사용하는 예시, 대화를 통해 추가정보를 제공하거나 질문을 하면서 정보확인이 가능.
[Imagen]-Google Brain Txt Encoder와 Diffusion모델 Decoder 등 DALL∙E 2와 상당히 유사. DALL∙E 2와의 주요 차이점: ∙ Imagen txt encoder: 순수한 txt data로 훈련됨 ∙ DALL∙E 2: txt encoder의 훈련과정에는 img data가 포함됨. 위의 차이점 및 결과를 DrawBench평가를 통해 보여준다.
[Stable Diffusion]-Stability AI, CompVis, Runaway 오픈소스로 제공되며, img자체가 아닌 AE의 latent space에서 작동하는 LDM(Latent Diffusion Model)을 사용하기에 특히나 빠르고 가볍다.
[Flamingo 🦩]-DeepMind Visual Language Model, VLM으로서 txt와 visual data가 혼합된 stream을 받고 decoder transformer스타일로 prompt에 이어 txt를 추가할 수 있다. ∙ Main Contribution: visual input feature를 적은 수의 visual토큰으로 encoding하는 vision encoder와 perceiver resampler를 통해 visual정보가 어떻게 transformer에 공급될 수 있는지 보여주는 것이다. LM 자체는 DeepMind의 초기 친칠라모델을 확장한 것으로, visual정보를 혼합할 수 있게 조정되었다.
Nvidia Labs가 GAN의 train속도 및 안정성향상을 위해 개발한 기술. 전체 해상도 img에 바로 GAN을 도입❌
4×4 pixel의 저해상도 img로 G, D를 훈련 → 훈련과정층에 점진적 추가 → 해상도를 높인다.
점진적 훈련
∙ [Vanilla GAN]: [G]: 처음부터 복잡한 고해상도 img로 작동 이는 train초기, 고수준의 구조학습속도가 느릴 수 있다.
그렇다면, 저해상도 Img를 출력하게 경량 GAN을 훈련시키고 이를 기반으로 해상도를 점진적으로 높이는게 낫지 않을까?
4×4 pixel의 저해상도 img로G, D를 훈련 → 훈련과정층에 점진적 추가 → 해상도를 높인다.
progressive training
이를 progressive training, 점진적 훈련이라 한다.
∙ (a) [Generator]: ProGAN은 Interpolation을 이용, 4×4 pixel로 압축된 trainset부터 시작해 단계적으로 훈련한다. 그 후, z(latent input noise vector)를 4×4×3 img로 변환하게 생성자를 훈련시킨다.
[Discriminator]: 4×4×3인 input img를 하나의 스칼라 예측으로 변환한다. 생성자와 판별자를 확장하려면 층을 추가해야하는데, 이는 위 그림과 같다. toRGB: feature map을 RGB img로 변환하는 합성곱층 fromRGB: RGB img를 feature map으로 변환하는 합성곱층 ∙ (b), (c) [Generator]: ∙ transition단계: 새로운 UpSampling과 Conv층이 기존 신경망에 추가. 기존에 훈련된 toRGB층의 출력유지를 위해 Skip Connection이 추가. 새 층은 처음에 α로 masking된다. (α는 transition단계동안 0에서 1로 점진적으로 증가, 기존 toRGB층의 출력을 줄이고 새 toRGB층의 출력을 늘림.) 이는 새 층으로 이전될 때, 신경망이 받을 변화의 충격을 회피시킨다. 즉, 기존 toRGB층을 통과하는 data가 사라지면 신경망은 stabilization단계로 들어간다.
∙ stabilization단계: 이 단계에서 기존 toRGB층을 통한 data흐름없이 추가훈련으로 신경망의 출력을 fine-tuning한다. [Discriminator]: 판별자 또한 비슷한 과정을 진행한다. 여기서 DownSampling과 Conv층을 추가한다. 이번에는 신경망이 시작될 때, input_img바로뒤에 추가된다.
기존 fromRGB층은 residual connection으로 연결되며 transition단계에서 새층으로 전환됨에따라 점진적으로 제거된다. stabilization단계에서는 판별자가 새 층으로 fine-tuning한다. 이 과정은 4×4 img부터 전체 해상도 img인 1024×1024에 도달할 때까지 계속된다.
주요 기술.
∙ minibatch standard deviation
특성값의 표준편차를 모든 픽셀과 mini-batch에 걸쳐 평균, (상수)특성으로 판별자에 추가하는 층이다. 장점: 생성자가 출력에서 더 많은 다양성을 부여하는데 도움이 됨. mini-batch에서 다양성이 낮으면 표준편차가 작아지고, 판별자는 이 특성으로 real/fake batch를 구별가능하게 되어 생성자를 독려하게 한다.
∙equalized learning rate
보통 신경망의 가중치는 He initialize로 초기화한다. 이를 통해 입력개수가 많아지면 가중치가 0에 가까운 값으로 초기화되어, train과정의 안정성이 향상된다. 특히 이때, Adam이나 RMSProp을 사용하는데, 여기서 문제가 발생한다.
이런 optimizer들은 weight에 대한 gradient update를 정규화한다. 즉, update크기가 weight크기와 무관하다. 하지만 범위가 큰 가중치(= input이 적은 층)가 범위가 작은 가중치(= input개수가 많은 층)보다 조정하는데 상대적으로 오래걸림을 의미한다. 이로인해 ProGAN에서 생성자와 판별자의 여러층에서 train속도간 불균형이 발생하기에 균등 학습률을 활용한다.
ProGAN은 표준정규분포로 초기화된다. 즉, 층이 호출될 때마다 He초기화 계수에 따라 가중치의 크기가 조정된다.
cf) He initialize: 표준편차가 층에대한 입력개수의 제곱근에 반비례한 Gauss분포
∙Pixel-wise Normalization
ProGAN은 Batch Normalization이 아닌, 픽셀별 정규화를 사용한다. - 각 픽셀의 feature vector가 단위길이로 정규화, - signal이 신경망으로 전파되어 제어불능상태가 되는 문제를 방지.
2. StyleGAN
prev.
StyleGAN은 ProGAN논문의 초기 아이디어를 기반으로한다. (사실 판별자는 동일, 생성자만 바뀌었다.)
GAN 훈련 시, 고수준특성에 해당하는 latent space vector구별이 어렵다. ex) 주근깨를 많이 넣으려 latent space img를 조정하면 배경색이 바뀌는경우.
StyleGAN은 다양한 지점에서 Style vector를 신경망에 명시적으로 주입, 일부는 고수준특성(ex. 얼굴방향)을 일부는 저수준특성(ex. 처피뱅, 시스루뱅, ...)을 제어한다.
Mapping Network: f
input noise z∈Z를 다른 latent space w∈W로 변환하는 간단한 FFN.
목적: 생성자는 noise있는 input vector를 후속 style생성층에서 쉽게 선택가능하게 서로 다른 변동요소로 해체가능. 이를 통해 synthesis과정(주어진 style을 갖는 img생성과정)과 이미지 스타일선택과정(mapping신경망)을 분리한다.
Synthesis Network
mapping network가 제공하는 style로 실제 Img생성. 위 그림에서 볼 수 있듯, style벡터 w는 합성신경망 여러지점에서 매번 다른 밀집연결층 A를 통해 주입된다. A는 bias벡터 yb와 scale벡터 ys 2개를 출력한다.
이 벡터가 신경망의 해당지점에 주입해야할 특정 style을 정의한다. 즉, 생성될 img를 특정 style방향으로 이동시키는 방법을 합성신경망에 전달한다. 이 조정은 Adaptive Instance Normalization층으로 이뤄진다.
∙ Adaptive Instance Normalization.
AdaIN층은 reference style의 편향 yb과 스케일 ys을 이용해 각 feature map x의 평균과 분산을 조정하는 신경망층이다. AdaIN층은 층간 Style정보가 새지 않게 방지해 각 층에 주입되는 Style벡터가 해당 층의 특성에만 영향을 미치게 한다. 이를통해 원본 z벡터보다 훨씬 덜 얽힌 latent vector w를 만들 수 있다.
cf) ProGAN구조기반이기에 점진적으로 훈련된다. 즉, latent vector w를 통해 생성된 img를 완전히 제어가능하며 합성신경망 여러지점에서 w벡터를 바꿔 다양한 수준의 style을 변경가능하다.
∙Style Mixing.
trick을 사용, 생성자가 훈련 중 인접한 style간 상관관계를 활용불가능하게한다. = 각 층에 삽입된 style이 가능한 얽히지 않게 함.
하나의 latent vector z만 sampling하지 않고 두 style vector(w1, w2)에 대응하는 두 latent vector(z1, z2)가 sampling된다.
그 후, 각 층에서 w1, w2 둘 중 하나를 random하게 선택해 vector간의 가능한 모든 상관관계를 끊는다.
∙Stochastic variation.
합성신경망은 개별 머리카락의 batch나 얼굴뒤 배경같은 stochastic detail을 위해 합성곱층 다음에 (학습된 broadcasting층 B를 통해) noise를 추가한다. 여기서도 noise가 주입되는 깊이가 img에 미치는 영향의 정도를 결정한다.
또한, style입력과 Noise입력에는 img에서 충분한 분산을 생성할만큼의 확률성이 이미 존재한다.
출력결과.
여기서 두 Source A, B이미지는 다른 w벡터로 생성된다. 두 img를 합치려고 A의 w벡터를 CNN에 통과, 어느 특정지점에서 B의 w벡터로 바꾼다.
🤔이런 변경이 초기해상도에 발생하면? 자세, 얼굴모양, 안경같이 B에 있는 큰 특징의 Style이 A로 전달된다.
🤔 이런 변경이 좀 나중에 발생하면? B에서 색깔, 얼굴의 미세형태같은 미세특징의 Style이 A로전달된다. (이때, A의 큰 특징은 유지됨.)
3. StyleGAN2
prev.
이 모델은 StyleGAN기반으로 생성품질향상을 위해 몇가지 주요 변경사항이 있다. StyleGAN과는 생성자, 판별자 모두 다르다. artifact: 물방울처럼 번진것 같은 현상StyleGAN2는 위 그림같이 img의 Artifact가 거의 발생하지 않는다. 이런 Artifact는 StyleGAN의 AdaIN층으로 인해 발생하는것으로 밝혀졌다.
Weight Mod와 Demod
Artifact문제 해결을 위해 아래 그림 (d)처럼 AdaIN층을 제거하고 가중치 변조(modulation)와 복조(demodulation)단계로 대체한다.
w는 합성곱의 가중치로 runtime에 StyleGAN2의 변조∙복조단계에서 직접 update된다. 이에비해 StyleGAN의 AdaIN층은 텐서가 신경망을 통과할 때 해당 텐서에 대해 작동한다. StyleGAN의 AdaIN층은 단순 instance normalization에 이은 Style변조(scale+bias)에 해당한다. StyelGAN2는 위 그림처럼 Conv층의 출력이 아닌 Runtime에 Conv층의 Weight에 직접 Style변조 및 정규화(복조)를 적용한다.
StyleGAN2에서 각 Dense layer는 하나의 Style벡터 si를 출력한다. (i: 해당하는 Conv층에 있는 input channel index) 이 Style벡터는 아래식처럼 Conv층의 가중치에 적용되는데, 이를 변조단계라 한다: w'i,j,k = si∙wi,j,k (j: 층의 output channel index , k: 공간차원 Index) 이후 가중치를 정규화해 다시 단위표준편차를 갖도록해 train안정성을 보장하는 복조단계를 거친다: w''i,j,k = w'i,j,k / √∑i,kw'i,j,k2 + ε
결과적으로 간단한 변경을 통해 물방을 artifact방지는 물론 Style벡터로 생성된 Img제어 + 출력품질을 높인다.
Path Length Regularization
StyleGAN구조에 또다른 변경사항이 있는데, Loss Function에 경로길이규제(Path Length Regularization)라는 추가적인 penalty항을 주는 것이다.
latent space에서 어느 방향이든 고정된 크기의 거리이동 시 img에 고정된 크기변화가 발생하는 것이 좋다. 그렇기에 latent space를 가능한 매끄럽고 균일하게 하는것이 선호된다.
위 속성을 장려하기위해 Gradient Penalty가 있는 일반적 Wasserstein Loss로 아래항 최소화를 목적으로 한다: 손실함수 규제항 효율성을 위해 16-batch마다 적용하는 Lazy Regularization기법으로 두드러진 성능저하가 발생하지도 않는다.
No Progressive Training
또 다른 주요변경사항은 training방식으로 일반적 점진적 train채택이 아닌, 생성자의 Skip Connection과 판별자의 Residual Connection을 활용 →신경망을 하나로 훈련한다.
즉, 더는 각기 다른 해상도에서 독립적으로 훈련하고 혼합할 필요가 없다.
training 진행시, 생성자가 판별자를 속이기 위해 진짜같은 img를 만들어야하는데,
위 그림은 고해상도 층이 진짜같은 img를 만드는데 점차 지배적이게 됨을 보여준다.
∙ Attention을 사용하지 않는 GAN기반 모델은 Convolution feature map이 local한 정보만 처리 ∙CNN은 channel수를 늘려야만 receptive field가 넓어져 좀 더 넓은 범위를 포착가능. ∙ 이 과정에서 고수준특징을 잡기위해 정확한 위치정보를 잃는 단점이 발생.
→ Self-Attention을 도입한 SAGAN으로 이런 문제를 해결. 붉은 점: 몸통부분 픽셀 (즉, Attention이 몸통부분의 픽셀에 집중.) 초록 점:배경부분 픽셀 (이 Attention은 머리와 뒷배경에 집중.) 파란 점:꼬리부분 픽셀 (이 Attention은 멀리 떨어진 다른 꼬리픽셀에 집중.) Attention을 사용하지 않고 이렇게 넓은 범위의 pixel-independency를 유지하기란 어려운데, 특히나 새 꼬리처럼 길고 얇은 구조의 경우, 픽셀의존성 유지가 더욱 어렵다.
BigGAN
DeepMind에서 개발한 모델로 SAGAN의 아이디어를 확장시켰다. ImageNet에서 훈련된 BigGAN으로 생성된 img 물론, 추가된 내용중 매우 혁신적인 내용도 존재한다:
∙ Truncation Trick : train 시, latent분포로 z~N(0,I)를 사용하지만 sampling 시, truncated normal distribution을 사용한다 (= truncated_threshold보다 작은 z를 sampling한다.) 이때, truncated_threshold가 작을수록 다양성은 줄어들지만 생성sample신뢰도는 커진다.
임계값은 좌측부터 2, 1, 0.5, 0.04
BigGAN의 batch_size는 2048로 SAGAN의 256보다 8배 더 크다. 각 층의 channel_size도 약 50% 증가했다.
즉, SAGAN보다 BigGAN은 구조적으로 향상되었는데, 여기서 Shared Embedding과 Orthogonal Regularization이 포함된다. latent vector z를 첫번째 층뿐만아니라 Generator의 층마다 사용한다.
VAE에서 학습한 표현이 연속적이 아닌, 이산적일 수 있음을 기반으로 만들어졌다. 사후붕괴로 인해 연속적 latent space 형성하는 VAE에서 흔히 발견되는 몇가지 문제를 피하여 고품질결과를 출력한다.
[사후붕괴; posterior collaps]: 지나치게 강력한 Decoder로 학습된 latent spacce가 유용하지 않은 경우.
cf) OpenAI의 DALLE∙1 또한 VQ-VAE와 유사한 이산 latent space의 VAE를 활용한다.
Discrete Latent Space
해당 인덱스에 연관된 학습된 벡터목록(= codebook)을 의미한다.
[VQ-VAE의 Encoder]: input_img를 codebook과 비교가능한 더 작은 vector_grid로 축소하는 역할. 그 후 각 grid정사각형 벡터에 가장 가까운 codebook vector를 decoder로 전달해 decoding한다.
codebook: 길이가 d(= embedding_size)인 학습된 벡터의 목록. 이 길이는 encoder 출력과 decoder 입력의 channel수와 일치한다.
Codebook은 주어진 img내용을 이해하고 Encoder Decoder가 공유하는 학습된 이산적인개념의 집합이다. 이를 통해 Encoder가 grid사각형 라벨을 Decoder에게 의미있는 특정 code vector로 채울 수 있다. ∴ VQ-VAE의 손실함수는 Encoder의 출력벡터가 Codebook의 벡터에 최대한 가까워지도록하는 2가지항(alignment loss, commitment loss)으로 만들어진 재구성 손실이다. 이 항이 일반적인 VAE에 있는 Encoding된 분포와 표준정규분포사이 KLD로 대신한다. 다만, VQ-VAE는 새 img생성할 때마다 Decoder에 전달할 때, code grid를 어떻게 sampling해야할 지 의문점이 존재한다. 이 문제를 해결하기위해, PixelCNN으로 이전 codevector가 주어지면, Grid의 Codevector를 예측한다. 즉, "VAE처럼 prior분포가 전적이 아니라 모델에 의해 학습"된다.
VQ-GAN은 위 그림구조와 같으며, VQ-VAE구조에 몇가지 주요변경사항을 적용한다:
① Loss에 적대적항을 추가하는 GAN판별자를 포함. 다만, VQ-GAN에도 VQ-VAE가 여전히 들어가며, GAN판별자는 VAE대체가 아닌 추가적 구성요소이다.
②GAN판별자는 전체img를 한번에 보지 않고, img의 작은 patch의 real/fake를 예측 판별자예측의 개별요소는 patch기반이기에 patch style을 사용해 결정을 내린다. VAE는 실제보다 더 흐릿한 img를 생성하는데, 판별자는 더 선명한 img생성을 독려한다.
③ VQ-GAN은 input pixel을 VAE Decoder의 output pixel과 비교하는 단일 MSE재구성손실을 사용하지 않는다. 대신, Encoder의 중간층과 이에 해당하는 Decoder층에서 feature map간 차이를 계산하는 "perceptural Loss항"을 사용한다.
④ PixelCNN대신 Transformer를 모델의 AR부분으로 사용. Code Sequence를 생성하게 train. Transformer는 VQ-GAN이 완전히 train된 후 별도의 단계에서 train. sliding window내의 token만 사용한다.
ViT VQ-GAN
[Vector-quantized Image Modeling with Improved VQGAN]논문은 VQ-GAN을 더 확장한 버전이다. VQ-GAN의 Convolutional Encoder-Decoder를 Transformer로 교체한다.
∙ Encoder: ViT
img를 일련의 8×8patch로 나누고 이 patch를 tokenize하여 Encoder Transformer에 input으로 주입한다.
구체적으로, ViT VQ-GAN은 겹치지 않는 input_patch를 먼저 펼친 후 저차원 embedding공간으로 투영하고, Positional Embedding을 추가. 그 후, 이 sequence를 ViT에 주입. 만들어진 embedding은 학습된 codebook에 따라 quantize된다.
Decoder Transformer모델이 이런 code를 처리. 전체 Encoder-Decoder모델은 하나의 End2End로 훈련된다: 기존 VQ-GAN모델처럼 훈련의 두번째 단계에서 AutoRegressive Decoder Transformer를 사용해 Code Sequence를 생성한다.
따라서 ViT VQ-GAN에는 GAN Discriminator와 학습된 CodeBook이외에도 총 3개의 Transformer가 존재한다. ViT VQ-GAN으로 생성된 ImageNet Sample Image
5. 요약
prev.
ProGAN[2017]: 선구적으로 개발한 점진적 훈련개념
StyleGAN[2018]: 특정 style벡터 생성 (by mapping신경망 & 다양한 해상도 style삽입)
StyleGAN2[2020]: path length regularization같은 추가개선사항 + AdaIN를 가중치 변조∙복조단계로 대체 이는 신경망을 "점진적으로 훈련하지 않고도" 점진적 해상도개선이라는 좋은 속성을 유지시킴.
SAGAN[2018]: Attention개념을 GAN에 포함, 공간차원에 정보를 분산시켜 장거리 종속성을 감지.
BigGAN[2018]: 위 아이디어를 확장, 더 큰 신경망을 훈련
VQ-GAN[2021]: 여러 유형의 생성모델을 결합, 이산잠재공간이 있는 VAE의 개념을 도입, VQ-VAE를 기반으로 판별자가 적대손실항을 추가해 덜 흐릿하게 VAE가 생성하게함.
ViT VQ-GAN[2021]: 위 아이디어를 확장, Transformer로 Encoder-Decoder를 교체
Diffusion은 GAN과 함께 10년 넘게 Image Generation방법 중 영향력이 큰 생성모델링 기법이다. (GAN의 최대 유행: 2017~2020) Diffusion모델은 여러 Benchmark Dataset에서 기존 최신 GAN보다 성능이 뛰어난데, 특히 txt2img의 OpenAI DALL∙E, Google Imagen등에서 많이 사용된다.
Diffusion모델의 핵심기반 아이디어는 VAE, EBM과 비슷한 점이 존재한다. Contrastive Diffusion(= Score 함수) 사용 대신, Log분포의 Gradient를 직접 추정해 모델을 학습하는 EBM분야인 "Score-based Generative Model"에서 매우 중요하다.
NCSN(Noise Conditional Score Network)라는 모델이 원시 data에 여러 scale의 noise를 적용, Data밀도가 낮은 영역에서도 잘 작동하였다.
img x0를 많은 step(ex. T=1000)동안 점진적으로 손상시켜 최종적으로 "Standard Gaussian Noise"와 구별불가능하게 만든다 가정하자.
img xt-1에 분산 βt를 갖는 소량의 Gaussian Noise를 추가. 새로운 img xt를 생성하는 함수 q를 정의하자. 이 q를 적용하면, 아래처럼 점진적으로 잡음이 커지는 img sequence (x0, ... , xT)를 생성할 수 있다.
이때, update과정은 아래와 같이 수학적으로 표기가능하다.
Reparameterization Trick
🤔 q를 t번 적용하지 않고 이미지 x0+Noise인 xt로 바로 Skip가능하다면 유용하지 않을까?
위 식의 2번째 줄은 2개의 Gaussian분포를 더해 새로운 Gaussian분포를 하나 얻을 수 있다:
따라서 원본 img x0에서 정방향 diffusion과정의 어느단계로든 건너뛸 수 있게 되었다. 또한, 기존의 βt대신, āt값을 사용해 diffusion schedule을 정의할 수 있다. āt: signal(= 원본 x0)로 인한 분산 1-āt: noise(ε)로 인한 분산
∴ Foward Diffusion과정 q는 아래와 같다:
Diffusion Schedule
추가적으로 각 time step마다 다른 β를 자유로이 선택가능하다. 즉, β 나 ā 값이 t에 따라 변하는 방식을 "Diffusion Schedule"이라 한다.
[Ho et al;2020]에서는 Linear Diffusion Schedule을 선택했다. 위 논문에서는 β1=0.0001부터 ,βT=0.02까지 선형적으로 증가한다.
이후 논문에서는 Cosine Diffusion Schedule이 도입되었다. 이때, 코사인 스케줄은 ā를 아래와 같이 정의한다:
위 그림을 보면 Cosine Diffusion Schedule이 더 느리게 상승했음을 확인가능하다. 즉, img에 noise를 점진적으로 추가해 train효율성 및 생성품질을 향상한다.
Reverse Diffusion
noise추가과정을 되돌리는 신경망 pθ(xt-1|xt)를 구축하자. 이때, 신경망 pθ(xt-1|xt)는 q(xt-1|xt)의 역방향 분포를 근사화하는 신경망이다.
이를 통해 N(0, I)에서 random noise sampling 후, reverse diffusion과정을 여러번 적용해 새로운 img를생성할 수 있다:
Train Algorithm
[Reverse Diffusion과 VAE간 비교] ∙ 목표: 신경망으로 random noise를 의미있는 출력으로 변환하는 것. ∙ 차이점: - VAE는 정방향(= img2noise)과정이 모델의 일부 (= 학습됨) - Diffusion: 이에 대한 parameter가 없이 진행.
그렇기에, VAE와 동일한 Loss function을 적용한다.
⭐️ 주의점: Diffusion모델이 실제로 2개의 신경망복사본을 유지한다는 점. 경사하강법으로 train된 신경망과 이전 train step의 신경망 가중치의 EMA(지수이동평균)을 사용하는 또다른 EMA신경망이다.
2. DDPM with U-Net
DDPM with UNet
앞서 신경망의 종류를 확인했다: img에 추가된 noise를 예측하는 신경망 이제, 이에 사용할 신경망 구조를 살펴보자. Skip Connection을 통해 정보가 신경망 일부를 건너뛰고 후속층으로 signal을 흘러보낼 수 있다. 특히나 "출력이 입력크기와 같아야할 때, U-Net이 유용하다".
신경망 후속층에서 사용가능하도록 noise분산값(= 스칼라값)을 더 복잡한 표현이 가능한 고차원 벡터로 변환한다. NeRF논문에서는 문장에서 단어의 이산적인 위치를 벡터로 encoding하는 것이 아닌, 연속적인 값으로 확장했다:
보통, noise embedding길이의 절반이 되게 L=16으로 선택하고 주파수 f의 최대 scaling계수로 ln(1000) / (L-1) 을 택한다.
Residual Block구조는 아래와 같다.
일부 Residual Block에서 Block의 출력과 channel수를 일치시켜야한다. 그렇기에 Skip Connection에 kernel_size=1인 Conv2D층을 추가한다.
Down Block & UpBlock
∙ Down Block
Residual Block으로 channel수를 늘린다. 또한, img_size를 줄이려고 마지막에 AvgPooling층을 적용한다. (UpBlock과의 Skip Connection을 위해 각 Residual Block에 list를 추가해야함.)
∙ UpBlock
UpSampling2D를 진행한다. (보통 ConvTranspose나 Interpolation을 적용.) 연속된 UpBlock은 channel수를 줄이면서 DownBlock의 출력과 연결한다.
import torch
import torch.nn as nn
import torch.nn.functional as F
class SwishActivation(nn.Module):
def forward(self, x):
return x * torch.sigmoid(x)
class ResidualBlock(nn.Module):
def __init__(self, width):
super(ResidualBlock, self).__init__()
self.width = width
self.conv1 = nn.Conv2d(in_channels=width, out_channels=width, kernel_size=3, padding=1)
self.bn1 = nn.BatchNorm2d(width)
self.activation = SwishActivation()
self.conv2 = nn.Conv2d(in_channels=width, out_channels=width, kernel_size=3, padding=1)
self.bn2 = nn.BatchNorm2d(width)
def forward(self, x):
input_width = x.size(1)
if input_width == self.width:
residual = x
else:
residual = nn.Conv2d(in_channels=input_width, out_channels=self.width, kernel_size=1)(x)
x = self.bn1(x)
x = self.activation(x)
x = self.conv1(x)
x = self.bn2(x)
x = self.activation(x)
x = self.conv2(x)
x = x + residual
return x
class DownBlock(nn.Module):
def __init__(self, width, block_depth):
super(DownBlock, self).__init__()
self.width = width
self.block_depth = block_depth
self.res_blocks = nn.ModuleList([ResidualBlock(width) for _ in range(block_depth)])
self.avg_pool = nn.AvgPool2d(kernel_size=2)
def forward(self, x):
x, skips = x
for res_block in self.res_blocks:
x = res_block(x)
skips.append(x)
x = self.avg_pool(x)
return x
class UpBlock(nn.Module):
def __init__(self, width, block_depth):
super(UpBlock, self).__init__()
self.width = width
self.block_depth = block_depth
self.up_sampling = nn.Upsample(scale_factor=2, mode='bilinear', align_corners=False)
self.res_blocks = nn.ModuleList([ResidualBlock(width) for _ in range(block_depth)])
def forward(self, x):
x, skips = x
x = self.up_sampling(x)
for res_block in self.res_blocks:
x = torch.cat([x, skips.pop()], dim=1)
x = res_block(x)
return x
3. DDPM Sampling과 DDIM
DDPM을 이용한 Sampling
훈련된 모델에서 img를 sampling해야한다. 이를 위해 reverse diffusion과정이 필요하다. 즉, random noise에서 시작해 원본이 남을 때 까지 모델로 noise를 점진적으로 제거해야한다.
모델은 random noise추가과정의 마지막 time_step에서 추가된 noise뿐만아니라 trainset의 img에 추가된 noise총량을 예측하게 훈련된다. (다만, 완전한 random noise에서 한번에 img예측은 불가능하다.)
그렇기에 두 단계를 거쳐 xt에서 xt-1로 이동한다. Step 1. 모델의 noise예측을 사용, x0의 추정치 계산 Step 2. 예측 noise를 t-1 step까지만 다시 적용해 xt-1을 생성. 이 과정을 여러단계에 걸쳐 반복하면 조금씩 점진적으로 x0에 대한 추정치로 결국 돌아갈 수 있다.. 아래 식은 이 과정을 수학적으로 보여준다: 위 식을 해석하면 다음과 같다: 첫번째 괄호:신경망 ε(t)θ에 의해 예측된 noise로 계산된 추정이미지 x0 이후 t-1 signal비율인 √āt-1로 scale을 조정, 예측된 noise를 재적용.
두번째 괄호: t-1 noise 비율인 √1 - āt-1 - σ2t로 scale 조정.
세번째 괄호: 추가적인 Gaussian Noise를 더한다.
DDIM (Denoising Diffusion Implicit Model)
DDPM에서 모든 t에 대해 σt=0인 특수한 경우. 즉, DDIM을 사용하면 생성과정이 완전히 "결정론적(deterministic)"하다. 그렇기에 Random Noise가 같다면 항상 동일한 출력을 만든다.
이는 latent space의 sample과 pixel space에서 생성된 출력사이에 잘 정의된 mapping이 있음을 나타낸다.
4. 요약
DDPM이후 DDIM논문의 아이디어는 생성과정을 완전히 결정론적으로 만들었다.
DDPM은 2가지 과정으로 나뉜다.
① 정방향 Diffusion - 일련의 작은 단계로 train data에 noise를 추가. 이때, reparameterization trick으로 어느단계에 해당하는 noise_img라도 계산가능하다. parameter선택 schedule 선택 또한 중요하다.
② 역방향 Diffusion
- 추가된 noise를 예측하는 모델로 구성. noise_img와 해당단계 noise비율이 주어지면 각 time step에서 noise를 예측하는 U-Net으로 구현된다. UpBlock: img 크기를 늘리고 channel수를 줄임. DownBlock: img 크기를 줄이고 channel수를 늘림. noise비율은 사인파임베딩(sinusoidal embedding)으로 encoding
역방향 Diffusion 단계를 늘리면 속도는 느려지나 img생성품질은 향상된다. 또한, 두 img 사이를 보간하기 위해 latent space연산을 수행한다.
에너지함수 Eθ(x)는 parameter가 θ이고 입력 img가 x를 하나의 스칼라값으로 변환하는 신경망. 이때, 여기에 Swish활성화함수를 사용한다. swish함수
이 Swish함수는 Gradient Vanishing을 완화시키는데, 특히 EBM에서 중요하다. 이 신경망은 일련의 Conv2D를 쌓아 img크기를 줄이고 channel크기를 늘린다. 마지막 층은 Linear Activation이기에 이 신경망은 (-∞,∞)의 범위값을 출력한다. 해당 코드는 아래와 같다.
🤔 How to... 에너지점수가 낮은 new sample생성? 랑주뱅 동역학기법:입력에 대한 Energy함수의 Gradient계산하는 기법 Step 1. Sample space의 임의의 지점에서 시작 Step 2. 계산된 Gradient의 반대방향으로 조금씩 이동, Energy함수를 감소. Step 3. train이후, random noise가 trainset과 유사한 img로 변환됨.
stochastic gradient Langevin Dynamic:
sample space 이동 시, 위 그림처럼 input에 소량의 random noise추가. (만약, 그렇지 않으면 local minima에 빠질 수 있음.)
다만, 일반 SGD랑은 당연히 차이가 있다. ∙ SGD: (-) 기울기방향으로 파라미터 조금씩 update, Loss최소화
∙ SGD with Langevin Dynamic: 여기에 Langevin Dynamic을 사용하면 Gradient Weight를 고정하고, input에 대한 출력의 Gradient를 계산한다. 그 후 (-) 기울기방향으로 input을 조금씩 update, 점진적으로 출력(energy score)을 최소화한다.
두 방식 모두 동일한 경사하강법을 활용하나, 다른 목적함수에 적용된다. 이론적인 Langevin Dynamic 방정식. [Langevin Sampling함수]:
def generate_samples(model, inp_imgs, steps=60, step_size=10, return_img_per_step=False):
"""
Function for sampling images for a given model.
Inputs:
model - Neural network to use for modeling E_theta
inp_imgs - Images to start from for sampling. If you want to generate new images, enter noise between -1 and 1.
steps - Number of iterations in the MCMC algorithm.
step_size - Learning rate nu in the algorithm above
return_img_per_step - If True, we return the sample at every iteration of the MCMC
"""
# Before MCMC: set model parameters to "required_grad=False"
# because we are only interested in the gradients of the input.
is_training = model.training
model.eval()
for p in model.parameters():
p.requires_grad = False
inp_imgs.requires_grad = True
# Enable gradient calculation if not already the case
had_gradients_enabled = torch.is_grad_enabled()
torch.set_grad_enabled(True)
# We use a buffer tensor in which we generate noise each loop iteration.
# More efficient than creating a new tensor every iteration.
noise = torch.randn(inp_imgs.shape, device=inp_imgs.device)
# List for storing generations at each step (for later analysis)
imgs_per_step = []
# Loop over K (steps)
for _ in range(steps):
# Part 1: Add noise to the input.
noise.normal_(0, 0.005)
inp_imgs.data.add_(noise.data)
inp_imgs.data.clamp_(min=-1.0, max=1.0)
# Part 2: calculate gradients for the current input.
out_imgs = -model(inp_imgs)
out_imgs.sum().backward()
inp_imgs.grad.data.clamp_(-0.03, 0.03) # For stabilizing and preventing too high gradients
# Apply gradients to our current samples
inp_imgs.data.add_(-step_size * inp_imgs.grad.data)
inp_imgs.grad.detach_()
inp_imgs.grad.zero_()
inp_imgs.data.clamp_(min=-1.0, max=1.0)
if return_img_per_step:
imgs_per_step.append(inp_imgs.clone().detach())
# Reactivate gradients for parameters for training
for p in model.parameters():
p.requires_grad = True
model.train(is_training)
# Reset gradient calculation to setting before this function
torch.set_grad_enabled(had_gradients_enabled)
if return_img_per_step:
return torch.stack(imgs_per_step, dim=0)
else:
return inp_imgs
Contrastive Divergence
앞서 Sampling space에서 낮으 energy를 갖는 새로운 point를 sampling하는 방법을 알아보았으니 모델 train쪽을 알아보자.
Energy함수는 확률을 출력하지 않는다 = MLE를 적용할 수 없다. (물론, 항상 그렇듯 NLL Loss최소화가 목적이다.) pθ(x)가 energy함수 Eθ(x)를 포함하는 Boltzmann 분포일 때, 이 값의 Gradient는 아래와 같다: 위 식은 직관적으로 이해가능하다: ∙ 실제 sample에 대해 큰 음의 energy score ∙ 생성된 가짜 sample에 대해 큰 양의 energy score를 출력하도록 모델 훈련 → 두 극단 간의 차이가 가능한 커지도록하여 이를 손실함수로 이용.
이때, 가짜 sample의 Energy score를 계산하려면 분포 pθ(x)에서 정확히 sampling가능해야한다. 다만, 분모계산이 어려워 불가능하다. → 대신, Langevin Sampling방법을 사용해 낮은 energy score를 갖는 sample set 생성이 가능하다.
또한, 이전 반복 sample을 buffer에 저장, 다음 batch시작점으로 순수noise대신에 이를 사용한다.
def sample_new_exmps(self, steps=60, step_size=10):
"""
Function for getting a new batch of "fake" images.
Inputs:
steps - Number of iterations in the MCMC algorithm
step_size - Learning rate nu in the algorithm above
"""
# Choose 95% of the batch from the buffer, 5% generate from scratch
n_new = np.random.binomial(self.sample_size, 0.05)
rand_imgs = torch.rand((n_new,) + self.img_shape) * 2 - 1
old_imgs = torch.cat(random.choices(self.examples, k=self.sample_size-n_new), dim=0)
inp_imgs = torch.cat([rand_imgs, old_imgs], dim=0).detach().to(device)
# Perform MCMC sampling
inp_imgs = Sampler.generate_samples(self.model, inp_imgs, steps=steps, step_size=step_size)
# Add new images to the buffer and remove old ones if needed
self.examples = list(inp_imgs.to(torch.device("cpu")).chunk(self.sample_size, dim=0)) + self.examples
self.examples = self.examples[:self.max_len]
return inp_imgs
이때, 각 step이 종료된 후, score normalization하지 않고 알고리즘에 따라 sample score는 아래로 내려가고, 가짜 sample의 점수는 위로 올라간다. 최종코드는 링크 참고.
EBM은 이후 score matching이라는 훈련기법으로 발전하였고, 이는 Denoising Diffusion Probabilistic Model이라는 모델로 발전해 DALLE∙2 및 Imagen같은 최첨단 생성모델 구현에 사용된다.
3. 기타 (BM, RBM)
Boltzmann Machine
EBM의 초기 사례중 하나로 fully connected undirected neural network이다. v: visible unit h: hidden unit W, L, J는 학습되는 Contrastive Divergence로 train. 이때, 균형점을 찾을 때 까지 v와 h사이를 번갈어서 Gibbs Sampling을 수행한다.
단점: train속도가 매우 느리고 hidden unit개수를 크게 늘릴 수 없다.
Restricted Boltzmann Machine
위의 Boltzmann machine을 확장한 RBM은 같은 종류 unit사이 connection을 제거, 2개 층으로 구성된 이분그래프(bipartite graph)를 생성한다. 이를 통해 RBM을 쌓아 더 복잡한 분포를 모델링하는 심층신뢰신경망(Deep Belief Network)을 만들 수 있다.
단점: Mixing Time(= Gibbs Sampling으로 목표상태에 도달하는데 걸린시간)이 긴 Sampling이 여전히 필요. 따라서 여전히 고차원 data 모델링에는 현실적으로 어렵다.
4. 요약
Deep EBM의 Sampling은 Langevin Dynamic으로 이뤄진다. 이 기법은 input img에 대한 점수의 gradient를 사용하여 gradient가 감소하는 방향으로 조금씩 input을 update한다.
앞서 설명한 VAE, GAN, AR모델은 분포 p(x)모델링문제해결을 위해 Sampling가능한 latent variable이나 (=VAE Decoder, GAN Generator) 이전 pixel값에 대한 함수로 분포를 모델링(=AR)한다. Normalizing Flow와의 공통점: ∙ AR처럼 다루기 쉽고 명시적인 data생성분포 p(x)를 모델링 ∙ VAE처럼 data를 Gaussian분포처럼 간단한 분포에 mapping
Normalizing Flow의 다른점: ∙ mapping함수의 형태에 제약을 둔다. (= 이 함수는 반전가능해야하고, 이를 이용해 새 data point생성이 가능해야함.)
1. Normalizing Flow
Normalizing Flow는 VAE와 상당히 유사하다.
VAE
Encoder학습 → 복잡한 분포와 sampling가능한 간단한 분포를 mapping → Decoder학습 → 단순한분포에서 복잡한 분포로 mapping ∴ 단순한 분포에서 point z를 sampling, 학습된 변환을 적용 시, 새 data point를 얻음. 즉, Decoder는 p(x|z)를 모델링. Encoder는 p(z|x)의 근사치인 q(z|x)로 둘은 완전히 다른 신경망임.
Normalizing Flows
Decoding함수 = Encoding함수의 역함수 (= 계산이 용이함.) But❗️신경망은 기본적으로 반전가능한 함수가 아니다! 이를 위해 변수변환이라는 기법이 필요한 것.
Change of Variables
변수변환:
px(x)가 2차원 x = (x1, x2) 직사각형 X위에 정의되어있다 가정하자.
이 함수를 주어진 분포영역에서 적분하면 1이 된다. (x1의 범위: [1,4] , x2:[0.2]) 따라서 아래와 같이 잘 정의된 확률분포를 정의할 수 있다: 이 분포를 이동하고 scaling하여 단위 정사각형 Z에 대해 정의한다 가정하자. 이를 위해 새로운 z=(z1, z2)변수와 X의 각 point를 정확히 Z의 한 Point에 mapping하는 함수 f를 정의할 수 있다: 이런 함수를 가역함수(invertible function)라 한다. 즉, 모든 z를 이에 해당하는 x로 다시 mapping가능한 g함수가 존재한다. (변수변환에 필수적.) 이 g함수가 없으면 두 공간사이를 mapping할 수 없다. 이때, pz(z) = ((3z1+1)-1)(2z2)/9 = 2z1z2/3로 변환가능하다. 다만, pz(z)를 단위면적에 대해 적분하면 아래와 같은 문제에 봉착한다: 1/6값이 나왔기에, 더이상 유효한 확률분포가 아니다. (적분결과가 1이 되어야함.) 원인:변환된 확률분포 영역이 원본의 1/6로 작아졌기 때문. 해결:새로운 확률분포의 상대적면적변화에 해당하는 정규화계수를 곱해야함 → 변환에 의한 부피변환 = Jacobian Matrix의 절댓값.
Jacobian Matrix:
z = f(x)의 Jacobain Matrix는 1계 편미분도함수의 행렬이다.
x1에 대한 z1의 편미분은 1/3이고, x1에 대한 z2의 편미분은 0이며, x2에 대한 z2의 편미분은 0이고, x2에 대한 z2의 편미분은 1/2이다.
따라서 함수 f(x)의 Jacobian Matrix는 아래와 같다. 행렬식(determinant)은 정방행렬(square matrix)에서만 정의된다. 해당행렬로 표현되는 변환을 단위 초입방체에 적용해 만들어진 평행육면체 부피와 같다. 즉, 2차원 행렬로 표현되는 변환을 단위 정사각형에 적용해 만들어진 평행사변형의 면적에 해당한다.
앞선 예시를 보자. det(a b c d) = ad - bc이므로 위 예시에서 Jacobian 행렬식은 1/3 × 1/2 = 1/6이다. 이값이 바로 scaling계수이다.
변수 변환 방정식
하나의 방정식으로 X와 Z사이 변수변환과정을 설명할 수 있다. 이를 "변수변환방정식(change of variables equation)"이라 한다.
간단한 분포라면, X→Z로 mapping할 적절한 가역함수 f(x)와 sampling된 z를 기존 domain point x로 다시 mapping 시 사용할 역함수 g(z)를 찾으면 된다. 이를 위해, 필요한 2가지 문제가 존재한다.
① 고차원 행렬식은 Too Hight Cost. O(n3)시간이 든다. ② f(x)의 역함수 계산이 명확하지 않다.
위의 문제들 해결을 위해 RealNVP라는 변환기법으로 수행가능하다.
2. RealNVP
prev.
RealNVP는 복잡한 data분포를 간단한 Gaussian분포로 변환하는 신경망을 만들 수 있다. 또한, 역변환이 가능하고 Jacobian Matrix를 쉽게 계산할 수 있다.
Coupling Layer
Coupling층은 input원소에 대해 scale계수와 translation계수를 만든다.
아래 예시처럼 Linear층으로 Scale출력을 만들고, 또다른 Linearfh translation계수를 만든다.
Coupling Layer는 input data가 들어갈 때, Masking 후 변환되는 방식이 독특하다.
Step 1.처음 d차원만 Coupling Layer에 주입. Step 2.남은 D-d차원은 완전히 Masking(= 0으로 설정.)
이때, 많은 정보를 Masking하는 이유가 뭘까? 이를 알기위해, 이 함수의 Jacobian Matrix를 살펴보면 알 수 있다.
장점 1
하삼각행렬형태로 하삼각행렬의 행렬식은 단순히 대각원소의 곱과 같다. 즉, 좌측하단의 복잡한 도함수와 상관이 없어진다!
장점 2
아래 그림 및 식을 보면, 쉽게 역전할 수 있는 함수라는 목표달성이 가능하다. 정방향계산을 재정렬하면, 아래와 같은 역함수식을 만들 수 있다.
Step 3. 그러면 이제 처음 입력의 d개 원소를 어떻게 update해야할까?
Coupling Layer 쌓기
선형대수학의 행렬식 조건 따라서 위의 선형대수학 행렬식조건에 따라 Coupling Layer를 쌓고, 매번 Masking을 뒤집으면, 간단한 Jacobian Matrix와 가역성이라는 필수속성을 유지하며 전체 input tensor를 변환하는 신경망을 만들 수 있다.
RealNVP 모델훈련
class RealNVP(nn.Module):
def __init__(self, input_dim, output_dim, hid_dim, mask, n_layers = 6):
super().__init__()
assert n_layers >= 2, 'num of coupling layers should be greater or equal to 2'
self.modules = []
self.modules.append(CouplingLayer(input_dim, output_dim, hid_dim, mask))
for _ in range(n_layers-2):
mask = 1 - mask
self.modules.append(CouplingLayer(input_dim, output_dim, hid_dim, mask))
self.modules.append(CouplingLayer(input_dim, output_dim, hid_dim, 1 - mask))
self.module_list = nn.ModuleList(self.modules)
def forward(self, x):
ldj_sum = 0 # sum of log determinant of jacobian
for module in self.module_list:
x, ldj= module(x)
ldj_sum += ldj
return x, ldj_sum
def backward(self, z):
for module in reversed(self.module_list):
z = module.backward(z)
return z
mask = torch.from_numpy(np.array([0, 1]).astype(np.float32))
model = RealNVP(INPUT_DIM, OUTPUT_DIM, HIDDEN_DIM, mask, N_COUPLE_LAYERS)
optimizer = torch.optim.Adam(model.parameters(), lr=1e-4)
prior_z = distributions.MultivariateNormal(torch.zeros(2), torch.eye(2))
Loss functions: NLL Loss
def train(epoch):
model.train()
train_loss = 0
for batch_idx, data in enumerate(train_loader):
optimizer.zero_grad()
z, log_det_j_sum = model(data)
loss = -(prior_z.log_prob(z)+log_det_j_sum).mean()
loss.backward()
cur_loss = loss.item()
train_loss += cur_loss
optimizer.step()
if batch_idx % LOG_INTERVAL == 0:
print('Train Epoch: {} [{}/{} ({:.0f}%)]\tLoss: {:.6f}'.format(
epoch, batch_idx * len(data), len(train_loader.dataset),
100.*batch_idx / len(train_loader),
cur_loss/len(data)))
print('====> Epoch: {} Average loss: {:.4f}'.format(
epoch, train_loss / len(train_loader.dataset)
))
[train 결과]:
복잡한 input이 단순한 Gaussian분포형태로 mapping됨.
3. GLOW &. FFJORD
GLOW
고품질 sample생성 및 유의미한 latent space 생성간으. 핵심: 역마스킹 설정을 반전가능한 1×1 conv로 대체 오죽하면 논문제목도 Glow: Generative Flow with Invertible 1x1 Convolutions 이를 이용해 모델이 원하는 채널순으로 조합생성 가능.
FFJORD
Discrete time Normalizing Flows: RealNVP, GLOW Continuous time Normalizing Flows: FFJORD
즉, Normalizing Flow의 단계수가 무한대이고, 단계크기가 0에 가깝기에 신경망에 의해 parameter가 정의되는 상미분방정식(ODE)을 사용해 data분포와 표준정규분포사이 변환을 모델링:
4. 요약
Normalizing Flow란, 다루기 쉬운 data밀도함수를 만들면서 고품질 sample을 생성하는 강력한 생성모델링방법이다. 이를 위해 신경망의 형태를 제한해 가역성과 계산하기 쉬운 Jacobian determinant를 계산한다.
VAE와 GAN: latent variable을 활용. 두 경우 모두 sampling이 쉬운 분포를 갖는 새로운변수를 도입, 이 변수를 기존 domain으로 다시 decoding하는 방식.
AR Model:생성모델링을 순차적과정으로 진행. 즉, latent variable이 아닌, sequence에 있는 이전값을 바탕으로 예측. 그렇기에 Data생성분포를 근사하는것이 아닌, 명시적으로 Data생성분포를 모델링한다.
이번에는 2가지 AR모델인 LSTM과 PixelCNN을 살펴볼 것이다. ∙ LSTM: text data ∙ PixelCNN: img data
이후, 또다른 성공적인 AR모델인 Transformer에 대해 논의할 것이다.
1. LSTM
Tokenization
LSTM은 RNN의 한 종류로 sequential data처리에 용이하다. tokenization은 텍스트를 단어나 문자같은 개별단위로 나누는 작업이다.
텍스트생성모델로 만들려는 종류에 따라 text tokenization방법이 달라지는데, word와 sentence token은 각기 장단점이 존재한다.
word token의 경우:
vocab이 매우 크며, 단어의 stem(어간)추출이 가능하다. ex) browse, browsing, browses는 모두 어간이 brows이다. 단어토큰화는 train_vocab에 없는 단어는 모델이 예측불가능하다.
character token의 경우:
모델이 문자의 sequence를 생성, train_vocab에 없는 새로운 단어 생성. 대문자를 소문자로 바꾸거나 별도의 token으로 남길 수 있다. 문자토큰화는 vocab_size가 비교적 매우 작아 마지막층에서 학습할 가중치 수가 적어 빠른 훈련속도를 갖는다.
2. PixelCNN
prev.
이전 픽셀을 기반으로 픽셀단위로 img를 생성하는 모델이다. [Aaron;2016] 이를 PixelCNN이라하며, AR방식으로 img를 생성하도록 훈련시킨다.
AR모델은 뛰어난 품질의 결과물을 얻을 수 있지만 PixelCNN같은 AR모델의 주요단점은 순차적인 sampling과정으로 인해 sampling속도가 느리다는 점이다. (∵ VAE는 한번의 예측으로 충분하지만 PixelCNN은 이전의 모든 픽셀을 기반으로 다음 픽셀을 예측하기 때문.)
PixelCNN을 이해하기 위해 필요한 2가지 개념 이해가 필요하다. ∙ Masked Convolution Layer ∙ Residual Block
Masked Convolution Layer
Conv층은 feature탐지에는 유용하나 pixel에 순서가 지정되지 않는다. 따라서 Auto-Regressive방식을 직접적으로 사용할 수 없다.
그렇기에 Conv층에 AR방식으로 이미지생성에 적용하기 위해 ① pixel순서를 지정하고 filter가 해당 pixel앞의 픽셀만 볼 수 있게 해야한다. ② 현재 img에 conv_filter를 적용하고 ③ 앞의 모든 Pixel로부터 다음 pixel의 값을 예측해 한번에 하나의 pixel씩 img를 생성한다.
Zig-Zag순으로 Masking을 진행하며, 1과 0으로 구성된 mask에 fitler weight matrix를 곱해 현재 pixel뒤에 있는 모든 pixel값이 0이 되게한다. 아래그림을 보면, 2종류의 Mask가 있음을 알 수 있다. ∙ Mask A: 중앙 pixel값이 Masking되는 유형 ∙ Mask B: 중앙 pixel값이 Masking되지 않는 유형 좌) 중심픽셀값 분포 예측을 위해 일련의 픽셀에 적용된 마스크 / 우) Masked Conv filter
초기 Masked Conv층(= input image에 직접적용되는 층)에서는 중앙픽셀=신경망이 예측해야할 픽셀이기에 해당 중앙픽셀을 사용할 수 없다. (= Mask A) 하지만 후속층(= Mask B)에서는 중앙픽셀을 활용할 수 있다.
이는 기존 input img의 이전 pixel에서 얻은 정보만으로 계산된 결과이기 때문이다.
핵심구성요소 중 하나로 Residual Block이 사용된다. residual block은 input signal을 그대로 유지하는데, 이는 최적의 변환일 때 중간층의 가중치를 0으로 만들어 쉽게 달성가능하기 때문이다. (Skip Connection이 없으면 신경망이 중간층으로 input과 동일해지는 mapping을 찾아야하는데 Cost가 너무 많이 든다.) 이때, 중간의 3x3 Conv는 MaskedConv2D를 사용한다.
2017년, PixelCNN의 느린 훈련과정 완화를 위해 이산 픽셀값에 대한 softmax출력대신 혼합분포로 만들었다. (즉, 혼합분포에서 Sampling을 한다는 의미.)
이를 위해서 다음 단계를 거친다. 1. 먼저 categorical분포에서 sampling 2. 특정 하위분포를 선택 3. 일반적인 방법으로 이 분포에서 sampling.
이전과의 큰 차이점: 픽셀값의 전체 범위를 활용한다는 점.
3.1. Transformers
prev.
LSTM, GRU같은 순환신경망 종류들은 텍스트를 순차적으로 만드는 AR model들이다. 이런 AR모델은 sequential data를 한번에 하나씩 처리하여 hidden vector를 지속해서 update한다.
Transformer는 순환구조가 필요없고, 순차모델링을 위해 Attention Mechanism에만 의존한다. RNN은 병렬화가 어려우나 Transformer는 병렬화가 용이해 Large Dataset에서 훈련할 수 있다.
이번에는 OpenAI의 핵심 AR모델인 GPT를 살펴볼 것이다.
Attention Mechanism (Attention Head)
문장의 특정단어에 attention을 하며, 다른 단어들은 거의 무시하는 뉘앙스로 작동한다. 즉, 관련없는 detail로 유용한 정보가 가려지는 것을 막고, 효율적으로 추출하기위해 입력의 어느위치에서 정보를 가져올 지 결정하는 것으로 추론 시 정보를 찾을 위치를 결정할 수 있다.
Query, Key, Value
Attention Head는 일종의 정보검색시스템으로 생각할 수 있다. Q가 K/V로 만들어지고, 출력은 Q와 각 K간의 유사성에 따라 가중치가 부여된 값의 합이다. Q: 현재 당면한 작업에 대한 표현 K: 문장의 각 단어에 대한 표현 V: 문장에 있는 단어의 표현 attention의 출력은 context vector이다.
Decoder에서 미래의 단어정보누출을 방지하기위해 QK dot곱에 Mask를 적용해야한다. 이를 Causal Masking이라 한다. Huggingface GPT2 구현 일부: cf) 참고코드
if attention_mask is not None:
assert batch_size > 0, "batch_size has to be defined and > 0"
attention_mask = attention_mask.view(batch_size, -1)
attention_mask = attention_mask.unsqueeze(1).unsqueeze(2)
attention_mask = attention_mask.to(dtype=next(self.parameters()).dtype) # fp16 compatibility
attention_mask = (1.0 - attention_mask) * -10000.0
Batch Norm v.s Layer Norm
Transformer는 학습과정안정성을 위해 Layer Normalization을 사용한다. Batch Norm: 각 채널의 출력의 평균이 0, 표준편차가 1이 되도록 정규화하는 방법. (이때, batch차원과 공간차원에 걸쳐 정규화 통계치가 계산된다.)
Layer Norm:채널차원에 대해 정규화 통계치가 계산된다. 따라서 batch에 있는 각 sequence위치마다 정규화한다. (이때, 정규화 통계치 계산방식이 BN과 완전히 반대이다.)
Positional Encoding
MHA층에서 K의 순서를 고려하는 부분이 없는데, 각 K와 Q의 dot곱에서 순차적 계산이 아닌, 병렬로 계산된다. 이를 위해 Transformer블록의 input을 위해 Positional Encoding을 사용한다.
token embedding
또한, token embedding을 사용해 token만 encoding하지 않고 positional encoding으로 token의 위치도 encoding한다.
3.2. Transformer etc.
T5 (Encoder-Decoder)
Encoder-Decoder구조를 이용한 최근 Transformer의 예시로 Google에서 발표한 T5(Text-To-Text Transfer Transformer)모델은 NMT, Linguistic Acceptability(언어적 적합성), Similarity, Summarization을 포함한 다양한 작업을 수행한다. T5의 주요 특징은 다음과 같다:
1. Text-To-Text Framework: - T5는 "문장을 문장으로"라는 원칙을 따르며, 모든 NLP 작업을 텍스트 입력으로 받아들이고 텍스트 출력으로 생성하는 일관된 프레임워크를 가지고 있는데, 이로써 다양한 자연어 처리 작업을 통일된 방식으로 다룰 수 있다.
2. Transformer Architecture: - T5는 Transformer 아키텍처를 기반이기에 Transformer는 Attention 메커니즘을 사용하여 시퀀스 데이터의 긴 범위의 의존 관계를 모델링하는데 효과적이다.
3. Pretraining and Fine-tuning: - T5는 대규모 텍스트 코퍼스에서 사전 훈련되었으며, 이렇게 사전 훈련된 모델은 다양한 자연어 처리 작업에 대해 효과적인 전이 학습을 가능하게 한다.
4. Task-Agnostic Representation: - T5는 작업에 특화된 task-specific한 head를 추가하지 않고, 단일 통합된 모델을 사용한다. - 이런 특성은 모델을 다양한 작업에 적용하는 것을 용이하게 한다.
5. Scalability: - T5는 다양한 모델 크기로 사용 가능하며, 작은 모델에서부터 매우 큰 모델까지 다양한 크기의 모델이 제공된다.
T5는 자연어 생성, 기계 번역, 질문 응답, 감정 분석, 문서 분류 등 다양한 NLP 작업에 사용될 수 있다.
ChatGPT
GPT-3보다 강력하고, 대화형식에 맞게 fine-tuning된 모델인 GPT-3.5가 사용된다.
ChatGPT가 GPT-3.5를 Fine-Tuning할 때, "RLHF(Reinforcement Learning from Human Feedback)"라는 테크닉을 사용한다.
ChatGPT train과정 (RLHF)
Step 1. Supervised Learning Fine Tuning 사람이 작성한 대화형 입력프롬프트 + 원하는 출력으로 구성된 dataset 수집 이 dataset으로 Supervised Learning의 기본언어모델(GPT-3.5)를 Fine-Tuning
Step 2. Reward Modeling label을 부여할 사람에게 prompt와 몇가지 sampling 모델출력을 제시. worst~best까지 출력순위를 지정하도록 요청. 대화기록이 주어지면 각 출력의 score를 예측하는 reward model을 학습.
Step 3. Reinforcement Learning 1단계에서 Fine tuning된 모델로 초기화된 LM이 policy가 되는 RL로 대화를 처리. 현재 state(= 대화기록)이 주어지면, policy는 action(= token sequence)를 출력.
2단계에서 학습된 Reward model로 해당출력의 score를 매긴 후 PPO로 보상 극대화하도록 LM의 가중치 조정
ChatGPT가 GPT-3.5를 Fine-Tuning할 때, "RLHF"라는 테크닉을 사용한다.
목표: 판별자가 판별 불가능한 img생성 input: 다변량표준정규분포에서 뽑은 벡터 output: 원본 train data에 있는 img와 동일한 크기의 img 위 설명이 마치 VAE같다면? 실제로 VAE의 Decoder와 동일한 목적을 수행한다. latent space의 벡터를 조작, 기존 domain에서 img의 고수준 특성을 바꾸는 기능을 제공하기 때문.
batch img생성→판별자에 통과→각 img에 대한 점수 get. ∙ G_Loss: BCELoss (0: fake img / 1: real img) ∙ D_Loss: BCELoss (0: fake img / 1: real img) 이때, 한번에 한 신경망 가중치만 update되도록 두 신경망을 번갈아 train해줘야함.
이때, DCGAN훈련과정이 불안정할 수 있다. (∵ 판별자와 생성자가 우위를 차지하려 서로 계속 경쟁하기 때문.) 시간이 충분히 지나면, 판별자가 우세해지는 경향이 있다. 다만, 이시점에는 생성자가 충분히 고품질 Img생성이 가능해서 큰 문제는 되지 않는다.
Label Smoothing
또한, GAN에 random noise를 조금 추가하면 유용한데, train과정의 안정성 개성 및 img선명도가 증가한다. (마치 Denoise Auto Encoder와 같은 느낌.)
GAN 훈련 팁 & Trick
∙ D >> G 인 경우.
판별자가 너무 강하면 Loss신호가 너무 약해진다. 이로 인해 생성자에서 의미있는 향상을 도모하기 어려워진다. 따라서 다음과 같이 판별자를 약화할 방법이 필요하다.
∙ 판별자에 Dropout rate 증가.
∙ 판별자의 LR 감소.
∙ 판별자의 Conv filter 수 감소.
∙ 판별자 훈련 시, Label에 Noise추가. (Label Smoothing)
∙ 판별자 훈련 시, 일부 img의 label을 random으로 뒤집는다.
∙ G >> D 인 경우.
mode collapse: 생성자가 거의 동일한 몇개의 img로 판별자를 "쉽게 속이는 방법" mode: 판별자를 항상 속이는 하나의 sample.
생성자는 이런 mode를 찾으려는 경향이 있고, latent space의 모든 point를 이 img에 mapping가능하다. 또한, 손실함수의 Gradient가 0에 가까운값으로 붕괴(collapse)하기에 이상태에서 벗어나기 어려워진다.
∙ 유용하지 않은 Loss
손실이 작을수록 생성된 img품질이 더 좋을 것이라 생각할 수 있다. 하지만 생성자는 현재 판별자에 의해서만 평가된다. 판별자는 계속 향상되기에 train과정의 다른지점에서 평가된 손실을 비교할 수 없다. 즉, 판별Loss는 감소하고, 생성Loss는 증가한다.→ GAN train과정 모니터링이 어려운 이유.
2. WGAN-GP (Wasserstein GAN with Gradient Penalty)
GAN Loss
GAN의 판별자∙생성자 훈련 시 사용한 BCE Loss를 살펴보자. 판별자 D훈련: real_img에 대한 예측 pi=D(xi)와 target yi=1을 비교. 생성자 G훈련: 생성_img에 대한 예측 pi=D(G(zi))와 target yi=0을 비교.
[GAN D_Loss 최대화 식]: [GAN G_Loss 최소화 식]:
Wesserstein Loss
[GAN Loss와의 차이점]: ∙ 1과 0대신, yi = 1, yi = -1을 사용. ∙ D의 마지막층에서 sigmoid제거. → 예측 pi가 [0,1]범위에 국한되지 않고 [-∞,∞] 범위의 어떤 숫자도 될 수 있게함. 위의 이유들로 WGAN의 판별자는 보통 비평자(Critic)라 부르며, 확률대신 점수"score"를 반환한다.
[Wesserstein Loss함수]:
WGAN의 critic D를 훈련하기위해 real_img에 대한 예측(D(xi))과 타겟(= 1)을 비교. 생성_img에 대한 예측(D(G(zi)))과 타겟(= -1)을 비교. ∴ 손실을 계산
[WGAN Critic D_Loss 최소화]: real과 생성간의 예측차이 최대화. [WGAN G_Loss 최소화]: Critic에서 가능한 높은 점수를 받는 img생성. (= Critic을 속여 real_img라 생각하게 만드는 것.)
1-Lipshitz Continuous function
sigmoid로 [0,1]범위에 국한하지 않고 Critic이 [-∞,∞] 범위의 어떤 숫자도 될 수 있게한다는 점은 Wessertein Loss가 제한없이 아주 큰 값일 수 있다는 것인데, 보통 신경망에서 큰 수는 피해야한다.
그렇기에, "Critic에 추가적인 제약이 필요"하다. 특히, Critic은 1-Lipshitz 연속함수여야 하는데, 이에대해 살펴보자.
Critic은 하나의 img를 하나의 예측으로 변환하는 함수 D이다. 임의의 두 input_img x1, x2에 대해 다음 부등식을 만족하면, 이 함수를 1-Lipshitz라 한다: |x1-x2| : 두 img 픽셀의 평균적인 절댓값 차이 |D(x1) - D(x2)| : Critic 예측간의 절댓값 차이 Lipshitz Continuous Function 기본적으로 기울기의 절댓값이 어디에서나 최대 1이어야한다 = 두 img간 Critic예측변화비율 제한이 필요하다는 의미.
WGAN-GP
WGAN의 Critic의 가중치를 작은 [-0.01, 0.01]범위에 놓이도록 train batch 이후 weight clipping으로 Lipshitz제약을 부과한다.
이때, 학습속도가 크게 감소하기에 Lipshitz제약을 위해 다른 방법을 적용한다: 바로 Wesserstein GAN-Gradient Penalty이다.
[WGAN-GP]: Gradient Norm이 1에서 벗어나면 모델에 불이익을 주는 방식이다. [Gradient Penalty Loss]: input_img에 대한 예측의 Gradient Norm과 1사이 차이를 제곱한 것. 모델은 자연스레 GP항을 최소화하는 가중치를 찾으려하기에 이 모델은 립시츠 제약을 따르게 한다.
Train과정동안 모든곳에서 Gradient계산은 힘들기에 WGAN-GP는 일부지점에서만 Gradient를 계산한다. 이때, real_img와 fake_img쌍 간의 interpolation img를 사용한다.
from torch.autograd import Variable
from torch.autograd import grad as torch_grad
def gradient_penalty(self, real_data, generated_data):
batch_size = real_data.size()[0]
# Calculate interpolation
alpha = torch.rand(batch_size, 1, 1, 1)
alpha = alpha.expand_as(real_data)
interpolated = alpha*real_data.data + (1 - alpha)*generated_data.data
interpolated = Variable(interpolated, requires_grad=True)
# Calculate probability of interpolated examples
prob_interpolated = self.D(interpolated)
# Calculate gradients of probabilities with respect to examples
gradients = torch_grad(outputs=prob_interpolated, inputs=interpolated,
grad_outputs=torch.ones(prob_interpolated.size()).cuda() if self.use_cuda else torch.ones(
prob_interpolated.size()),
create_graph=True, retain_graph=True)[0]
# Gradients have shape (B,C,W,H)
# so flatten to easily take norm per example in batch
gradients = gradients.view(batch_size, -1)
self.losses['gradient_norm'].append(gradients.norm(2, dim=1).mean().data[0])
# Derivatives of the gradient close to 0 can cause problems because of
# the square root, so manually calculate norm and add epsilon
gradients_norm = torch.sqrt(torch.sum(gradients**2, dim=1) + 1e-12)
# Return gradient penalty
return self.gp_weight * ((gradients_norm-1)**2).mean()
[WGAN-GP에서의 Batch Normalization]
BN은 같은 batch안의 img간의 correlation을 만든다. 그렇기에 gradient penalty loss의 효과가 떨어지에 WGAN-GP는 Critic에서 BN을 사용해서는 안된다.
3. CGAN (Conditional GAN)
prev.
앞서 설명한 모델들은 "주어진 trainset에서 사실적인 img를 생성하는 GAN"이었다. 하지만, "생성하려는 img의 유형을 제어할 수 는 없었다." (ex. 생성하려는 img유형: 크거나 작은 벽돌, 흑발/금발 등등)
latent space에서 random한 하나의 point sampling은 가능하다. latent variable을 선택하면 어떤 종류의 img가 생성될 지 쉽게 파악가능하다.
CGAN
[GAN v.s CGAN]: CGAN은 GAN과 달리 "label과 관련된 추가정보를 생성자와 critic에 전달한다는 점"이다. ∙ 생성자: 이 정보를 one-hot encoding vector로 latent space sample에 단순히 추가. ∙ Critic: label 정보를 RGB img의 채널에 추가채널로 추가. → input img가 동일한 크기가 될 때 까지 one-hot encoding vector를 반복. [유일한 구조 변경사항]: label정보를 G,D의 기존 입력에 연결하는 것.
Encoding: Data를 저차원 latent space의 특정 point에 mapping시키는 것. Decoding: 이 point의 위치를 받아 해당 Data를 다시 생성하려 시도하는 것. encoder는 decoder가 정확하게 재구성하도록 가능한 많은 정보를 내포시키려 하는데, 이 벡터를 embedding이라 한다.
Auto Encoder: encoding과 decoding작업을 수행하도록 훈련된 신경망. → 원본에 가까워지도록 train시킨다. 보통 Loss function으론 원본과 재구성 img의 pixel간의 RMSE나 BCE를 사용한다. input은 latent embedding vector z로 encoding, 원본픽셀공간으로 decoding
🤔 이미 갖고 있는 img를 왜 재구성?
이미 갖고있는 img를 왜 재구성해야할까? "Auto Encoder 사용이유": Embedding공간(latent space) 때문! latent space에서 sampling하여 새로운 img를 생성할 수 있기 때문.
cf) test_set을 encoder에 통과시켜 만들어진 embedding을 그래프(scatter 등)로 나타내면, img가 latent space에 어떻게 embedding되는지 시각화가능하다.
import torch
import torch.nn as nn
import matplotlib.pyplot as plt
from torchvision import datasets, transforms
from torch.utils.data import DataLoader
# 예시: Autoencoder 모델 정의
class Autoencoder(nn.Module):
def __init__(self, input_size, hidden_size):
super(Autoencoder, self).__init__()
self.encoder = nn.Linear(input_size, hidden_size)
self.decoder = nn.Linear(hidden_size, input_size)
def forward(self, x):
encoded = self.encoder(x)
decoded = self.decoder(encoded)
return encoded, decoded
# 예시: 잠재 공간 시각화 함수
def visualize_latent_space(data_loader, model, device):
model.eval()
latent_space, labels = [], []
with torch.no_grad():
for images, labels_batch in data_loader:
images = images.view(images.size(0), -1).to(device)
encoded, _ = model(images)
latent_space.append(encoded.cpu().numpy())
labels.append(labels_batch.numpy())
latent_space = torch.cat(latent_space, dim=0)
labels = torch.cat(labels, dim=0)
plt.figure(figsize=(10, 8))
plt.scatter(latent_space[:, 0], latent_space[:, 1], c=labels, cmap='viridis')
plt.colorbar()
plt.title('Latent Space Visualization')
plt.show()
# 예시: 데이터 로딩
transform = transforms.Compose([transforms.ToTensor(), transforms.Normalize((0.5,), (0.5,))])
mnist_data = datasets.MNIST(root='./data', train=True, transform=transform, download=True)
data_loader = DataLoader(mnist_data, batch_size=64, shuffle=True)
# 예시: 모델 및 학습 설정
input_size = 28 * 28 # MNIST 이미지 크기
hidden_size = 2 # 잠재 공간 차원
autoencoder_model = Autoencoder(input_size, hidden_size)
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
autoencoder_model.to(device)
# 예시: 학습된 모델의 가중치 로드
autoencoder_model.load_state_dict(torch.load('autoencoder_model.pth'))
autoencoder_model.eval()
# 예시: 잠재 공간 시각화
visualize_latent_space(data_loader, autoencoder_model, device)
이에대해, Decoder를 사용해 다시 pixel공간으로 변환하면 새로운 img를 생성할 수 있다.
import torch
import torch.nn as nn
import matplotlib.pyplot as plt
from torchvision import datasets, transforms
from torch.utils.data import DataLoader
# 예시: Autoencoder 모델 정의
class Autoencoder(nn.Module):
def __init__(self, input_size, hidden_size):
super(Autoencoder, self).__init__()
self.encoder = nn.Linear(input_size, hidden_size)
self.decoder = nn.Linear(hidden_size, input_size)
def forward(self, x):
encoded = self.encoder(x)
decoded = self.decoder(encoded)
return encoded, decoded
# 예시: 이미지 생성 함수
def generate_image_from_latent_space(latent_vector, model, device):
model.eval()
with torch.no_grad():
latent_vector = torch.tensor(latent_vector).float().to(device)
reconstructed_image = model.decoder(latent_vector)
reconstructed_image = reconstructed_image.view(1, 1, 28, 28) # MNIST 이미지 크기
return reconstructed_image
# 예시: 이미지 시각화 함수
def visualize_image(image):
plt.imshow(image.squeeze().cpu().numpy(), cmap='gray')
plt.axis('off')
plt.show()
# 예시: 데이터 로딩
transform = transforms.Compose([transforms.ToTensor(), transforms.Normalize((0.5,), (0.5,))])
mnist_data = datasets.MNIST(root='./data', train=True, transform=transform, download=True)
data_loader = DataLoader(mnist_data, batch_size=64, shuffle=True)
# 예시: 모델 및 학습 설정
input_size = 28 * 28 # MNIST 이미지 크기
hidden_size = 2 # 잠재 공간 차원
autoencoder_model = Autoencoder(input_size, hidden_size)
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
autoencoder_model.to(device)
# 예시: 학습된 모델의 가중치 로드
autoencoder_model.load_state_dict(torch.load('autoencoder_model.pth'))
autoencoder_model.eval()
# 예시: 잠재 공간에서 샘플링하여 이미지 생성 및 시각화
latent_vector_sample = [0.5, 0.5] # 잠재 공간에서 샘플링한 값
generated_image = generate_image_from_latent_space(latent_vector_sample, autoencoder_model, device)
visualize_image(generated_image)
2. Variational Auto Encoder
Auto Encoder의 문제점.
Auto Encoder는 latent space가 연속적인지 강제하지 않기에, 다룰 수 있는 차원 수가 적다. 그렇기에, 더 복잡한 img생성 시, latent space에 더 많은 차원 사용 시, 이 문제는 뚜렷해진다. 즉, encoding 시, latent space를 자유롭게 사용하면 비슷한 point가 모인 그룹간의 큰 간격이 발생한다. 이런 간격이 발생한 공간에서는 잘 형성된 img를 생성할 가능성이 낮다.
이를 위해 Auto Encoder 대신, Variational Auto Encoder로 바꿔야한다.
VAE
AE: 각 img가 latent space의 한 point에 직접 mapping됨. VAE: 각 img가 latent space point주변의 다변량정규분포에 mapping됨. 다변량 정규분포 N(0,I)는 평균벡터가 0, 공분산행렬이 단위벡터이다. 즉, 요약해보면 Encoder는 input을 받아 latent space의 다변량정규분포를 정의하는 2개의 벡터로 encoding한다. ∙ z_mean: 이 분포의 평균벡터 ∙ z_log_var: 차원별 분산의 로그값
이런 작은 변화로 어떻게 Encoder를 향상시킬 수 있었을까? 이전 AE: latent space를 연속적으로 만들필요❌ VAE: z_mean주변영역에서 random point를 sampling하기에 재구성손실이 작게 유지되도록 동일영역의 Point의 img와 매우 비슷하게 decoding.
cf) reparameterization trick이란?
mean과 log_var로 정의된 정규분포에서 직접 sampling하는 대신, 표준정규분포에서 epsilon을 sampling → 올바른 평균과 분산을 갖도록 sample수동조정.
이 방법은 epsilon을 포함함으로써 출력의 편도함수를 결정론적(= random epsilon과 무관함)으로 표시할 수 있어 역전파를 위해 필수적이다.
🧐 Loss Function
AE의 손실함수: Src_img와 AE통과한 출력간의 Reconstruction Loss VAE: Reconstruction Loss + KL-Divergence (Latent Loss)
KL Divergence는 z_mean과 z_log_var가 표준정규분포와 얼마나 다른지를 측정한다.
∙ sample dataset X를 가지고
∙ sample이 알지 못하는 어떤 p_data분포로 생성되었다 가정.
∙ p_data를 흉내내는 생성모델 p_model을 만들자.
이를위해 p_model에서 sampling해 p_data에서 추출한 것 같은 sample을 생성.
∴ p_model의 ideal한 속성:
- 정확도:
생성된 sample의 p_model이 높으면 p_data에서 추출한 것 같아야함.
- 생성:
p_model에서 새로운 sample이 쉽게 sampling가능해야함.
- 표현:
data의 다양한 high-level feature가 p_model로 어떻게 표현되는지 이해가능해야함.
∙ Representation Learning
"고차원 data의 표현(representation)"을 배운다는 것은 무슨 의미일까? 고차원 표본공간을 직접모델링❌ 저차원의 잠재공간을 이용 → trainset의 각 sample을 표현 → 원본 공간의 point에 mapping latent space의 각 point = 어떤 고차원 Image에 대한 표현.
즉, 수학적으로 Encoder-Decoder기법은 data가 놓여있는 고차원 비선형 manifold를 sampling가능한 단순한 Latent space로 변환한다. cf) Manifold는 local로는 저차원의 euclidean distance로 볼 수 있는 고차원공간을 의미.
🔔 핵심 확률이론
∙ 표본공간(sample space): sample x가 갖는 모든 값의 집합.
∙ 확률밀도함수 p(x):point x를 0과 1사이 숫자로 mapping하는 함수 표본공간에 있는 모든 point에 대해 p(x) 적분 시, 1이 되면 잘 정의된 확률분포. 또한, pdata(x)는 하나이지만 pmodel(x)는 무수히 많이 존재.
∙ 모수 모델링(parametric modeling):안정적인 pmodel(x) 찾기위한 기법. 유한개의 parameter θ를 사용해 기술가능한 밀도함수 pθ(x)의 한 종류.
∙parameter집합 θ의 likelihood함수: 𝐿(θ|x) = pθ(x)= ∏x∈X pθ(x) x가 주어졌을 때, θ의 타당성을 측정하는 함수 cf) Log Likelihood: 𝑙(θ|X)=∑x∈Xlog pθ(x) 이때, 모수모델링의 관심은 dataset X가 관측될 가능도를 최대화하는 parameter θ̂의 최적값을 찾는것.
∙MLE(Maximum Likelihood Estimation):θ̂ 추정 기법. θ̂ = argmaxx𝑙(θ|x) 신경망은 보통 손실함수를 최소화 = NLL 최소화하는 parameter집합을 찾는 것 즉, θ̂ = argminθ(-𝑙(θ|x)) = argminθ(-𝑙ogpθ(X))
다만, 일반 고차원문제에서 pθ(X)를 직접 계산하긴 어렵기에 생성모델마다 다른방법으로 이를 해결한다.
생성모델 분류.
prev.
생성모델은 밀도함수 pθ(x)를 모델링하는 방식이 조금씩 다르다. ∙ 방식 ①: 명시적으로 밀도함수 모델링. (다만 밀도함수를 쉽게 다루기 위해=계산가능하도록 모델을 제약) -ex) VAE, Energy-based, Diffusion