Notice
Recent Posts
Recent Comments
«   2024/12   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31
Tags
more
Archives
Today
Total
관리 메뉴

No Limitation

[Deep learning] Generative Adversarial Networks (1) - GAN Intro, Wasserstein GAN 본문

ML & DL & RL

[Deep learning] Generative Adversarial Networks (1) - GAN Intro, Wasserstein GAN

yesungcho 2022. 5. 19. 15:06

본 포스팅은 카이스트 산업 및 시스템 공학과 박찬영 교수님의 지식서비스를 위한 기계학습 강의를 중심으로 정리하였고 부가적으로 데이비드 포스터의 Generative Deep Learning 교재를 참고하였습니다. 

 

그 외로 다음 포스팅들을 참고하였습니다.

Arjovsky, M., Chintala, S., & Bottou, L. (2017, July). Wasserstein generative adversarial networks. In International conference on machine learning (pp. 214-223). PMLR.  { Original WGAN paper }

https://aigong.tistory.com/66

https://github.com/MagmaTart/Paper-Reading/blob/master/summarys/Improved-Techniques-for-Training-GANs.md

https://deepseow.tistory.com/44

https://github.com/soumith/ganhacks

 

지난 포스팅에서는 생성 모델로서 VAE를 살펴보았는데 VAE는 여러 한계점을 가지고 있었습니다. Regularization을 구성하는 term에서 사전 확률이 가우시안인 경우를 제한해서 사용이 가능하다는 한계점을 가지고 있었고 또 다른 한계점으로는 그다지 정확한, 실제로 유사한 이미지들을 generate하는 것은 아니다는 것을 언급합니다. VAE에 대한 내용은 이전에 정리한 포스팅( https://yscho.tistory.com/104 ) 을 참고 바랍니다!

 

아래 그림을 보시죠.

출처 : 카이스트 박찬영 교수님 지식서비스를 위한 기계학습 강의자료

VAE의 decoder 부분만 가져온 그림입니다. 실제 우측 상단에 있는 이미지가 real이라고 한다면 output에 나온 밑의 2 이미지를 봅시다. 왼쪽 7의 경우 아래에 포인트 점 하나가 더 찍혔고 오른쪽 이미지는 7. 같이 보이는 이미지가 생성되었습니다. 사람은 오른쪽보다 왼쪽 그림이 실제로 더 가깝다고 생각을 하게 되지만, MSE나 BCE 같은 loss 관점에서 두 이미지는 VAE가 보기에는 동일한 이미지입니다. 이런 부분이 VAE가 갖는 한계라고 할 수 있습니다. 

 

자 다음 Generative models들을 잘 정리한 분류 표를 보시죠. 개인적으로 많이 사용되는 분류표고 굉장히 깔끔하게 잘 정리된 분류표라고 생각이 듭니다. 

출처 : 카이스트 박찬영 교수님 지식서비스를 위한 기계학습 강의자료

 

해당 분류표에서 앞에서 배운 VAE는 어디있는 지 확인해볼까요? VAE의 경우 앞서 언급드렸듯 사전 확률에 대한 분포를 가정하는, 즉 'explicit density' 카테고리에 속하게 되고, variation inference 과정을 통해 근사시킴으로서 동작하기 때문에 'approximate density' 카테고리에 속하게 됩니다. 반면 우리가 배울 GAN은 어떨까요? GAN의 경우 어떠한 분포를 가정하지 않습니다. 즉, 'implicit density'에 속하게 됩니다. 그렇다면 이 implicit density를 바탕으로 하는, "Implicit Generative Models"는 어떻게 학습을 수행하는 지 살펴봅시다. 

 

출처 : 카이스트 박찬영 교수님 지식서비스를 위한 기계학습 강의자료

자 해당 그림을 살펴보겠습니다. 우선 z가 형성하는 사전 확률을 위 'input distribution'이라고 한다면, 어떠한 네트워크 G로 인해, 우측처럼 output distribution이 도출되었습니다. 저 어떠한 네트워크라는 것이 우리가 뒤에서 배울 Generator를 의미하게 되고 보통 G로 표시합니다. 그리고 output distribution은 G(z)로 표시할 수 있겠죠. 이를 보다 더 직관적인 그림으로 나타내면 아래 그림과 같습니다. 

 

출처 : https://blog.openai.com/generative-models/

즉, generate model을 통해 도출되는 분포와 실제 true data의 분포 간의 loss를 최소화하면서 학습을 수행하는 것이 implicit generative model이 수행하는 기본적인 역할이라는 것을 익힌 채로 본격적으로 GAN을 공부해보겠습니다. 

 

GAN은 크게 2개의 네트워크, Generator와 Discriminator로 구축됩니다. 

출처 : 카이스트 박찬영 교수님 지식서비스를 위한 기계학습 강의자료

Generator의 경우 z의 데이터에서 생성을 통해 real과 유사한 데이터를 만들고자 하고, Discriminator는 fake와 real을 잘 구분하기 위해 학습을 수행합니다. 그래서 이에 대한 objective function은 아래와 같이 표기할 수 있습니다.

출처 : 카이스트 박찬영 교수님 지식서비스를 위한 기계학습 강의자료

minimax game을 수행한다고 보면 되는데요, 위 수식에서 D(x)의 경우 real data에 대한 discriminator의 output이며 D(G(z))의 경우 fake data에 대한 discriminator의 output입니다. discriminator의 경우 D(x)는 진짜이기 때문에 1로, D(G(z))는 가짜기 때문에 0으로 맞추게끔 학습을 할 것입니다. 반면 generator는 D(G(z))를 1로 근사시키는 것을 목적으로 학습을 수행할 것입니다. 조금 수식이 혼동될 수 있습니다. 이는 gradient가 수행되는 방향이 반대기 때문에 보통 학습을 수행할 때 한 네트워크를 학습하는 동안 다른 네트워크는 가중치를 고정시킨 채로 학습을 수행합니다. 아래 그림을 보시죠.

출처 : 카이스트 박찬영 교수님 지식서비스를 위한 기계학습 강의자료

전체 GAN의 학습 process를 나타낸 알고리즘입니다. 우선 처음 discriminator를 학습하게 되고 discriminator는 1번의 epoch 때 k step만큼 학습을 수행합니다. 그리고 discriminator의 학습이 완료되면 generator의 학습이 수행되죠. 일반적으로 discriminator가 더 많이 update를 수행합니다. 각 네트워크의 update 방향은 다르게 되고 discriminator는 gradient ascent, generator는 gradient descent를 수행하게 됩니다. 그래서 우측 그림처럼 saddle-point optimization에 도달하게 됩니다.

 

자 그렇다면 이러한 GAN의 global optimal은 어떠한 값을 가지게 될까요? 바로 generator가 형성하는 distribution이 real data의 distribution과 같은 분포를 가지게 되는 

다음 수식이 만족되는 부분이 바로 global optima가 되게 됩니다.

 

자 그렇다면, G(z)의 파라미터는 고정시켜 놓고 optimal D*(x)를 찾아봅시다. 우선 다음 objective function을 만족해야하기 때문에

이에 대한 optimal discriminator는 다음 연산 과정을 통해 도출할 수 있습니다.

출처 : 카이스트 박찬영 교수님 지식서비스를 위한 기계학습 강의자료

2번째 줄을 보면 expectation을 계산하는 과정에서 z에 대한 적분 식은 3번째 줄처럼 x ̃ 에 대한 수식으로 전개할 수 있습니다 ( x로 적힌 부분은 오타인 것 같습니다. ) 그리고 이를 D에 대해 미분을 수행한 지점이 0이 되면 되기 때문에 global optima는 아래 그림과 같이 나오게 됩니다. 

 

자 그러면 이 global optima를 objective function에 대입해보겠습니다. 아래 수식을 보시죠.

출처 : 카이스트 박찬영 교수님 지식서비스를 위한 기계학습 강의자료

아래 수식의 맨 마지막 부분을 보게 되면 첫 번째 term은 두 확률 분포에 대한 합을 구하는 과정으므로 해당 수식은 (1+1)*(-log2) 해서 -log(4)가 됩니다. 그리고 2번째 수식을 보면, 이는 Jensen-Shannon Divergence로 계산된 p_data와 p_g의 거리 값이 나옴을 확인할 수 있습니다. JS-Divergence는 KL-Divergence와 마찬가지로 distance metric으로 사용할 수 있는 지표인데, KL이 symmetric하지 않아 metric으로 사용이 어려운 점이 있어 이를 보완하여 metric처럼 사용이 가능한 distance를 정의했다고 보시면 될 것 같습니다. 자세한 정보는 해당 블로그 ( https://aigong.tistory.com/66 )을 참고하시면 좋을 것 같습니다. 따라서 다음과 같이 수식이 정리될 수 있겠습니다.

출처 : 카이스트 박찬영 교수님 지식서비스를 위한 기계학습 강의자료

 

자, 그렇다면 이러한 GAN이 모든 걸 잘 만들어준다면 좋았겠지만 안타깝게도 GAN은 여러 문제에 직면하게 됩니다. 대표적으로는 다음 3가지 문제를 들 수 있습니다. 

 

- Vanishing Gradient 

 

- Non-Convergence

 

- Mode Collapse or Mode Dropping

 

위 내용을 하나씩 공부해보겠습니다. 

 

첫 번째 Vanishing Gradient부터 살펴보겠습니다.

여기서는 Discriminator가 너무 성능이 좋아서 generator에게 어떠한 학습의 signal을 줄 수 없는 경우를 의미합니다. 구체적으로 살펴보도록 합시다. 

 

아래 수식과 그림을 살펴보겠습니다.

출처 : 카이스트 박찬영 교수님 지식서비스를 위한 기계학습 강의자료

위 수식은 Generator에 대한 gradient을 구한 것인데요, 수식은 -D(G(z))처럼 나오게 됩니다. 그리고 이 값은 위 수식에 있는 것처럼 만약 discriminator가 confident하다면 gradient는 0으로 가게 됩니다. 이게 무슨 말일까요? 다음 그래프를 봅시다.

출처 : 카이스트 박찬영 교수님 지식서비스를 위한 기계학습 강의자료

x축이 D(G(z))이고 y는 log(1-D(G(z)))입니다. 여기서 Generator는 D(G(z))=1로 만들기 위해 열심히 학습을 할 것입니다. 하지만 그래프를 보면 초기 D(G(z))가 0에 근사하는, 즉 아직 generator가 충분히 학습이 잘 되지 않는 부분에서, 기울기가 거의 flat한 것을 알 수 있습니다. 즉, improve가 잘 되지 않는 것이죠. 그리고 앞서 말씀드렸듯 D(G(z))가 0으로 근사하는 것이 discriminator의 목적이기에 discriminator가 잘 학습하면 이는 generator의 gradient도 0으로 가까이 가는 것이기 때문에, 왼쪽 상단과 같이 거의 gradient가 0이 되기 때문에 학습이 잘 되지 않는, 즉 vanishing gradient 문제가 발생하게 됩니다. 

 

그럼 이 문제를 어떻게 풀 수 있을까요? 간단한 heuristic한 방법이 제안됩니다. 바로 다음과 같이 generate의 objective function을 약간 수정해주는 것이죠.

출처 : 카이스트 박찬영 교수님 지식서비스를 위한 기계학습 강의자료

이렇게 되면 어떤 부분이 변화할까요? 아래 그림을 보시죠.

출처 : 카이스트 박찬영 교수님 지식서비스를 위한 기계학습 강의자료

아래 그림이 원래 cost function이었다면, 위처럼 수정된 cost function을 얻을 수 있습니다. 여기서는, 처음에 D(G(z))가 0에 근사하더라도 큰 gradient을 통해 improve를 세게 하면서 결국 D(G(z))가 1에 근사하게 되는 방향으로 잘 학습이 수행될 수 있습니다. 이러한 간단한 휴리스틱을 통해 문제를 풀 수 있습니다. 

 

두 번째로는 Non-Convergence 문제를 살펴보겠습니다.

이 문제는 generator와 discriminator가 minigame을 할 때 optimal solution에 도달하지 못하는, 즉, equilibrium에 도달하지 못하는 걸로 인해 발생하는 문제입니다. 게임이론을 생각하면 쉬운데요. GAN이 게임이론이랑 논리가 유사합니다. 결국 두 네트워크를 전부 만족시키는 내쉬균형을 찾는 것인데 이 내쉬균형을 찾지 못하는 문제를 바로 Non-convergence 라고 합니다. 그러면 이 문제를 어떻게 해결할 수 있을까요? 이 문제에 대한 다양한 해결책들을 정리해놓은 포스팅 ( https://github.com/soumith/ganhacks )이 있으니 참고 바랍니다. 본 포스팅에서는 이 많은 방법들 중 하나인 'One-sided label smoothing'을 다루고자 합니다. 

 

일단 GAN의 objective function을 보면, generator는 discriminator의 output에 매우 민감할 수 밖에 없습니다. 하지만 이 민감도가 매우 커지게 되면, generator를 올바르게 학습하는 데에 방해가 될 수 있습니다. 즉, gradient signal을 크게 주지 않는 방안을 생각하게 되었고 다음과 같이 Discriminator의 confidence를 weaker하게 하는 parameter α를 추가해주게 됩니다. 

출처 : 카이스트 박찬영 교수님 지식서비스를 위한 기계학습 강의자료

 

예를 들어 discriminator가 D(x)를 1이라는 output을 내놓았다고 하면 α를 0.8로 주었다 하면 이 confidence의 정도를 0.8로 낮추는 것입니다. 

 

자 그렇다면 여기서 궁금한 점은 왜 저 α를 logD(x) term, 즉 왜 real sample들에만 smooth를 적용하였을까요? 왜 generate된 것에는 적용하지 않았을까요?

 

자 만약 α를 real 에 적용하고 β를 fake sample들에 적용을 한다고 생각해봅시다. 그러면 이전에 구했던 optimal D는 다음과 같이 변하게 됩니다.

출처 : 카이스트 박찬영 교수님 지식서비스를 위한 기계학습 강의자료

분자에 원래 β 항이 없어야 하는데 이게 생긴 것이죠. 이렇게 optimal D가 구해지면 어떠한 것이 문제가 될까요? 

예를 들어 P_data는 작은데 P_g는 큰 영역을 생각해보겠습니다. 이해를 돕기 위해 아래 그림을 살펴보겠습니다.

저기서 P_data는 real, P_g는 fake distribution입니다. 저기서 보라색 point가 바로 P_data는 작은데 P_g는 큰 영역입니다. 저기서는 optimal D*(x)는 큰 값이 됩니다. 그렇다면 discriminator는 그 경우에 confidence가 커지게 되고, generator도 저 경우에 해당하는 sample들을 계속해서 생성, 즉 distribution을 P_data에 근사시키려고 하지 않을 것입니다. 따라서 이 문제를 대응해주기 위해 β=0으로 설정해주어, P_data가 작으면 D(x) 값도 작게끔 해주어, 올바르게 학습할 수 있게끔 해주는 것입니다. 다만, 그 penalty를 α만큼 적용해주는 것입니다. 이 방법을 One-sided label smoothing이라고 합니다. 

 

세 번째로는 Mode Collapse or Dropping 문제를 살펴보겠습니다.

즉, 이는 쉽게 말해 generator가 discriminator를 속이는 데 성공한 특정 sample들만 지속해서 생성해내는 것을 의미합니다. 아래 그림을 보면 쉽게 이해가 될 것 같습니다.

출처 : 카이스트 박찬영 교수님 지식서비스를 위한 기계학습 강의자료

파란색이 실제 데이터의 분포인데, 빨간색 분포를 generator가 형성이 되는데, 저 모여있는 일부 샘플들이 discriminator를 속이는데 성공한다면 저 나머지 real data의 distribution은 무시가 됩니다. 이는 저희가 기대하고자 하는 바가 아니기 때문에 이 문제를 mode collapse라고 합니다. 

 

자 그러면 이 문제를 어떻게 해결할 수 있을까요? 이 역시 많은 해결책들이 제시되고 있습니다만 가장 간단한 방법으로는 minibatch discrimination이라는 방법이 있습니다.

 

즉, 개별 샘플이 아닌 Minibatch 단위로 sample들을 뽑은 다음에 Discriminator가 이들을 한꺼번에 고려해서 Collapse 문제를 극복하고자 한 것입니다. 구체적으로는 mini batch 내에 있는 sample들 끼리의 'closeness'를 계산하기 위한 새로운 방법을 정의하여 문제를 풀게 되는데 이에 대한 구체적인 내용들은 다음 포스팅들( https://github.com/MagmaTart/Paper-Reading/blob/master/summarys/Improved-Techniques-for-Training-GANs.md

https://towardsdatascience.com/gan-ways-to-improve-gan-performance-acf37f9f59b )을 참고하시면 더 도움이 될 것 같습니다. 본 포스팅에서는 이 방법과 관련한 자세한 사항은 넘어가겠습니다 ( 저도 구체적으로 더 공부할 시간이 필요할 것 같네요.. )

 

또한 real image들에 label을 주어 학습을 수행하면 보다 diversity가 있는 sample들을 generate할 수 있다고 합니다.

 

하지만 이 mode collapse 문제를 보다 깊게 풀고자 한 논문이 바로 'Wasserstein GAN'이라는 논문입니다. 줄여서 WGAN이라고 하며 WGAN에 대해 자세한 부분들을 공부해보도록 하겠습니다.

 

WGAN에서 지적하는 첫 번째는 바로 'distance metric'에 대한 지적입니다. 이전 GAN은 원래 JS-Divergence을 사용하여 P_data와 P_g를 근사시키고자 하였지만 이 metric이 별로 좋지 못하다는 것입니다. 그래서 WGAN은 그것이 아닌 Earth Mover Distance를 제안합니다. 그렇다면 EMD를 배우기 전에 JS-Divergence가 뭐가 문제가 있다는 건지 한번 살펴보겠습니다. 

출처 : 카이스트 박찬영 교수님 지식서비스를 위한 기계학습 강의자료

자 다음을 가정해보죠. P_0가 real 이고 g(Z)가 generate된 샘플이고 각각은 uniforn distribution으로 샘플링을 한다고 해봅시다. 즉, 저기서는 저 검정 실선을 빨간 실선에 근사시키는 것을 목적으로 한다고 해보죠. 자 저렇게 될 때, JS-divergence는 다음과 같은 결과를 도출합니다. 

출처 : 카이스트 박찬영 교수님 지식서비스를 위한 기계학습 강의자료

즉, 쉽게 말하면 완전히 일치되는 지점에서는 JS는 0이 되지만 그렇지 않을 때는 아무리 g가 p에 근사하더라도 그 JS값은 log2가 됩니다. 즉, 근사 정도를 전혀 반영해주지 않는다는 것이죠. 바로 이 부분이 문제점으로 지적합니다. 반면 Wasserstein distance를 사용하면 다음과 같이 그 근사하는 정도를 반영해줄 수 있게 됩니다. 

 

출처 : 카이스트 박찬영 교수님 지식서비스를 위한 기계학습 강의자료

그래서 이 distance를 사용해서 loss function을 수정하는 방안을 제안하는 것이 바로 WGAN입니다. 

 

그렇다면 그 Wasserstein distance는 구체적으로 어떻게 정의되고 그 의미를 살펴보겠습니다. 여기서부터는 조금 수리적인 내용이 들어가서 저도 100%는 이해하지 못한 부분이 있습니다. critic하면서 봐주시면 감사하겠습니다. 혹시 더 디테일한 수리적인 내용이 필요하신 분은 다음 블로그 ( https://deepseow.tistory.com/44 ) 를 참고하시면 좋을 것 같습니다. 

 

우선 Pr을 real distribution이고 Pg가 fake distribution이면 W-distance는 다음과 같이 나타낼 수 있습니다.

출처 : 카이스트 박찬영 교수님 지식서비스를 위한 기계학습 강의자료

저기서 inf는 infimum으로 하한을 의미하고, γ는 plan으로 생각하시면 될 것 같습니다. 목적 함수는 || x - y ||를 최소로 하는 plan Pr, Pg를 찾는 것을 목적으로 하게 됩니다. 그리고 저게 바로 Earth mover distance입니다. 하지만 저 경우 저 W를 모든 가능한 plan들에 대해 optimize하는 것은 불가능하기 때문에 'Kantorovish-Rubinstein duality'를 사용해서 dual form으로 바꾸어 나타낼 수 있습니다 ( 이 부분이 어려워서 위 블로그를 참고해주세요 ㅠㅠ ). 그러면 아래 수식처럼 전개가 될 수 있습니다. 

출처 : 카이스트 박찬영 교수님 지식서비스를 위한 기계학습 강의자료

여기서 f는 D를 의미하게 됩니다. 다만 여기서 제약, 즉 1-Lipschitz function이 붙게 되는데 이것은 무엇을 의미하는 걸까요? 즉, Discriminator ( 특별히 WGAN에서는 critic(비평자)라고 하지만 논리의 통일을 위해 그냥 Discriminator로 표현하겠습니다 )에 추가적인 제약이 붙는 것인데요 다음과 같은 제약이 붙게 됩니다. 

위 수식이 의미하는 바는, x1-x2는 두 이미지의 픽셀의 평균적인 절대값의 차이를 말하고 분자는 discriminator 예측 간의 절대값의 차이입니다. 즉, 기본적으로 두 이미지 사이에서 discriminator의 예측이 변화할 수 있는 비율을 제한할 필요가 있다는 겁니다. 바로 이러한 제약식이 붙게 됩니다. 

 

이런 1-립시츠 제약이 붙은 상황에서 WGAN의 objective function은 다음과 같이 표기됩니다. 

출처 : 카이스트 박찬영 교수님 지식서비스를 위한 기계학습 강의자료

 

그렇다면 여기서 저 제약을 만족시키려면 어떠한 조치를 취해야 할까요? 바로 'weight clipping'을 적용합니다. 강제로 [-0.01, 0.01]사이의 가중치를 갖게 하는 방법 등을 생각해볼 수 있습니다. 

 

그렇게 적용하게 되면 어느 정도 효과를 볼 수 있을까요? 아래 그림을 보시죠.

출처 : 카이스트 박찬영 교수님 지식서비스를 위한 기계학습 강의자료

파란색 분포의 경우 실제, 초록색의 경우가 fake입니다. 이 때 일반적인 GAN의 discriminator는 빨간색처럼 이미 saturate되버리는 문제가 발생하죠. 하지만, 민트색으로 표현된 WGAN의 경우 안정적인 gradient를 형성함을 알 수 있습니다. 

 

이렇게 구성된 WGAN의 슈도 코드는 아래와 같습니다.

하지만 이 또한 만능은 아닙니다. 왜냐하면 결국 clipping을 어느 정도 적용하냐에 따라, 또 성능의 차이가 심하게 되는 문제가 발생하기 때문입니다. 아래 그림을 보면 이를 더 쉽게 알 수 있습니다. 또, 가중치가 특정 value ( 아래 그림에서는 -0.01, 0.01 ) 에 converge하는 문제도 발생하게 됩니다. 

출처 : 카이스트 박찬영 교수님 지식서비스를 위한 기계학습 강의자료

따라서 이러한 clipping이 아닌 더 좋은 방법이 없을까하고 등장한 개념이 바로 'gradient penalty'입니다. 이는 WGAN-GP로 표현됩니다. 

 

즉, weight에 clipping을 주지 말고, gradient의 norm을 1 이내로 제한하라는 방법을 의미합니다. 구체적으로는 아래 수식처럼 나타낼 수 있겠네요. 

출처 : 카이스트 박찬영 교수님 지식서비스를 위한 기계학습 강의자료

기존의 discriminator loss에 gradient penalty term이 추가된 것을 볼 수 있습니다. 특별히 더 고려할 부분은 저기서 뽑히는 x ̂는 그냥 x가 아니라 다음과 같이 x와 generated된 x ̃ 의 convex combination으로 구성된 x ̂에서 샘플을 뽑은 것을 알 수 있습니다. 그리고 weight clipping을 사용하지 않았기 때문에, momentum을 적용할 수 있다는 장점도 생기게 됩니다. 따라서 WGAN-GP에서는 optimizer로 기존 RMSProp이 아닌 Adam을 사용하게 됩니다. 이걸 적용했을 때의 결과는 더 좋았다고 합니다.

출처 : 카이스트 박찬영 교수님 지식서비스를 위한 기계학습 강의자료

 

WGAN-GP의 슈도 코드는 아래와 같습니다.

크게 달라진 부분은 없지만, clipping이 사라지고 gradient penalty term이 생기고, Adam optimizer로 바뀐 부분이 달라진 부분이네요. 

 

여기까지 GAN의 첫 번째 part, GAN의 intro와 WGAN에 대해 공부하였습니다. 

다음 포스팅에서는 GAN의 더 다양한 architecture에 대해 정리하도록 하겠습니다! 긴 글 읽어주셔서 감사합니다!