tag:blogger.com,1999:blog-52019564504615969142024-03-19T00:19:35.901-07:00Daddy MakersSW, HW, CG, ART, 건설, 건축 메이크 과정을 정리, 공유하는 블로그입니다 - 대디 메이커Daddy Makerhttp://www.blogger.com/profile/02275758703365576780noreply@blogger.comBlogger430125tag:blogger.com,1999:blog-5201956450461596914.post-86270712956728077682024-03-16T22:58:00.000-07:002024-03-17T06:27:56.465-07:00GPU 없는 로컬에서 서비스 가능한 경량 소형 LLM, LLAMA2.c 빌드, 실행, 학습 및 코드 분석하기<div style="text-align: left;">이 글은 GPU 없는 컴퓨터에서 경량 LLAMA2.c (라마씨)를 빌드하고, 로컬에서 실행, 학습하는 방법을 간략히 따라해 본다.</div><div style="text-align: left;"><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgtCr6WIJHECQoR5epVBSJoLWBPZdguJVlCpZHvTHhTxXHxPJq6TNu7uAc5uoLRLLG-Qa072EvooJG6o4Q0x7O8vG4h2HCdBTSGfw8fuiE6uxvYJsRMTez30rOB1WiVGsSoyP-dRuQbRSaAut1eGEJDsXvb-DPe0-4J9BJWeNn8PbHr9qxGo8wy01qnCbsm" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="720" data-original-width="1280" height="180" src="https://blogger.googleusercontent.com/img/a/AVvXsEgtCr6WIJHECQoR5epVBSJoLWBPZdguJVlCpZHvTHhTxXHxPJq6TNu7uAc5uoLRLLG-Qa072EvooJG6o4Q0x7O8vG4h2HCdBTSGfw8fuiE6uxvYJsRMTez30rOB1WiVGsSoyP-dRuQbRSaAut1eGEJDsXvb-DPe0-4J9BJWeNn8PbHr9qxGo8wy01qnCbsm" width="320" /></a></div></div><div class="separator" style="clear: both; text-align: center;">LLAMA2.c</div><br /></div><div style="text-align: left;"><b>머리말</b></div><div style="text-align: left;"><div>앞서 설명한 코드 라마2는 최소 28GB이상의 GPU RAM이 필요하다. 많은 개발자의 경우, 현재 시점에서 이 정도 수준의 GPU 스펙 장비를 가진 경우는 매우 드물다. CoLab의 경우 T4 GPU는 16GB 크기를 가진다. 그러므로, 이를 해결할 수 있는 솔류션이 개발되었다. </div><div><br /></div><div>LLAMA2.c는 OpenAI 엔지니어인 Andrej Karpathy가 개발한 라마2를 이용한 소형 LLM 오픈소스 프로젝트이다. 이 라마 코드는 순수 C로 개발되었으며, 모델은 겨우 100MB로 동작되어, GPU가 없는 로컬 PC에서도 가볍게 실행된다. </div></div><div style="text-align: left;"><br /></div><div style="text-align: left;"><div><b>LLAMA.c 설치</b></div><div>설치, 실행 및 학습 방법은 다음과 같다. 터미널에서 다음 명령을 실행한다.</div><div>git clone <a href="https://github.com/karpathy/llama2.c.git">https://github.com/karpathy/llama2.c.git</a></div><div>cd llama2.c</div><div>wget https://huggingface.co/karpathy/tinyllamas/resolve/main/stories15M.bin</div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjWKdFlUsvfa0fQS7Nd61ftwEBKF9EG6p6XUgJEp7MIzXdEdx6TNxbwdlc7hrfGpV_8aGUT2J_9GnwNSo4E-4f-hi5Nm4WLwlTK0byJQfymd_LOOCHovrNbVPdseyhgo1g7MihFstaVVzgS4eW1vyjqSIizXDXEZocvEucRSdaKww7LoW_Xu5Ywjr8eRk89" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="785" data-original-width="1268" height="198" src="https://blogger.googleusercontent.com/img/a/AVvXsEjWKdFlUsvfa0fQS7Nd61ftwEBKF9EG6p6XUgJEp7MIzXdEdx6TNxbwdlc7hrfGpV_8aGUT2J_9GnwNSo4E-4f-hi5Nm4WLwlTK0byJQfymd_LOOCHovrNbVPdseyhgo1g7MihFstaVVzgS4eW1vyjqSIizXDXEZocvEucRSdaKww7LoW_Xu5Ywjr8eRk89" width="320" /></a></div><div><br /></div><div>이 결과, https://huggingface.co/datasets/roneneldan/TinyStories 으로 학습된 모델이 다운로드된다.</div><div><br /></div><div><b>실행하기</b></div><div>코드를 컴파일하고, 다운로드받은 스토리 학습 모델을 실행한다. </div><div><div>make run</div><div>./run stories15M.bin</div></div><div><br /></div><div>실행 결과는 다음과 같다. 텍스트가 잘 생성되는 것을 확인할 수 있다.</div></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhwJ8ai7i0CiHPPV347lJcBkCLrI9UJ0I6xwHyPrOlKLbnZCyyEtma0Smjg1J9j4BhMIHBAcjFCyIyzKdxDHHs5jFur6QISZ6PlaJH1C9RhIjE_vPNY6CKPoEibzEnksMTepl3GRGrvl0zh3pOTpwC43HMiUyb3wX7zW6g6vuU0_j094wBXjUCzR5ijamRo" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="239" data-original-width="1274" height="120" src="https://blogger.googleusercontent.com/img/a/AVvXsEhwJ8ai7i0CiHPPV347lJcBkCLrI9UJ0I6xwHyPrOlKLbnZCyyEtma0Smjg1J9j4BhMIHBAcjFCyIyzKdxDHHs5jFur6QISZ6PlaJH1C9RhIjE_vPNY6CKPoEibzEnksMTepl3GRGrvl0zh3pOTpwC43HMiUyb3wX7zW6g6vuU0_j094wBXjUCzR5ijamRo=w640-h120" width="640" /></a></div><br />좀 더 큰 모델을 다운받아 실행해 본다.</div><div><div>wget https://huggingface.co/karpathy/tinyllamas/resolve/main/stories42M.bin</div><div>./run stories42M.bin</div></div><div>./run stories42M.bin -t 0.8 -n 256 -i "One day, Lily met a Shoggoth"</div><div><br /></div><div><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both;"><div class="separator" style="clear: both;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhTs-F14rWbeVowDCcxDYTcguWuVYR-74J-5h_B21jL8tHOufZqgsljaKJW9wdEWQXWZEYIGDoRM1UWP3u2EyUXH4L_tnwS8n3AkGu9dohqfAMR9--0b5PRXhIqwyPaXd0HvCXtJSq2o-cYRmocX9zF0ojR-EEL85g569Fda5FsYCTBEVVo1-ogktZ9kLZJ" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="286" data-original-width="1009" height="182" src="https://blogger.googleusercontent.com/img/a/AVvXsEhTs-F14rWbeVowDCcxDYTcguWuVYR-74J-5h_B21jL8tHOufZqgsljaKJW9wdEWQXWZEYIGDoRM1UWP3u2EyUXH4L_tnwS8n3AkGu9dohqfAMR9--0b5PRXhIqwyPaXd0HvCXtJSq2o-cYRmocX9zF0ojR-EEL85g569Fda5FsYCTBEVVo1-ogktZ9kLZJ=w640-h182" width="640" /></a></div></div><div style="text-align: left;"><br />다음과 같이, 접두어, 추가 명령줄 인수를 지정할 수도 있다.</div></div></div><div>-t: 생성 온도. 0~1.0</div><div>-p: <a href="https://peterchng.com/blog/2023/05/02/token-selection-strategies-top-k-top-p-and-temperature/">top-p sampling</a> (<a href="https://docs.cohere.com/docs/controlling-generation-with-top-k-top-p">설명</a>. <a href="https://huggingface.co/blog/how-to-generate">How to generate text</a>)</div><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px;"> </blockquote><div><b>데이터 학습 방법</b></div><div>새로운 데이터 훈련을 위해서는 앞에서 사용한 스토리 모델이 어떻게 학습되었는지 확인해 볼 필요가 있다. 그 방법은 다음과 같다.</div><div><div>python tinystories.py download</div><div>python tinystories.py pretokenize</div></div><div>python train.py</div><div><br /></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEha3iQ8uHXi4tIv7hMK7oObS25ET9nmeGRQdE2mEViufHZnUaQkgrEwbQbFGhsJZQyceEJSTzi2G1SvGOTj4T1ATjor8P77pIXFcexcZ-qx_XtwQdc7VwuoubFnjonJZHfvW4XGWYfN9PYlgpVDYlm7hq9C10rmeQbJDv4JDfF_Kl2okjs-cokFJ_zDLs0b" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="99" data-original-width="1017" height="62" src="https://blogger.googleusercontent.com/img/a/AVvXsEha3iQ8uHXi4tIv7hMK7oObS25ET9nmeGRQdE2mEViufHZnUaQkgrEwbQbFGhsJZQyceEJSTzi2G1SvGOTj4T1ATjor8P77pIXFcexcZ-qx_XtwQdc7VwuoubFnjonJZHfvW4XGWYfN9PYlgpVDYlm7hq9C10rmeQbJDv4JDfF_Kl2okjs-cokFJ_zDLs0b=w640-h62" width="640" /></a></div><div class="separator" style="clear: both; text-align: center;">학습 데이터 다운로드 화면 일부</div><br /></div><div>이제 컴파일을 한다. </div><div>make run</div><div><br /></div><div>다음과 같이 실행하면 된다. <br />./run stories15M.bin</div><div><br /></div><div><b>코드 및 학습 데이터 분석하기</b></div><div>LLAMA2.c의 구조는 매우 간단한데, make파일을 분석해 보면 단 하나의 run.c를 컴파일해 실행파일을 빌드하는 것을 확인할 수 있다.</div><div>gcc -O3 -o run run.c -lm</div><div><br /></div><div>run.c의 주요 부분을 살펴보면 다음과 같다. </div><div><div><span style="color: #274e13;">typedef struct {</span></div><div><span style="color: #274e13;"> // token embedding table</span></div><div><span style="color: #274e13;"> float* token_embedding_table; // (vocab_size, dim)</span></div><div><span style="color: #274e13;"> // weights for rmsnorms</span></div><div><span style="color: #274e13;"> float* rms_att_weight; // (layer, dim) rmsnorm weights</span></div><div><span style="color: #274e13;"> float* rms_ffn_weight; // (layer, dim)</span></div><div><span style="color: #274e13;"> // weights for matmuls. note dim == n_heads * head_size</span></div><div><span style="color: #274e13;"> float* wq; // (layer, dim, n_heads * head_size)</span></div><div><span style="color: #274e13;"> float* wk; // (layer, dim, n_kv_heads * head_size)</span></div><div><span style="color: #274e13;"> float* wv; // (layer, dim, n_kv_heads * head_size)</span></div><div><span style="color: #274e13;"> float* wo; // (layer, n_heads * head_size, dim)</span></div><div><span style="color: #274e13;"> // weights for ffn</span></div><div><span style="color: #274e13;"> float* w1; // (layer, hidden_dim, dim)</span></div><div><span style="color: #274e13;"> float* w2; // (layer, dim, hidden_dim)</span></div><div><span style="color: #274e13;"> float* w3; // (layer, hidden_dim, dim)</span></div><div><span style="color: #274e13;"> // final rmsnorm</span></div><div><span style="color: #274e13;"> float* rms_final_weight; // (dim,)</span></div><div><span style="color: #274e13;"> // (optional) classifier weights for the logits, on the last layer</span></div><div><span style="color: #274e13;"> float* wcls;</span></div><div><span style="color: #274e13;">} TransformerWeights;</span></div></div><div><br /></div><div><div>이 구조체의 이름은 TransformerWeights이며, 이는 Transformer 모델의 가중치를 저장하는 데 사용된다. 트랜스포머의 어텐션 스코어를 계산하는 데 핵심적인 자료구조이다. 각 필드는 다음과 같은 의미를 가진다:</div></div></div><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"></blockquote><ul style="text-align: left;"><li>token_embedding_table: 토큰 임베딩 테이블을 나타낸다. 이는 어휘 크기(vocab_size)와 차원(dim)의 2차원 배열이다.</li><li>rms_att_weight, rms_ffn_weight: RMSNorm의 가중치를 나타낸다. 각각 attention과 feed-forward network에 대한 것이다.</li><li>wq, wk, wv, wo: 각각 query, key, value, output에 대한 가중치를 나타낸다. 이들은 multi-head attention에서 사용된다.</li><li>w1, w2, w3: feed-forward network에서 사용되는 가중치이다.</li><li>rms_final_weight: 최종 RMSNorm의 가중치를 나타낸다.</li><li>wcls: (선택적으로) 마지막 레이어에서 로짓에 대한 분류기 가중치를 나타낸다.</li><li>이 구조체는 Transformer 모델의 가중치를 저장하고 관리하는 데 사용된다.</li></ul><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"></blockquote><div style="text-align: left;"><div>전체적인 프로그램 실행 구조는 다음과 같다. </div><div><br /></div></div><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><div><div>1. 이 코드는 트랜스포머 모델의 구성, 가중치 및 상태에 대한 구조와 메모리 할당, 메모리 해제 및 체크포인트 읽기 기능을 정의한다.</div></div></div><div style="text-align: left;"><div><div>2. 'rmsnorm', 'softmax', 'matmul'과 같은 신경망 블록을 위한 함수도 포함되어 있다.</div></div></div><div style="text-align: left;"><div><div>3. 코드는 Transformer 모델을 통해 입력 토큰을 처리하여 출력 로짓을 생성하는 'forward' 함수를 구현한다.</div></div></div><div style="text-align: left;"><div><div>4. 또한 BPE(Byte Pair Encoding) 토크나이저가 문자열을 토큰으로 또는 그 반대로 변환하는 구조와 함수가 있다.</div></div></div><div style="text-align: left;"><div><div>5. 코드에는 argmax, 샘플링 및 top-p 샘플링과 같은 다양한 전략을 사용하여 확률을 기반으로 토큰을 샘플링하는 '샘플러' 구조와 함수가 포함된다.</div></div></div><div style="text-align: left;"><div><div>6. 시간 측정을 위한 유틸리티 기능과 Transformer 모델을 기반으로 텍스트를 생성하는 '생성' 기능을 정의한다.</div></div></div><div style="text-align: left;"><div><div>7. 마지막으로 트랜스포머 모델을 사용하여 사용자와 어시스턴트 간의 대화를 시뮬레이션하는 '채팅' 기능을 정의한다. </div></div></div></blockquote><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgAwQz88LvIZp5oePaJCLvX_rTfYgUmiaKL6CWD2DewyTszU_ctpkX6QawLb8Vx2HbFLAzI6C04Z9_8rhZzAg70u3gj7xlpkNjxhCwR4y-bAx_l0tBWSxVeTuSsQYcN7b-Z80ndqGQmPogoCch4yKcE5pQnByIBJQdOAxnz99dZFE6WyJIL5VAsLXmE559u" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="851" data-original-width="479" height="538" src="https://blogger.googleusercontent.com/img/a/AVvXsEgAwQz88LvIZp5oePaJCLvX_rTfYgUmiaKL6CWD2DewyTszU_ctpkX6QawLb8Vx2HbFLAzI6C04Z9_8rhZzAg70u3gj7xlpkNjxhCwR4y-bAx_l0tBWSxVeTuSsQYcN7b-Z80ndqGQmPogoCch4yKcE5pQnByIBJQdOAxnz99dZFE6WyJIL5VAsLXmE559u=w303-h538" width="303" /></a></div><div class="separator" style="clear: both; text-align: center;">전체 프로그램 구조도</div><div class="separator" style="clear: both; text-align: center;"><br /></div><div style="text-align: left;"><div>앞에서 사용된 1.5GB가 넘는 스토리 학습 데이터의 구조는 다음과 같이 되어 있다.</div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEh_1729Gf3m6MRmZkCBUsdFErhiwNLZrYPOkUY37Ppum9G6_pgiCMsz4ti7F7xaYTWy0mkvwlKN4lkw_IWt8YU8feew0WhtR5lIrOdwKypMNGmKSeUOn9mAXJxG0OtarD7AKI36KUgO133cJ4fQGoPO6oYqLY0HLg4ozKcc8G1e_Au7vjcN31FvS-hf5RAS" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="609" data-original-width="938" height="260" src="https://blogger.googleusercontent.com/img/a/AVvXsEh_1729Gf3m6MRmZkCBUsdFErhiwNLZrYPOkUY37Ppum9G6_pgiCMsz4ti7F7xaYTWy0mkvwlKN4lkw_IWt8YU8feew0WhtR5lIrOdwKypMNGmKSeUOn9mAXJxG0OtarD7AKI36KUgO133cJ4fQGoPO6oYqLY0HLg4ozKcc8G1e_Au7vjcN31FvS-hf5RAS=w400-h260" width="400" /></a></div><div class="separator" style="clear: both; text-align: center;">학습 데이터 구조 일부</div><br />이를 본인의 목적에 맞게 적절히 데이터를 준비한 후, 다음과 같이 동일한 방식으로 학습하면 가중치 벡터가 저장된 bin파일을 얻을 수 있다. 이를 이용해, 앞서 언급된 방식과 동일하게 사용하면 된다.</div><div><br /></div><div><b>레퍼런스</b></div><div><ul style="text-align: left;"><li>LLAMA2.c, <a href="https://github.com/karpathy/llama2.c">https://github.com/karpathy/llama2.c</a></li><li><a href="https://medium.com/@kinoshitayukari18/how-to-train-llama2-c-with-google-colab-b0a91c36b6a9">How to train llama2.c with Google Colab</a></li><li><a href="https://codetoflow.com/">Code to Flow</a></li></ul></div></div>Daddy Makerhttp://www.blogger.com/profile/02275758703365576780noreply@blogger.com0tag:blogger.com,1999:blog-5201956450461596914.post-58319749536656703562024-03-13T22:15:00.000-07:002024-03-19T00:19:00.304-07:00오픈소스 기반 프로젝트 문서, 버전 및 지식 관리 도구 소개<div style="text-align: left;">이 글은 무료로 사용할 수 있는 오픈소스 기반 프로젝트 문서, 버전, 지식 관리 도구들을 조사하고, 이를 간략히 나눔한다. 일부는 github에 공개되어 있어, 이를 잘 이용하면, 프로젝트 작업 프로세스에 통합 등을 할 수 있다.<br /><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEi6Cg3lN63Zgjh1Z-RH3Li4aTnTNGteqzC4R9HSOi5tFjDcyT7q2e9HWsUweK5jqbEdzCFyL1DSVUzsva0uhAlAp9aHlEKAhL4LJQg3usVGK33Fx4LE4BQoIWPsrP4yymvI2WxJpuO4XNZNm9bCTkuRZ9S7AlCad9VRkjaszkIq3i5oWzg88g7FGCzEdagp" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="949" data-original-width="1853" height="205" src="https://blogger.googleusercontent.com/img/a/AVvXsEi6Cg3lN63Zgjh1Z-RH3Li4aTnTNGteqzC4R9HSOi5tFjDcyT7q2e9HWsUweK5jqbEdzCFyL1DSVUzsva0uhAlAp9aHlEKAhL4LJQg3usVGK33Fx4LE4BQoIWPsrP4yymvI2WxJpuO4XNZNm9bCTkuRZ9S7AlCad9VRkjaszkIq3i5oWzg88g7FGCzEdagp=w400-h205" width="400" /></a></div><div class="separator" style="clear: both; text-align: center;">프로젝트 문서 관리 도구 예시</div><br /></div><div class="separator" style="clear: both; text-align: left;"><span style="font-size: medium;"><b>웹 기반 파일 폴더 관리 도구</b></span></div><div class="separator" style="clear: both; text-align: left;"><a href="https://github.com/psolom/RichFilemanager"><b>Rich Filemanager</b></a></div><div class="separator" style="clear: both; text-align: left;">이 도구는 MIT라이센스에 따라 출시된 파일 관리자이다. 드래그앤드롭을 지원하며, AWS S3, 위지윅 편집기, 웹 기반 UI 지원한다. </div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;"><b><a href="https://github.com/filebrowser/filebrowser">FileBrowser</a></b></div><div class="separator" style="clear: both; text-align: left;">이 도구는 파일 관리 UI를 제공해, 파일 업로드, 삭제, 미리보기, 이름변경 및 편집을 지원하다. 독립 실행형 앱으로 사용할 수 있다.</div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhyQrZDy70XDWoKUKIlm6U9ZExpUP0xT_urwR4l_k6SVB-Q_WXwrXz6ZeQzvkqm0Hn_dPGavrpYkJKklMnBBdwrQyHHeRgj96eQ89tcoZrFA0K--rCxlHRO0UKC_tTnghL4T4mDMPbNlN1MlZWIwD_MuKmZqP-kvBTjil4UCugWVn3w39Lp7JJKX9ykLrF4" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="1660" data-original-width="2582" height="206" src="https://blogger.googleusercontent.com/img/a/AVvXsEhyQrZDy70XDWoKUKIlm6U9ZExpUP0xT_urwR4l_k6SVB-Q_WXwrXz6ZeQzvkqm0Hn_dPGavrpYkJKklMnBBdwrQyHHeRgj96eQ89tcoZrFA0K--rCxlHRO0UKC_tTnghL4T4mDMPbNlN1MlZWIwD_MuKmZqP-kvBTjil4UCugWVn3w39Lp7JJKX9ykLrF4" width="320" /></a></div><br /><b><a href="https://medevel.com/filestash/">FileStash</a></b></div><div class="separator" style="clear: both; text-align: left;">이 도구는 SFTP, FTP, S3, GIT, LDAP, MYSQL등 지원하는 웹 파일 관리자이다. 이 도구는 인증, 공유, 미디어 재생 등을 지원한다. 모바일 친화적이고, 플러그인으로 확장이 가능하다.</div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjQl_id8ghx9tb0bZud0fHIT4IJp1xRGeXkiwTj4r282nU4uU6fG1UQoU2wVpq2wfMmbjGSZL3JZhj9In0Y_Iq1i4-gEKLhdbLcKtbdFlwUQlkkVDl4dR-ZVBGBWzwnos_O_037B0ZiDZYN47ZCLAmb4Wz_6SzGHhqiN-7HdvP8m_uz9m0LcjkJ2-h44JfB" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="789" data-original-width="1420" height="178" src="https://blogger.googleusercontent.com/img/a/AVvXsEjQl_id8ghx9tb0bZud0fHIT4IJp1xRGeXkiwTj4r282nU4uU6fG1UQoU2wVpq2wfMmbjGSZL3JZhj9In0Y_Iq1i4-gEKLhdbLcKtbdFlwUQlkkVDl4dR-ZVBGBWzwnos_O_037B0ZiDZYN47ZCLAmb4Wz_6SzGHhqiN-7HdvP8m_uz9m0LcjkJ2-h44JfB" width="320" /></a></div><div class="separator" style="clear: both; text-align: center;"><a href="https://demo.filestash.app/">데모</a></div><div class="separator" style="clear: both; text-align: left;"><br /></div><a href="https://github.com/kalcaddle/KodExplorer"><b>KodExplorer</b></a></div><div class="separator" style="clear: both; text-align: left;">웹용 파일 관리자이다. 직접 웹사이트 개발이 가능하다. 리눅스, 윈도우, 맥을 지원하고, 로컬 혹은 온라인 실행 가능하다. PHP5로 개발되었다.</div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEj3lqsGR3NJLhV9eD9-dkw2rgRDo3Dho60GfW2BWBkxe42VyEHGA6eQWX6l7AVdc9ge9nckkLXhqxJ6rNjhhmIEjEL-GgQ76unX_zL2EPdaq1w0mJdC2yazeYRq6NR0cCItwoTaoyFNziCqPNAadALKThYX05zuuLR8rrlPSH-gzxE0JOKgdFRJe0IBw4a3" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="965" data-original-width="1314" height="235" src="https://blogger.googleusercontent.com/img/a/AVvXsEj3lqsGR3NJLhV9eD9-dkw2rgRDo3Dho60GfW2BWBkxe42VyEHGA6eQWX6l7AVdc9ge9nckkLXhqxJ6rNjhhmIEjEL-GgQ76unX_zL2EPdaq1w0mJdC2yazeYRq6NR0cCItwoTaoyFNziCqPNAadALKThYX05zuuLR8rrlPSH-gzxE0JOKgdFRJe0IBw4a3" width="320" /></a></div></div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;"><a href="https://github.com/prasathmani/tinyfilemanager"><b>TinyFileManager</b></a></div><div class="separator" style="clear: both; text-align: left;">서버 모든 폴더를 관리할 수 있는 웹 기반 PHP 파일 관리자이다. 웹 브라우저 기반 파일, 폴더 저장, 업로드 및 관리할 수 있다. PHP 5.5+ 로 개발되었다. </div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjgomdnqpqSHXh9_1MlNKbJEoP3ppKNDDwKBUGfoqzbgfKuyBYwscqPyDcSxguB3eLFum-4_AnjHt3WTlWNy74iE1A09hdImWl1pl7M2JLgQIKXRzdDG-yO0EQFlH4tjIoWbouwvChHkePIcLHgF-P1wOkHloWoNKnlWBbvZblczAZ0sNqbZFCoU_Hy3B0V" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="657" data-original-width="1200" height="175" src="https://blogger.googleusercontent.com/img/a/AVvXsEjgomdnqpqSHXh9_1MlNKbJEoP3ppKNDDwKBUGfoqzbgfKuyBYwscqPyDcSxguB3eLFum-4_AnjHt3WTlWNy74iE1A09hdImWl1pl7M2JLgQIKXRzdDG-yO0EQFlH4tjIoWbouwvChHkePIcLHgF-P1wOkHloWoNKnlWBbvZblczAZ0sNqbZFCoU_Hy3B0V" width="320" /></a></div><div class="separator" style="clear: both; text-align: left;"><br /><a href="https://github.com/TimboKZ/Chonky"><b>Chonky</b></a></div><div class="separator" style="clear: both; text-align: left;">React 기반 웹 브라우저 파일 관리 도구이다. 드래그앤드롭, 미리보기, 그리드 파일 보기 등을 지원한다. </div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEiAkofLnW_Y6SBFJfrgdwv4x8T7xgrP1nXCqyXjF2mmApcunGTi68NohU8II1dnVYDqjqQsj56D1jrubD1pCgAD8RF-JwkD2drkPF4i8lJ_sAsdKPw07q-K-6WnbYQp93fCZSA58iUFdVG96_ae6GFUKfh3t9YeL--nl2EZp-6h8yBT3K00yQe9C23n5yVh" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="448" data-original-width="974" height="147" src="https://blogger.googleusercontent.com/img/a/AVvXsEiAkofLnW_Y6SBFJfrgdwv4x8T7xgrP1nXCqyXjF2mmApcunGTi68NohU8II1dnVYDqjqQsj56D1jrubD1pCgAD8RF-JwkD2drkPF4i8lJ_sAsdKPw07q-K-6WnbYQp93fCZSA58iUFdVG96_ae6GFUKfh3t9YeL--nl2EZp-6h8yBT3K00yQe9C23n5yVh" width="320" /></a></div><b><a href="https://github.com/filegator/filegator">FileGator</a></b></div><div class="separator" style="clear: both; text-align: left;">이 도구는 파일, 폴더를 관리하는 호스팅 웹 도구이다. 이 도구는 관리자, 사용자에게 개별 접근 권한, 역할을 부여할 수 있다. 대용량 파일 업로드를 지원한다.</div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEixrQuz-YGupHCBZlqmxMfhF5IArK21MUZf7j9_wHp6CMAiHl03hdsYwJXiTgyYL_OhgIiX9-XoRdO-LAEJcuua1z0IxRe7pESvMKtPnKSDy2pUTQo20YfERXNuFC5pOQ1cpHPEtN6c2hCUc1sA_QYAgEWRjZ8Xf6nbfxdm-Xuca8SfGNeUSuGUmrFvlWgS" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="534" data-original-width="752" height="227" src="https://blogger.googleusercontent.com/img/a/AVvXsEixrQuz-YGupHCBZlqmxMfhF5IArK21MUZf7j9_wHp6CMAiHl03hdsYwJXiTgyYL_OhgIiX9-XoRdO-LAEJcuua1z0IxRe7pESvMKtPnKSDy2pUTQo20YfERXNuFC5pOQ1cpHPEtN6c2hCUc1sA_QYAgEWRjZ8Xf6nbfxdm-Xuca8SfGNeUSuGUmrFvlWgS" width="320" /></a></div><b><a href="https://github.com/reallyrehan/flask-fileexplorer">WiFile</a></b></div><div class="separator" style="clear: both; text-align: left;">이 도구는 Flask 서버를 사용해 WiFi, Internet 기반 파일 탐색 도구이다. 컴퓨터 전체 폴더를 탐색하고, 모든 파일을 다운로드 할 수 있다. 브라우저에서 비디오, 오디오 파일을 스트리밍할 수 있다.</div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;"><b><span style="font-size: medium;">문서 관리 시스템</span></b></div><div class="separator" style="clear: both; text-align: left;"><a href="https://www.mayan-edms.com/"><b>Mayan EDMS</b></a></div><div class="separator" style="clear: both; text-align: left;">파이썬 기반 Django를 사용해 개발된 문서 관리 시스템이다. 이 도구는 인텔, 대학 등 많은 기업에서 사용된다. 문서 워크플로 정의 기능을 지원한다.</div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgGbUKzknMZ6nbchA2jF8Huz5Y8suBu4GXAueqMAMBO5PjmqdpHqpeNmALOzLbd-aHOyB2jahhIEpBNC0-aEFNtK8wULZh4iBoniYTe2O8fKIF-5YTXQOecWrMb7iPFkF1pmyygQKo684aXclfpcIMoPitDyfL_ZGTwfFLcXsFewTqOMUZSx40PsPD0W7bF" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="350" data-original-width="650" height="172" src="https://blogger.googleusercontent.com/img/a/AVvXsEgGbUKzknMZ6nbchA2jF8Huz5Y8suBu4GXAueqMAMBO5PjmqdpHqpeNmALOzLbd-aHOyB2jahhIEpBNC0-aEFNtK8wULZh4iBoniYTe2O8fKIF-5YTXQOecWrMb7iPFkF1pmyygQKo684aXclfpcIMoPitDyfL_ZGTwfFLcXsFewTqOMUZSx40PsPD0W7bF" width="320" /></a></div><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgUEnM-8fMMfUBPUIPrPftKbRXppY8RiLco38vBZYMCnEyBBQHDFasxgRzoOo130mrIxNsWOi2OvqcF6i_kJNEKNAj_5uzR2YP4D3ZwOlsJolU0zfv5suGEcX5_9N3MwbXKZnH4-5WLAi4ulTZWTLc6z0632MEP1a4276BRKlaw4iATwLrLNJ6JPZg1tjcA" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="547" data-original-width="1068" height="164" src="https://blogger.googleusercontent.com/img/a/AVvXsEgUEnM-8fMMfUBPUIPrPftKbRXppY8RiLco38vBZYMCnEyBBQHDFasxgRzoOo130mrIxNsWOi2OvqcF6i_kJNEKNAj_5uzR2YP4D3ZwOlsJolU0zfv5suGEcX5_9N3MwbXKZnH4-5WLAi4ulTZWTLc6z0632MEP1a4276BRKlaw4iATwLrLNJ6JPZg1tjcA" width="320" /></a></div></div><br /><b><a href="https://www.seafile.com/en/home/">SeaLife</a></b></div><div class="separator" style="clear: both; text-align: left;">이 도구는 클라우드 파일 공유 도구로, 멀티 플랫폼을 지원하며, 모바일도 지원한다. 백업, 복구 옵션이 있으며, 클라이언트 간 파일 동기화, 파일 잠금, 로깅, 암호화 등을 지원한다. 라즈베리파이에 설치 가능하다. <br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhtWT8buGDYvxi9oOvMGgE8DFomiJvoUEXbxN0M41fyjtOjGmQXzf7EoiWtBf4_KacSp-_WsZ0Y2FTuWvU76v-mnCfl0a83W-ukCLIp3YGxsRw-cOXDsG5L-aIfnc78CjW_UyjHUb4QEWl99cH-_BPGrnlhJiQ0paUV15BOjCnEb9U2p0WvZajnYKMD7sme" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="892" data-original-width="480" height="240" src="https://blogger.googleusercontent.com/img/a/AVvXsEhtWT8buGDYvxi9oOvMGgE8DFomiJvoUEXbxN0M41fyjtOjGmQXzf7EoiWtBf4_KacSp-_WsZ0Y2FTuWvU76v-mnCfl0a83W-ukCLIp3YGxsRw-cOXDsG5L-aIfnc78CjW_UyjHUb4QEWl99cH-_BPGrnlhJiQ0paUV15BOjCnEb9U2p0WvZajnYKMD7sme" width="129" /></a></div><br /><b><a href="https://nextcloud.com/">NextCloud</a></b></div><div class="separator" style="clear: both; text-align: left;">이 도구는 모듈식으로 개발된 클라우드 파일 공유 도구로 GDPR를 준수하며, DICOM(Medical Imaging) 뷰어 앱을 제공한다. 이런 이유로, 의료 분야에 많이 사용된다. 이 도구로 부터 <a href="https://owncloud.org/">OwnCloud</a>가 파생 개발되었다. </div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEg5wMjIcKvuxoBzv03c7bsbFRuh5aWxF0uxhfMNe4-NWAvwrVtRGZXnzlIKNe_VLRs2rr0X0vd58ZMI2-m5Xwa0bQD7MzaSwQGBb9lQOILLZOeRCymEwlmASQR3iPGCzlkL4G49IqupZB2viQrs5Fj0NteBF03tj_N_DPFvgbuTp8vActSJ8pBFSgJM0rTL" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="576" data-original-width="1003" height="184" src="https://blogger.googleusercontent.com/img/a/AVvXsEg5wMjIcKvuxoBzv03c7bsbFRuh5aWxF0uxhfMNe4-NWAvwrVtRGZXnzlIKNe_VLRs2rr0X0vd58ZMI2-m5Xwa0bQD7MzaSwQGBb9lQOILLZOeRCymEwlmASQR3iPGCzlkL4G49IqupZB2viQrs5Fj0NteBF03tj_N_DPFvgbuTp8vActSJ8pBFSgJM0rTL" width="320" /></a></div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;"><b><a href="https://www.seeddms.org/index.php?id=2">Seed DMS</a></b></div><div class="separator" style="clear: both; text-align: left;">실시간 협업, 버전 관리, 사용자 접근 관리, 문서 미리보기 및 승인, 워크플로 지원, 검색 등을 지원한다. </div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhhJxUJoylMEK-o5Oh8RvALFR2oopdwkjsG8ByS5nQnhUxkpjudQvZ26szemtRYwL3NB8vOQ7qXPhXG6RzGZcWxR6gGRsdPmJ8XGyIkhSuEqJFzeRkOu0lOTnZxaHLfBPKwbWLVUtPMGgg31Xhqiy9fEszo5DIZbQG8xASdbbdV9vOofVhdC2FrjHVDcHgi" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="589" data-original-width="1024" height="184" src="https://blogger.googleusercontent.com/img/a/AVvXsEhhJxUJoylMEK-o5Oh8RvALFR2oopdwkjsG8ByS5nQnhUxkpjudQvZ26szemtRYwL3NB8vOQ7qXPhXG6RzGZcWxR6gGRsdPmJ8XGyIkhSuEqJFzeRkOu0lOTnZxaHLfBPKwbWLVUtPMGgg31Xhqiy9fEszo5DIZbQG8xASdbbdV9vOofVhdC2FrjHVDcHgi" width="320" /></a></div><div class="separator" style="clear: both; text-align: left;"><br /></div></div><div class="separator" style="clear: both; text-align: left;"><a href="https://afterlogic.org/aurora-files"><b>Aurora Files</b></a></div><div class="separator" style="clear: both; text-align: left;">자체 호스팅 지원하는 문서 관리 도구로, PHP로 개발되었고, 설치가 쉽다. </div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;"><a href="https://www.alfresco.com/"><b>Alfresco</b></a></div><div class="separator" style="clear: both; text-align: left;">Java로 개발된 도구이며, 기업용 오픈 소스 문서 관리 시스템이다. </div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgSfqFG_ewRBQhuN5LxMXssuUNU4BNIm7luVmxwHI8eYpTzd8gz0cqy47hzRsYsSkYtgbYZuLjVXQuc8CMysPHHXDF_diGrbW4NizTX_Sa_kCpKxmRL4L4uduA-NF9XHPVav3cfhTTulZx6i-b6M9geHlFNgBXKXxv8K08iUGzamZVCFhsI2cODCSwtGF5K" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="472" data-original-width="717" height="211" src="https://blogger.googleusercontent.com/img/a/AVvXsEgSfqFG_ewRBQhuN5LxMXssuUNU4BNIm7luVmxwHI8eYpTzd8gz0cqy47hzRsYsSkYtgbYZuLjVXQuc8CMysPHHXDF_diGrbW4NizTX_Sa_kCpKxmRL4L4uduA-NF9XHPVav3cfhTTulZx6i-b6M9geHlFNgBXKXxv8K08iUGzamZVCFhsI2cODCSwtGF5K" width="320" /></a></div><br /></div><div class="separator" style="clear: both; text-align: left;"><a href="https://www.opendocman.com/"><b>OpenDocMan</b></a></div><div class="separator" style="clear: both; text-align: left;">자체 호스팅을 지원하는 문서 관리 도구로, PHP로 개발되었다. MySQL을 사용하며, 파일 검토, 알림, 만료, 로깅, 리비전 기록 등을 지원한다. 메타 데이터 검색을 지원한다. </div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;"><b><span style="font-size: medium;">버전 및 형상관리 </span></b></div><div class="separator" style="clear: both; text-align: left;"><b><a href="https://git-scm.com/">Git</a></b></div><div class="separator" style="clear: both; text-align: left;">이 도구는 소스 분산 버전 관리 도구이다. 로컬 분기, 스테이징, 워크플로 등을 지원한다. </div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;"><a href="http://savannah.nongnu.org/projects/cvs"><b>CVS</b></a></div><div class="separator" style="clear: both; text-align: left;">소스 형상 관리 도구로 널리 사용되고 있다. 파일 및 문서 기록을 남기 있고, 여러 개발자가 동시 개발이 가능하다. </div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;"><b><a href="https://www.mercurial-scm.org/">Mercurial</a></b></div><div class="separator" style="clear: both; text-align: left;">멀티 플랫폼 지원하는 형상 관리 도구이다. 가볍고, 파이썬 애드온 등을 지원한다.</div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;"><a href="http://bazaar.canonical.com/en/"><b>Bazaar</b></a></div><div class="separator" style="clear: both; text-align: left;">크로스 플랫폼을 지원하며, 부분 체크인, 아웃을 제공한다. 중앙 서버 유무에 관계없이 사용할수 있다.</div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;"><b><span style="font-size: medium;">지식 관리 도구</span></b></div><div class="separator" style="clear: both; text-align: left;"><a href="https://www.goodfirms.co/software/openkm"><b>OpenKM</b></a></div><div class="separator" style="clear: both; text-align: left;">OpenKM은 스캔 이미지를 포함한 전자 문서를 저장, 추적, 관리 지원하는 지식관리 시스템이다. 대량 업로드, 오피스 추가 기능 등을 제공한다.</div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjaQxi89jcHAleqQxNKdlJo7dC5XzVr_leILtOGpoardGv_le7Oj-GKpjgbpIBPKIs-OehpBCkLKUd1Ece933YlDFitn3Z5qECgTnRU2lW1ziI2IjwDv4t73tZ9piWG5bb_BQNJYE-rasbrOw_FRxxXHQizshp2DwSVmJXslUOxebBAZP9bt9U6bFqy2ttO" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="721" data-original-width="1286" height="179" src="https://blogger.googleusercontent.com/img/a/AVvXsEjaQxi89jcHAleqQxNKdlJo7dC5XzVr_leILtOGpoardGv_le7Oj-GKpjgbpIBPKIs-OehpBCkLKUd1Ece933YlDFitn3Z5qECgTnRU2lW1ziI2IjwDv4t73tZ9piWG5bb_BQNJYE-rasbrOw_FRxxXHQizshp2DwSVmJXslUOxebBAZP9bt9U6bFqy2ttO" width="320" /></a></div><br /><b><a href="https://www.documize.com/">documize</a></b></div><div class="separator" style="clear: both; text-align: left;">이 도구는 내외부 문서를 중앙 집중화하여, 정보를 검색하고, 문서에 레이블을 지정해 분류하는 데 도움을 준다.</div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgz8DHMPVXSqGSkLx9lpaXAq59tsueFrVNg1ouj45-TXkBb-0rCuhrQO4dc3AbWnhF-1_vGkiDbbQc_uYrUWXD5XmAgDSisS_C8yb4bWljPmdxVvAyADEP5kl15FTUS8wEOXSTx3BmQ_nAUkFlU8fbwlrsYpHGG4KeQDjJLCiIMfwuNOtvQUYcH0AcDvgJR" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="517" data-original-width="700" height="236" src="https://blogger.googleusercontent.com/img/a/AVvXsEgz8DHMPVXSqGSkLx9lpaXAq59tsueFrVNg1ouj45-TXkBb-0rCuhrQO4dc3AbWnhF-1_vGkiDbbQc_uYrUWXD5XmAgDSisS_C8yb4bWljPmdxVvAyADEP5kl15FTUS8wEOXSTx3BmQ_nAUkFlU8fbwlrsYpHGG4KeQDjJLCiIMfwuNOtvQUYcH0AcDvgJR" width="320" /></a></div><br /><b><a href="https://www.exoplatform.com/">eXo</a></b></div><div class="separator" style="clear: both; text-align: left;">이 도구는 팀, 지식을 연결하는 기능을 제공한다. 프로젝트 협업 시 유용한 기능을 제공한다.</div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgzchVGI1Ga0TNY_p-AM0oJhNJ7e8dv4ZSe6-3omXW-6IYC6LcLUlO30-Yn-CibZ2sZaClZhyRUOFJnvqcIZRb2gZXkjB1kDD71po0iX8S2eAS5Ev4mrKpzwJvj_O0tuZHC2iaUP0gDYSi7hT7UmiBm5jNCYn4ZAgeTAev4m_A6YU0WVn3R7sd46sgvB6q1" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="477" data-original-width="690" height="221" src="https://blogger.googleusercontent.com/img/a/AVvXsEgzchVGI1Ga0TNY_p-AM0oJhNJ7e8dv4ZSe6-3omXW-6IYC6LcLUlO30-Yn-CibZ2sZaClZhyRUOFJnvqcIZRb2gZXkjB1kDD71po0iX8S2eAS5Ev4mrKpzwJvj_O0tuZHC2iaUP0gDYSi7hT7UmiBm5jNCYn4ZAgeTAev4m_A6YU0WVn3R7sd46sgvB6q1" width="320" /></a></div><br /><a href="https://www.zendesk.com/blog/customer-messaging-introducing-zendesk-message/"><b>Zendesk</b></a></div><div class="separator" style="clear: both; text-align: left;">이 도구는 고객의 질문, 답변을 관리해 지식 창고를 구축할 수 있는 기능을 제공한다. </div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjXR_uv4OJBXjrVjPgy2DQDstx2EQBt150kEuU7ld9KYDofG_fnpQqwUg_E8jC4oCQTNKfvHRIc-Rd4c3ORXBLVHmYgC-t5PAAHtfvgCEW08q3_Qy5ByhDDYnmspFYIRCUZQY6_Wwk-SHSU31eNa4U7DmJtLer9_lVjMzd-zqvO6CXXU3tX_f9iBt9lhRi4" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="437" data-original-width="700" height="200" src="https://blogger.googleusercontent.com/img/a/AVvXsEjXR_uv4OJBXjrVjPgy2DQDstx2EQBt150kEuU7ld9KYDofG_fnpQqwUg_E8jC4oCQTNKfvHRIc-Rd4c3ORXBLVHmYgC-t5PAAHtfvgCEW08q3_Qy5ByhDDYnmspFYIRCUZQY6_Wwk-SHSU31eNa4U7DmJtLer9_lVjMzd-zqvO6CXXU3tX_f9iBt9lhRi4" width="320" /></a></div><div class="separator" style="clear: both; text-align: left;"><br /></div>다음은 각 도구의 장단점을 보여준다.</div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEiNLDFbW62TetrYH_dcT2j-e4Anh1M2p04zWb2QLuVBr3iiceTScvvoRFJ-tuf-ysoM2fQfrcxsqGpZtYH1-hU1ktqYaG9QqKfyP6Mj2ikJXY4dTMSvZu-Qn84fB44CxepN-NE2v9cIjlIo0YvVhqzuZD0hbX1Tuhn4qjKJkgDFg-O-528fCIU0shu0hYiu" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="601" data-original-width="861" height="279" src="https://blogger.googleusercontent.com/img/a/AVvXsEiNLDFbW62TetrYH_dcT2j-e4Anh1M2p04zWb2QLuVBr3iiceTScvvoRFJ-tuf-ysoM2fQfrcxsqGpZtYH1-hU1ktqYaG9QqKfyP6Mj2ikJXY4dTMSvZu-Qn84fB44CxepN-NE2v9cIjlIo0YvVhqzuZD0hbX1Tuhn4qjKJkgDFg-O-528fCIU0shu0hYiu=w400-h279" width="400" /></a></div><br /><b><span style="font-size: medium;">쉐어포인트 대용 오픈소스 도구</span></b></div><div class="separator" style="clear: both; text-align: left;"><a href="https://clickup.com/download"><b>ClickUp</b></a></div><div class="separator" style="clear: both; text-align: left;">이 도구는 사용자화 가능한 프로젝트 협업 메뉴를 제공한다. 쉐어포인트와 유사하게 동작하며, 문서 기반 협업이 용이하다. 워크플로, 아이디어 교환 등 기능을 제공한다.</div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjjNVAHCR0WDF5bba0GYtzBSDfCLhJDtQq8o9XQTzOobyGLdb1StEJSn2EOOJZF9NbFwmABt4OEdCGPg3KGqHe_rqqIqOp6IEjCBVEr8gRffGyhmMQzqYhYsEFVGGL4yYbL4cVFsOYz-qMkZ6V00FjBsJer-_bloQf8ZPNUhq5ZCBZk_SwFHpHCIHqo_M4_" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="650" data-original-width="964" height="216" src="https://blogger.googleusercontent.com/img/a/AVvXsEjjNVAHCR0WDF5bba0GYtzBSDfCLhJDtQq8o9XQTzOobyGLdb1StEJSn2EOOJZF9NbFwmABt4OEdCGPg3KGqHe_rqqIqOp6IEjCBVEr8gRffGyhmMQzqYhYsEFVGGL4yYbL4cVFsOYz-qMkZ6V00FjBsJer-_bloQf8ZPNUhq5ZCBZk_SwFHpHCIHqo_M4_" width="320" /></a></div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;"><a href="https://www.atlassian.com/software/confluence"><b>Confluence</b></a></div><div class="separator" style="clear: both; text-align: left;">이 도구는 Atlassian에서 개발한 협업 도구로, 중앙화된 작업 공간, 팀 작업 지원, 지식관리, 프로젝트 협업 등을 지원한다. </div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgVUy38zd0A6e7EoJnf_Oe35-K8RQv7bCZLFVazDNItnwU-vilM2FlHC_CkraKd81q3fZNZdtxAjHn3PApWoWdOUwDiV-TgMHqPRrv4MR5rZUxCHPL-M1QtXmb5B89SLU43N6yP6_i_VloQTZx_1BB1UGN9glWJNDnOk-So6_H-Cn9JiljPT23zoiKY053p" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="910" data-original-width="1600" height="182" src="https://blogger.googleusercontent.com/img/a/AVvXsEgVUy38zd0A6e7EoJnf_Oe35-K8RQv7bCZLFVazDNItnwU-vilM2FlHC_CkraKd81q3fZNZdtxAjHn3PApWoWdOUwDiV-TgMHqPRrv4MR5rZUxCHPL-M1QtXmb5B89SLU43N6yP6_i_VloQTZx_1BB1UGN9glWJNDnOk-So6_H-Cn9JiljPT23zoiKY053p" width="320" /></a></div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;"><a href="https://slack.com/"><b>Slack</b></a></div><div class="separator" style="clear: both; text-align: left;">이 도구는 메시징 기반 프로젝트 협업 도구로, 커뮤니케이션, 보이스 및 비디오 콜, 파일 공유, 작업 추적 등을 지원한다.</div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEipy_e_5w_9c-s4KOBxYzo_upP5rWCosVZ7RTCTRNR7Xd92yMSl42ZDgsvCKz4k5RcB_nIkctQN5yo49d0oxGWhIDD3bSqSnrE-VwsVVUvZ2Nvt7uN3suhsxnr4ZCsOauSWzjQC3uifJR45qoPJ24m14hCuSE7gQaroml1cFppuU4Qi_ZzjLr4FU__hKmGj" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="593" data-original-width="991" height="191" src="https://blogger.googleusercontent.com/img/a/AVvXsEipy_e_5w_9c-s4KOBxYzo_upP5rWCosVZ7RTCTRNR7Xd92yMSl42ZDgsvCKz4k5RcB_nIkctQN5yo49d0oxGWhIDD3bSqSnrE-VwsVVUvZ2Nvt7uN3suhsxnr4ZCsOauSWzjQC3uifJR45qoPJ24m14hCuSE7gQaroml1cFppuU4Qi_ZzjLr4FU__hKmGj" width="320" /></a></div><br /></div><a href="https://www.jostle.me/"><b>Jostle</b></a></div><div class="separator" style="clear: both; text-align: left;">이 도구는 인트라넷 서비스를 제공하여, 단순한 UI, 일정 공유, 파일 공유, 팀 협업, 조직도, 비디오 콜 등을 제공한다. </div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEiMrHamFkvUPkqiBtngh40yBPufyixS1xcRHNJIkHktrhp4wgx7VwuKaYk4KDGbuz-lCP9GmkCI6-mQnUnUzmgDZt-aEwlpHiuI_AbXJn8vlSC4o7VOMgUA3i8nNhj1Bpy5xdvxcwwX-E5jSEIzOGE5FRmVVG-Vy8QmFy6_6grHpr-T-YkgWcLt-zQ7FBhY" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="1025" data-original-width="1800" height="182" src="https://blogger.googleusercontent.com/img/a/AVvXsEiMrHamFkvUPkqiBtngh40yBPufyixS1xcRHNJIkHktrhp4wgx7VwuKaYk4KDGbuz-lCP9GmkCI6-mQnUnUzmgDZt-aEwlpHiuI_AbXJn8vlSC4o7VOMgUA3i8nNhj1Bpy5xdvxcwwX-E5jSEIzOGE5FRmVVG-Vy8QmFy6_6grHpr-T-YkgWcLt-zQ7FBhY" width="320" /></a></div><br /><b><a href="https://www.clearpointstrategy.com/">ClearPoint</a></b></div><div class="separator" style="clear: both; text-align: left;">전략 계획, 프로젝트 관리, 협업을 지원하는 도구로, KPI 추적, 리포트 생성, BSC 관리 등을 제공한다. </div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhsSQ-naGiZYF9aP0OeCyyY1UI-BjofYL4nfH9zk5aFDHki2PmwleEyj_N285-WZPyJ5uQ2eqD0Ljh8OtludjH9um3h2HWx9xgduqzdhyvEbgnEidBUUm7867jFG3i9m4SdheTGQUBzpLyujFTyC0czwDYs7J5Yh-ZGJPRmH686QonqD4ANzoTZAqDmaes7" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="682" data-original-width="996" height="219" src="https://blogger.googleusercontent.com/img/a/AVvXsEhsSQ-naGiZYF9aP0OeCyyY1UI-BjofYL4nfH9zk5aFDHki2PmwleEyj_N285-WZPyJ5uQ2eqD0Ljh8OtludjH9um3h2HWx9xgduqzdhyvEbgnEidBUUm7867jFG3i9m4SdheTGQUBzpLyujFTyC0czwDYs7J5Yh-ZGJPRmH686QonqD4ANzoTZAqDmaes7" width="320" /></a></div><br /><a href="https://redbooth.com/"><b>RedBooth</b></a></div><div class="separator" style="clear: both; text-align: left;">이 도구는 프로젝트 관리, 비용 관리, 작업 추적 등을 지원한다. </div><br /><a href="https://www.glasscubes.com/"><b>Classubes</b></a></div><div class="separator" style="clear: both; text-align: left;">이 도구는 세어포인트와 유사 기능을 제공하여, 문서 관리, 인트라넷, 일정 관리, 협업 등을 제공한다. </div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEipnh1I2n68Q-BHRVU7hhdo88fNw82gO7T0BjlUv3hAUFultNUDiXX16oKaLoQOxrGKx06FOm2RjxBV3rUQvp9sdAnVc9WExm9h7nB5jV0Jx4uBxJi6v0WKdDO-pZ-AqZbSXyYpdtFvXY7rimfdTOnVI1Qf0ry4-W9FBwFtJBB0MLsSBkzRj04MTrOyAHYt" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="330" data-original-width="545" height="194" src="https://blogger.googleusercontent.com/img/a/AVvXsEipnh1I2n68Q-BHRVU7hhdo88fNw82gO7T0BjlUv3hAUFultNUDiXX16oKaLoQOxrGKx06FOm2RjxBV3rUQvp9sdAnVc9WExm9h7nB5jV0Jx4uBxJi6v0WKdDO-pZ-AqZbSXyYpdtFvXY7rimfdTOnVI1Qf0ry4-W9FBwFtJBB0MLsSBkzRj04MTrOyAHYt" width="320" /></a></div><b><a href="https://www.huddle.com/">Huddle</a></b></div><div class="separator" style="clear: both; text-align: left;">이 도구는 협업 기능을 제공하며, 문서 기반 관리를 제공한다. 파일 공유, 작업 관리, 사용자화가 가능하다.</div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEiYzBUc2wHRN_UH_yiEshmzLOerp9PrYwxVTGCkKpUNvV4keHnWgwqHs7zfvWnLtM3Y5k3cUxHkCZVvqIeVTZCf5QKeQ2t0Y50h3BOF7JC2g5ACDwtdfx6EIlj1Lc_cQ00Bgx2ln60oT5FP8YnH4C0HGz9JqJxueXft8HeKHw5_DvN04yDM35W5eEsonWSc" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="717" data-original-width="970" height="237" src="https://blogger.googleusercontent.com/img/a/AVvXsEiYzBUc2wHRN_UH_yiEshmzLOerp9PrYwxVTGCkKpUNvV4keHnWgwqHs7zfvWnLtM3Y5k3cUxHkCZVvqIeVTZCf5QKeQ2t0Y50h3BOF7JC2g5ACDwtdfx6EIlj1Lc_cQ00Bgx2ln60oT5FP8YnH4C0HGz9JqJxueXft8HeKHw5_DvN04yDM35W5eEsonWSc" width="320" /></a></div><br /></div><div class="separator" style="clear: both; text-align: left;"><span style="font-size: medium;"><b>마무리</b></span></div><div class="separator" style="clear: both; text-align: left;">이외 다음과 같은 오픈소스 기반 문서 관리 도구가 있으니, 참고하기를 바란다. </div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjsjln6OaArQ46w9yDKDO7PgrXX425UfvNh5NxojuS_G8VK18Mhee8p7VsV__szP-9ZYMt6lKdm-PGoJNfxqY6phH-GrYPzLcBbixM3krBlvxO5YxnQfjXk4_jK2Nk6IinjGenrg6dVkwyBY2UDRfcSHNPhr38ylGZCqoDbK_PW_rV5y0nbKm5-T1drdG29" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="896" data-original-width="626" height="640" src="https://blogger.googleusercontent.com/img/a/AVvXsEjsjln6OaArQ46w9yDKDO7PgrXX425UfvNh5NxojuS_G8VK18Mhee8p7VsV__szP-9ZYMt6lKdm-PGoJNfxqY6phH-GrYPzLcBbixM3krBlvxO5YxnQfjXk4_jK2Nk6IinjGenrg6dVkwyBY2UDRfcSHNPhr38ylGZCqoDbK_PW_rV5y0nbKm5-T1drdG29=w448-h640" width="448" /></a></div><div class="separator" style="clear: both; text-align: center;"><a href="https://www.softwaresuggest.com/blog/free-open-source-document-management-system/">Top 14 Free and Open Source Document Management System</a></div><br /></div></div><div style="text-align: left;"><b>레퍼런스</b></div><div style="text-align: left;"><ul style="text-align: left;"><li><a href="https://medevel.com/15-web-based-file-manager/">17 Free and Open-source Web-based Cloud File Manager</a></li><li><a href="https://github.com/filebrowser/filebrowser">filebrowser/filebrowser: 📂 Web File Browser</a></li><li><a href="https://medevel.com/dms-cloud-file-sharing-opensource/">Best 17 Free Open source Self-hosted Document Management Solution (DMS)</a></li><li><a href="https://drupalsun.com/bloggerserge/2018/02/17/which-open-source-document-management-system-best-meets-your-needs">Which Is The Open Source Document Management System That Best Meets Your Needs?</a></li><li><a href="https://www.techjockey.com/blog/top-document-management-software-list">Top 10 Free & Open Source Document Management System List</a></li><li><a href="https://blog.desdelinux.net/en/scan-manage-archive-documents-open-paperless/">Scan, manage and archive documents with Open-paperless</a></li><li><a href="https://www.softwaresuggest.com/blog/free-open-source-document-management-system/">Top 14 Free and Open Source Document Management System</a></li><li><a href="https://www.geeksforgeeks.org/top-5-free-and-open-source-version-control-tools-in-2020/">Top 5 Free and Open-Source Version Control Tools in 2020 - GeeksforGeeks</a></li><li><a href="https://www.goodfirms.co/knowledge-management-software/blog/free-open-source-knowledge-management-software">The 8 Free and Open Source Knowledge Management Tools</a></li><li><a href="https://www.clearpointstrategy.com/blog/alternatives-to-sharepoint">13 Great Alternatives To SharePoint</a></li></ul></div>Daddy Makerhttp://www.blogger.com/profile/02275758703365576780noreply@blogger.com0tag:blogger.com,1999:blog-5201956450461596914.post-34097678314620976562024-03-13T04:38:00.000-07:002024-03-18T04:10:07.603-07:00오픈소스 기반 프로젝트 관리 및 공통 데이터 개발 환경 도구 소개<div style="text-align: left;">이 글은 오픈소스 기반 프로젝트 관리 및 공통 데이터 개발 환경 도구를 소개한다.<br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgMMbczgzZJkx0nvJEheovq93lh1jHJmEIPbYiRRWdx1j1sz9EaIyQbH_MoMH3ZGpzIX-lGID3VrvBr2LZjHnvG7RsTZumV9JtvAQdIkcqwVRUR0YCWE7-S_zON7jf_wkh7Bp3Nu6X8GWnCnlIZTzoMhwqMij9AVZDAxBIZD4hPZfVPHpbpaxu8IrnXLFut" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="746" data-original-width="1200" height="249" src="https://blogger.googleusercontent.com/img/a/AVvXsEgMMbczgzZJkx0nvJEheovq93lh1jHJmEIPbYiRRWdx1j1sz9EaIyQbH_MoMH3ZGpzIX-lGID3VrvBr2LZjHnvG7RsTZumV9JtvAQdIkcqwVRUR0YCWE7-S_zON7jf_wkh7Bp3Nu6X8GWnCnlIZTzoMhwqMij9AVZDAxBIZD4hPZfVPHpbpaxu8IrnXLFut=w400-h249" width="400" /></a></div><br /><br /></div><div style="text-align: left;"><b>레퍼런스</b></div><div style="text-align: left;"><ul style="text-align: left;"><li><a href="https://github.com/shahedbd/awesome-project-management">Awesome lists about Project Management interesting and useful topics.</a></li><li><a href="https://www.cloudwards.net/open-source-project-management-software/">Best Open Source Project Management Software 2024</a></li><li><a href="https://opensource.com/article/18/2/agile-project-management-tools">Top 8 open source project management tools for agile teams | Opensource.com</a></li><li><a href="https://www.techrepublic.com/article/open-source-project-management-software/">8 Best Open Source Project Management Software for 2024</a></li><li><a href="https://github.com/omarsamy3/CDE-ITI-Project">CDE-ITI-Project: this is a simple web application to prove the concept of Common Data Environment(CDE).</a></li><li><a href="https://blog.opendomain.com/what-is-a-cde-for-bim">What is a Common Data Environment for BIM? </a></li><li><a href="https://github.com/marketplace/category/project-management">GitHub Marketplace · Tools to improve your workflow</a></li><li><a href="https://github.com/search?q=project+management&type=repositories&p=4">Repository search results</a></li><li><a href="https://github.com/guilehm/project-management-system?tab=readme-ov-file">A Project Management System built with Python and Django </a></li><li><a href="https://github.com/JordanKnott/taskcafe">An open source project management tool with Kanban boards </a></li><li><a href="https://github.com/opensourceBIM/BIMserver">The open source BIMserver platform </a></li><li><a href="https://github.com/topics/bim-server">bim-server · GitHub Topics</a></li><li><a href="https://github.com/IfcOpenShell/IfcOpenShell/issues/2224">Options for viewing large federated models · Issue #2224 · IfcOpenShell/IfcOpenShell</a></li><li><a href="https://www.openproject.org/">OpenProject - Open Source Project Management Software</a></li><li><a href="https://www.websiteplanet.com/ko/blog/%EB%85%84%EB%8F%84-%EC%B5%9C%EC%83%81%EC%9C%84-%EB%AC%B4%EB%A3%8C-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EA%B4%80%EB%A6%AC-%EC%86%8C%ED%94%84%ED%8A%B8%EC%9B%A8%EC%96%B4/">2024년도 최상위 무료 프로젝트 관리 소프트웨어 6</a></li><li><a href="https://tech.ktcloud.com/19">협업 프로젝트 관리 1부 - 오픈소스 툴 소개</a></li><li><a href="https://odose.tistory.com/entry/%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EA%B4%80%EB%A6%AC-%EB%8F%84%EA%B5%AC-%ED%99%9C%EC%9A%A9%ED%95%98%EA%B8%B0-%ED%8C%80-%ED%9A%A8%EC%9C%A8%EC%84%B1-%ED%96%A5%EC%83%81%EC%9D%84-%EC%9C%84%ED%95%9C-%EA%B0%80%EC%9D%B4%EB%93%9C">프로젝트 관리 도구 활용하기: 팀 효율성 향상을 위한 가이드</a></li></ul></div>Daddy Makerhttp://www.blogger.com/profile/02275758703365576780noreply@blogger.com0tag:blogger.com,1999:blog-5201956450461596914.post-73330901422084920582024-03-11T17:30:00.000-07:002024-03-18T03:47:29.092-07:00유용한 Django GUI 라이브러리 소개<div style="text-align: left;">이 글은 유용한 Django GUI 라이브러리를 소개하는 글이다. </div><div style="text-align: left;">장고는 파이썬 기반 웹 앱 개발 지원 프레임웍으로 매우 강력한 기능을 지원한다. 이 글은 장고 기반 웹 앱 개발 시 사용할 수 있는 유용한 GUI 라이브러리를 살펴본다. </div><div style="text-align: left;"><br /></div><div style="text-align: left;"><b><a href="https://blog.devgenius.io/gantt-charts-in-python-with-plotly-e7213f932f1e">Plotly</a></b></div><div style="text-align: left;">이 도구는 웹 기반 차트 등 생성을 제공한다. 다양한 데이터소스를 제공해, 이를 사전 정의된 테마, 다양한 차트를 통해 웹에 렌더링할 수 있다.</div><div style="text-align: center;"><img alt="" data-original-height="649" data-original-width="828" height="240" src="https://blogger.googleusercontent.com/img/a/AVvXsEgrCSK82dtnahUcXwAQdxx7OfBYCKOCylOnD6txy9CAz9oMDPalz_FEsIL0b5BhcX1dsJB9om-vhQsmGK4N-ZfBKUvs_9BNVESqpGg4qAFeBWQz6spzVYGLmDmHqIMzRiItG7MSIPLdDIBLQSBuo0woXOwBIPwhfr04eynzwphP-AaapU_ZqnJjgM99Q5Gw" style="color: #0000ee;" width="306" /></div><div><br /></div><div><b><a href="https://www.fusioncharts.com/django-charts/?framework=django">FusionCharts</a></b></div><div>150개 이상의 반응형 그래프, 차트를 지원하는 이 도구는 대화형 방식의 GUI를 제공한다. MySQL 서버에서 가져온 데이터를 이용해 즉각적인 차트를 생성한다. 사용자 입력에 대한 이벤트를 지원하고, 실시간으로 차트를 추가할 수 있다. </div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhrfGATo3qTKGsGeSWqRoYHKRX11moq_EVUzzJdp5GV7AW_Cr8NBAF6FEEhbkh78LOH1zlXYtTSKpYdAtyrusB_coSrak5Y_5iMdIsMXYXGrnriuWceW8DvJOUo87nnRrAi_sgaxhAcpUhSwmTR9_SUcnOr8CQVS-ODdGlDHqayPIQMw85oXjiXoBkYNdwd/s740/1.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="527" data-original-width="740" height="228" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhrfGATo3qTKGsGeSWqRoYHKRX11moq_EVUzzJdp5GV7AW_Cr8NBAF6FEEhbkh78LOH1zlXYtTSKpYdAtyrusB_coSrak5Y_5iMdIsMXYXGrnriuWceW8DvJOUo87nnRrAi_sgaxhAcpUhSwmTR9_SUcnOr8CQVS-ODdGlDHqayPIQMw85oXjiXoBkYNdwd/s320/1.PNG" width="320" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div><a href="https://pypi.org/project/highcharts-gantt/"><b>highcharts-gantt</b></a></div><div>간트 차트 등 그래프 생성을 지원한다. 이 도구는 매우 다양한 차트를 제공하는 <a href="https://www.highcharts.com/demo#highcharts-gantt-demo-highcharts-gantt">highcharts</a>를 기반으로 개발되었다. </div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjQm9Y5mxX2ouLQNDREco9BLvYd_K4BhRKeC78rZSpNNAFaUKHgP0lGV2hOn220SIhGwDpkNFuQATc3pDSfX5EiqlnZD7TthAvhjEtlCjCnpTMQRYfAMRUTpwReXqSjVSphJ4m67QzDFna2E9CyNy_iyeAh0qEwoeSfIS7P4ZsP0orgpQkhXY1bGlUgKXyp/s759/2.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="583" data-original-width="759" height="246" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjQm9Y5mxX2ouLQNDREco9BLvYd_K4BhRKeC78rZSpNNAFaUKHgP0lGV2hOn220SIhGwDpkNFuQATc3pDSfX5EiqlnZD7TthAvhjEtlCjCnpTMQRYfAMRUTpwReXqSjVSphJ4m67QzDFna2E9CyNy_iyeAh0qEwoeSfIS7P4ZsP0orgpQkhXY1bGlUgKXyp/s320/2.PNG" width="320" /></a></div><br /><div class="separator" style="clear: both; text-align: center;"><br /></div><a href="https://blog.appseed.us/managing-the-ui-in-django/"><b>Material Dashboard</b></a></div><div>이 도구는 타임 시리즈 데이터를 데쉬보드로 가시화하는 데 유용하다. 부트스트랩 기반으로 개발되었으며, 다양한 테마를 제공한다. </div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgoB4ghrGwOTwF8vpI5YA1XxVoow6qXY-ZtAS7sEb3FmNyoKxZIUFzRu8btYIC18UW_6OKrdGcQLSTX-_CQChaVLMz2PZZh4aYKOXTki6VLxoqOI6Bm70aCh_nIzsJdEDn0-G4dXF91EbaBNbKdCwmTfX6OH1ENhnZvn93CbYPcHivjrECq5q64K4fp3DXj/s1200/3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="569" data-original-width="1200" height="190" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgoB4ghrGwOTwF8vpI5YA1XxVoow6qXY-ZtAS7sEb3FmNyoKxZIUFzRu8btYIC18UW_6OKrdGcQLSTX-_CQChaVLMz2PZZh4aYKOXTki6VLxoqOI6Bm70aCh_nIzsJdEDn0-G4dXF91EbaBNbKdCwmTfX6OH1ENhnZvn93CbYPcHivjrECq5q64K4fp3DXj/w400-h190/3.png" width="400" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div></div><div>기타, 장고에는 DB 객체 맵핑 도구인 ORM, 다중 파일 업로드를 지원하는 <a href="https://medium.com/django-unleashed/implementing-multiple-file-uploads-in-django-e9b1833755ed">ModelForm</a>와 같은 강력한 기능을 기본으로 제공한다. </div><div style="text-align: left;"><br /></div><div style="text-align: left;"><b>레퍼런스</b></div><div style="text-align: left;"><ul style="text-align: left;"><li><a href="https://blog.devgenius.io/gantt-charts-in-python-with-plotly-e7213f932f1e">Gantt Charts in Python with Plotly | by Max Bade | Dev Genius</a></li><li><a href="https://www.fusioncharts.com/django-charts/?framework=django">Django Charts and Graphs for web | 150+ Charts & 1000+ Maps</a></li><li><a href="https://pypi.org/project/highcharts-gantt/">highcharts-gantt · PyPI</a></li><li><a href="https://blog.appseed.us/managing-the-ui-in-django/">Managing the UI in Django | AppSeed Blog</a></li><li><a href="https://medium.com/django-unleashed/implementing-multiple-file-uploads-in-django-e9b1833755ed">Implementing Multiple File Uploads in Django | by Pwaveino Clarkson | Django Unleashed | Medium</a></li></ul></div>Daddy Makerhttp://www.blogger.com/profile/02275758703365576780noreply@blogger.com0tag:blogger.com,1999:blog-5201956450461596914.post-15051146247886756262024-03-05T20:31:00.000-08:002024-03-16T23:10:59.133-07:00UniVision: 시각 중심 3D 인식을 위한 통합 프레임워크<p>3D 인식 모델은 많은 구조적 및 개념적 유사성을 공유하지만 기능 표현, 데이터 형식 및 목표에는 여전히 격차가 존재하여 통합되고 효율적인 3D 인식 프레임워크 설계에 어려움을 겪고 있다. </p><p>이 기술은 비전 중심 3D 인식의 두 가지 주요 작업인 점유 예측과 객체 감지를 통합하는 간단하고 효율적인 프레임워크인 UniVision을 제시한다. 구체적으로 우리는 보완적인 2D-3D 특징 변환을 위한 명시적-암시적 뷰 변환 모듈을 제안한다. 우리는 효율적이고 적응적인 복셀 및 BEV 특징 추출, 향상 및 상호 작용을 위한 로컬-글로벌 특징 추출 및 융합 모듈을 제안한다.</p><p style="text-align: center;"><img alt="" data-original-height="564" data-original-width="1374" height="213" src="https://blogger.googleusercontent.com/img/a/AVvXsEglY_fpevQNlyw8Z-Nj7rNK_j7E9cXPlVTswDiDVE692bsrLoj3XqyFI0jUh_yBkYI96CQVpxF5PMFBJsT3P1QcZDcqHVGHtEsCUJjHb0geVdmg5kmN1avwZJJ5mFvG4ub9js3xZTb5tNvsjEwnLNjVibtzImYXeupUq6_38sWwnGGDGatxNa_yeJzv-Rx-=w520-h213" style="color: #0000ee;" width="520" /></p><p>다중 작업 프레임워크 훈련의 효율성과 안정성을 가능하게 하는 공동 점유 감지 데이터 증대 전략과 점진적인 손실 가중치 조정 전략을 제안한다. nuScenes LiDAR 세분화, nuScenes 감지, OpenOccupancy 및 Occ3D를 포함한 4가지 공개 벤치마크에서 다양한 인식 작업에 대한 광범위한 실험을 수행한다. UniVision은 각 벤치마크에서 각각 1.5mIoU, 1.8NDS, 1.5mIoU, 1.8mIoU 이득으로 최첨단 결과를 달성했다. </p><p></p><div class="separator" style="clear: both; text-align: center;"><br /></div><b>레퍼런스</b><p></p><p></p><ul style="text-align: left;"><li><a href="https://arxiv.org/abs/2401.06994">UniVision: A Unified Framework for Vision-Centric 3D Perception</a></li></ul><p></p>Daddy Makerhttp://www.blogger.com/profile/02275758703365576780noreply@blogger.com0tag:blogger.com,1999:blog-5201956450461596914.post-73090963234364598242024-02-27T03:04:00.000-08:002024-03-17T00:19:50.051-07:00생성AI 기반 코딩 에이전트 개발하기<p> 이 글은 GitHub Copilot과 같은 생성AI 기반 코딩 에이전트 개발방법을 간단히 정리한다.</p><div><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEiZZM-BFEJwwincG-wfGJpu8altjIVBe8k2N7qUf6p9K0_3CVznc2R3OGS9LBhcwdypabngN08FTW2WXYw6n2nSdn4CQxrWYcqblz-5uyaPLOpaCX7kGs1iyi2QOxq1kiy92s9TjWbIa-M-VVJ_xVhHajY3rcCS_m9lOArscG_-H8KTtk1WeoBLDGlZmc4T" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="968" data-original-width="720" height="400" src="https://blogger.googleusercontent.com/img/a/AVvXsEiZZM-BFEJwwincG-wfGJpu8altjIVBe8k2N7qUf6p9K0_3CVznc2R3OGS9LBhcwdypabngN08FTW2WXYw6n2nSdn4CQxrWYcqblz-5uyaPLOpaCX7kGs1iyi2QOxq1kiy92s9TjWbIa-M-VVJ_xVhHajY3rcCS_m9lOArscG_-H8KTtk1WeoBLDGlZmc4T=w298-h400" width="298" /></a></div><div class="separator" style="clear: both; text-align: center;">CODE LLAMA 개념도</div></div></div><div><br /></div><div><b>머리말</b></div><div>Code LLAMA는 코드 작업에 특화된 LLAMA2 기반 기술로, 커뮤니티 라이센스로 릴리즈되었다. 코드 라마는 라마2 기반으로 5,000억 토큰 코드 데이터를 학습하였다. 파이썬, 자연어 명령어를 이해할 수 있고, 미세 조정을 통해 성능을 개선했다. </div><div><br /></div><div>파이썬, C++, JAVA, PHP. C#, TypeScript, Bash 코드 생성을 지원한다. 7B, 13B 모델을 제공하며, 16k 컨텍스트 길이를 학습하였다. 코드 라마는 위치 임베딩을 을 스케일링할 수 있는 RoPE가 적용되었다. </div><div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjVSL7OXhJ4GdBWhA_vVr-8IxT20NYmLoQ2j-peatwl4CwzVvWGSnLPVepFx9kI4MXtmSMinzxk4vg-77fpa1QfBresf4VnN01p2wbc9ElxxH_WMzJ9T7uCn07M8-LcNMMZksW2anPgAlAYiVs-72wJL9eZ7npYeJ7jNv3RQVpR9llFI7vNYzhVU5PNuSdU" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="185" data-original-width="699" height="124" src="https://blogger.googleusercontent.com/img/a/AVvXsEjVSL7OXhJ4GdBWhA_vVr-8IxT20NYmLoQ2j-peatwl4CwzVvWGSnLPVepFx9kI4MXtmSMinzxk4vg-77fpa1QfBresf4VnN01p2wbc9ElxxH_WMzJ9T7uCn07M8-LcNMMZksW2anPgAlAYiVs-72wJL9eZ7npYeJ7jNv3RQVpR9llFI7vNYzhVU5PNuSdU=w468-h124" width="468" /></a></div><div class="separator" style="clear: both; text-align: center;">코드 학습 예시</div></div><div><br /></div></div><div>라마2를 이용해 인터뷰 프로그래밍 질문 항목을 만들고, 코드 라마를 통해 테스트를 수행해 평가되었다. </div><div><br /></div><div>이 글은 다음 개발환경에서 동작되었다. </div><div><ul style="text-align: left;"><li>NVIDIA GPU (Over 28 GB RAM), CUDA 12.1, PyTorch 2.1.2+cu121 (<a href="https://pytorch.org/get-started/locally/">link</a>)</li></ul></div><div>이 글은 학습모델을 최적화하기 위해, 4비트 양자화를 사용해, 크기를 줄인 모델을 사용한다.</div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEh48diwKv_7PBcS6swcVoOB3dz2qjWzMQONKXcR-egUMNrfxOT76-YYrX3sHu-P1iKulx4oBn6ZMI5woZ3eamYWka7K4Pe0iN5PG7AEwGxhtlFTqCqSrsNqSc4YcI6udnYJw8p9K0sP7nUJ2l-lXH_VsvdVUKMQddE3PxbfCYGu79BeaX5YsrJ2O9z1MfDQ" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="679" data-original-width="1600" height="255" src="https://blogger.googleusercontent.com/img/a/AVvXsEh48diwKv_7PBcS6swcVoOB3dz2qjWzMQONKXcR-egUMNrfxOT76-YYrX3sHu-P1iKulx4oBn6ZMI5woZ3eamYWka7K4Pe0iN5PG7AEwGxhtlFTqCqSrsNqSc4YcI6udnYJw8p9K0sP7nUJ2l-lXH_VsvdVUKMQddE3PxbfCYGu79BeaX5YsrJ2O9z1MfDQ=w600-h255" width="600" /></a></div></div><div class="separator" style="clear: both; text-align: center;">4비트 양자화 개념도</div><div><br /></div><div><b>코드 라마 사용 방법</b></div><div>4.33 버전부터는 학습 및 추론 예제, 안전한 텐서 포맷(safetensors), 4비트 양자화, 파라메터 미세조정(PEFT), 모델 생성 지원 도우, 배포 등 기능이 트랜스포머 라이브러리에 추가되었다. 다음과 같이 최신 버전을 설치한다.</div><div><div>pip install --upgrade transformers</div><div><div>pip install accelerate peft bitsandbytes transformers trl</div></div></div><div><br /></div><div>다음은 코드 라마 모델을 로딩하고, 트랜스포머 파이프라인을 생성한 후, 기본적인 함수 정의를 생성하는 예제이다. 입력하고 실행해 본다.</div><div><div>from transformers import AutoTokenizer</div><div>import transformers</div><div>import torch</div><div><br /></div><div>tokenizer = AutoTokenizer.from_pretrained("codellama/CodeLlama-7b-hf")</div><div>pipeline = transformers.pipeline(</div><div> "text-generation",</div><div> model="codellama/CodeLlama-7b-hf",</div><div> torch_dtype=torch.float16,</div><div> device_map="auto",</div><div>)</div><div><br /></div><div>sequences = pipeline(</div><div> 'def fibonacci(',</div><div> do_sample=True,</div><div> temperature=0.2,</div><div> top_p=0.9,</div><div> num_return_sequences=1,</div><div> eos_token_id=tokenizer.eos_token_id,</div><div> max_length=100,</div><div>)</div><div>for seq in sequences:</div><div> print(f"Result: {seq['generated_text']}")</div></div><div><br /></div><div>다음은 그 결과를 보여준다.</div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEiq5FvX6v-_Wk6fEVvIi2SwhtlN2TMkYaAYjpbm1jebegBRsmBXnD1I6eYmUB-80pqhe4iXek9bgIctWOiFPkguqY6VjofbxAXY7_W4jsneRCqB1PCEYWtasetrASlxhBeo1XIp0_Gr0LSZF7_xxdKLIPOXuU5Zb6FcGW2HS-_cXlc_Vce5W1zlNGgEGVY7" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="174" data-original-width="592" height="94" src="https://blogger.googleusercontent.com/img/a/AVvXsEiq5FvX6v-_Wk6fEVvIi2SwhtlN2TMkYaAYjpbm1jebegBRsmBXnD1I6eYmUB-80pqhe4iXek9bgIctWOiFPkguqY6VjofbxAXY7_W4jsneRCqB1PCEYWtasetrASlxhBeo1XIp0_Gr0LSZF7_xxdKLIPOXuU5Zb6FcGW2HS-_cXlc_Vce5W1zlNGgEGVY7" width="320" /></a></div><br /></div><div>이 경우 70억 파라메터를 가진 모델이기 때문에, 최소한 28GB이상 GPU RAM이 필요하다(4바이트일경우 = 7 x 10^9 x 4 bytes. 8바이트일 경우 56GB). </div><div><br /></div><div>라마2 미세 조정 방법은 다음 링크를 참고한다. </div><div><ul style="text-align: left;"><li><a href="https://www.datacamp.com/tutorial/fine-tuning-llama-2">Fine-Tuning LLaMA 2: A Step-by-Step Guide to Customizing the Large Language Model</a></li></ul></div><div><br /></div><div><b>마무리</b></div><div>코딩에 대한 생성 AI 모델 테스트는 일반적으로 HumanEval 데이터세트에서 벤치마킹된다. 이 데이터세트는 모델 함수 서명과 스트링이 제공되며, 함수 본문을 작성하는 프로그래밍 과제로 구성된다. 생성된 코드는 미리 정의된 단위 테스트 세트를 실행해 검증한다. 그 결과 얼마나 많은 테스트를 통과했는 지 비율이 리포트된다. </div><div><br /></div><div>다음은 그 결과 리더보드 평균 점수를 보여준다. </div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhqjL-Pzmc1Bqh3YaiA1dVlk-6vyZjLZS_74oP_HzgyOM7PcSq0cTH1_mfdjCuhjN8MQPvX-AQwxLmG-CCDOifEWQ-5uKEcuLmrCkMD6KmgVurztjRDUP2c_jOS_cjg3QPHvIj0STmKgYugynSN_ksAQE8_GCEgGWzeXtdBILI58XDFxnu70-sQp0gJAPm2" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="526" data-original-width="1808" height="93" src="https://blogger.googleusercontent.com/img/a/AVvXsEhqjL-Pzmc1Bqh3YaiA1dVlk-6vyZjLZS_74oP_HzgyOM7PcSq0cTH1_mfdjCuhjN8MQPvX-AQwxLmG-CCDOifEWQ-5uKEcuLmrCkMD6KmgVurztjRDUP2c_jOS_cjg3QPHvIj0STmKgYugynSN_ksAQE8_GCEgGWzeXtdBILI58XDFxnu70-sQp0gJAPm2" width="320" /></a></div><div class="separator" style="clear: both; text-align: center;"><a href="https://huggingface.co/spaces/bigcode/bigcode-models-leaderboard">Big Code Models Leaderboard - a Hugging Face Space by bigcode</a> (<a href="https://huggingface.co/m-a-p/OpenCodeInterpreter-DS-33B">OpenCode</a>)</div><br /></div><div><br /></div><div><div><b>레퍼런스</b></div><div><ul><li><a href="https://huggingface.co/blog/codellama">Code Llama: Llama 2 learns to code (huggingface.co)</a></li><li><a href="https://www.datacamp.com/tutorial/fine-tuning-llama-2">Fine-Tuning LLaMA 2: A Step-by-Step Guide to Customizing the Large Language Model | DataCamp</a></li><li><a href="https://medium.com/@kinoshitayukari18/how-to-train-llama2-c-with-google-colab-b0a91c36b6a9">How to train llama2.c with Google Colab | by yuca | Medium</a></li><li><a href="https://openmmlab.medium.com/fine-tuning-llama2-takes-less-than-200-lines-of-code-b93dd91f9541">Fine-tuning Llama2 takes less than 200 lines of code! | by OpenMMLab | Medium</a></li><li><a href="https://towardsai.net/p/machine-learning/fine-tuning-a-llama-2-7b-model-for-python-code-generation?amp=1">Fine-Tuning a Llama-2 7B Model for Python Code Generation – Towards AI</a></li><li><a href="https://www.infoworld.com/article/3706470/what-is-llama-2-metas-large-language-model-explained.html">What is Llama 2? Meta’s large language model explained | InfoWorld</a></li><li>Medium, 2023, <a href="https://towardsdatascience.com/how-to-build-an-ai-assistant-with-openai-python-8b3b5a636f69">How to Build an AI Assistant with OpenAI & Python | by Shaw Talebi | Towards Data Science</a></li><li>OpenAI, 2021, <a href="https://arxiv.org/abs/2103.00020" style="text-align: center;">Learning Transferable Visual Models From Natural Language Supervision</a></li><li>Huggin Face, 2023, <a href="https://huggingface.co/docs/transformers/main/model_doc/llama">LLaMA: Open and Efficient Foundation Language Models</a></li><li>Github, <a href="https://github.com/unit-mesh/build-your-ai-coding-assistant">unit-mesh/build-your-ai-coding-assistant</a></li><li>Github, <a href="https://github.com/topics/ai-assistants?l=python">ai-assistants · GitHub Topics</a></li><li>Facebook, 2023, <a href="https://arxiv.org/pdf/2302.13971.pdf">LLaMA: OpenandEfficient Foundation Language Models</a></li><li>Facebook, 2023, <a href="https://arxiv.org/pdf/2307.09288.pdf">Llama 2: OpenFoundation and Fine-Tuned Chat Models</a></li><li><a href="https://scholar.google.co.kr/scholar?hl=en&as_sdt=0%2C5&q=ai+assistant+coding+&btnG=">AI assistant coding - Google Scholar</a></li><li><a href="https://www.reddit.com/r/Python/comments/p3arsz/build_your_own_ai_assistant_with_python/">Build your own AI Assistant with Python : r/Python (reddit.com)</a></li><li><a href="https://www.youtube.com/playlist?list=PLU9tksFlQRirGvp7qOGrrU1PwcjgV8TG1">Build your own AI Assistant - YouTube</a></li><li><a href="https://www.youtube.com/watch?v=q5lML34Noio&list=PLU9tksFlQRirGvp7qOGrrU1PwcjgV8TG1&index=11">Build a ChatGPT-Like language model in Python on a Raspberry Pi (youtube.com)</a></li><li><a href="https://github.com/kevinmcaleer/PythonAI">kevinmcaleer/PythonAI (github.com)</a></li><li><a href="https://www.youtube.com/watch?v=DX9KbfDm2Zk">GeoGPT alpha showcase demo - YouTube</a></li><li><a href="https://medium.com/@ageospatial/geogpt-using-openais-custom-gpts-for-geospatial-analysis-aa6145f9666d">GeoGPT+: Using OpenAI’s custom GPTs for geospatial analysis | by Ageospatial | Jan, 2024 | Medium</a></li><li><a href="https://arxiv.org/pdf/2307.07930.pdf">GeoGPT: Understanding and Processing Geospatial Tasks through An Autonomous GPT</a></li><li>Open BIM <a href="https://www.openbimgpt.com/">GPT (openbimgpt.com)</a></li><li><a href="https://arxiv.org/abs/2304.09333">BIM-GPT: a Prompt-Based Virtual Assistant Framework for BIM Information Retrieval</a></li><li><a href="https://codepen.io/">CodePen: Online Code Editor and Front End Web Developer Community</a></li><li><a href="https://www.youtube.com/watch?v=EsRyyJmO-u8">How ChatGPT Built My App in Minutes 🤯 (youtube.com)</a></li><li><a href="https://tiiny.host/">tiiny.host - The simplest way to share your web project</a></li><li><a href="https://www.webintoapp.com/">Web To App - Turn Your Website Into Android & iOS App Online (webintoapp.com)</a></li><li><a href="https://www.youtube.com/watch?v=5--JexprHuk">How To Create Custom GPTs - Build your own ChatGPT (youtube.com)</a></li></ul></div></div>Daddy Makerhttp://www.blogger.com/profile/02275758703365576780noreply@blogger.com0tag:blogger.com,1999:blog-5201956450461596914.post-39263015937694071022024-02-25T18:21:00.000-08:002024-03-01T17:58:23.337-08:00오픈소스 LLaVA 기반 ChatGPT 4.0 유사한 멀티모달 생성AI 서비스 만들기<div style="text-align: left;">이 글은 ChatGPT 4.0과 같은 LMM(Large langauge Multi-modal Model. 멀티모달 대규모 언어모델)인 LLaVA(Large Language and Vision Assistant. 라바)기반 멀티모달 생성AI 서비스 개발 방법을 나눔한다. </div><div style="text-align: left;"><br /></div><div style="text-align: left;">LLaVA는 Image To Text와 같은 언어-이미지 시각 어시스턴스(Language-Image Visual Assistant)를 지원하기 위해, ViT(Visual Instruction Tuning. 시각적 지시 조정)을 기반으로 개발된 멀티모달 모델 오픈소스이다. 예를 들어, 이미지를 단순히 분류해 주는 것이 아닌, 이미지 내 특정 객체들을 인식하고 관계를 설명할 수 있는 기술을 지원한다.</div><div style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhyXK2OeP8kerVe4_hW9QdJBOZdz_oxcwrf6Me5gt6p85qQSC8F1q3aMUQXWMpet7T4NPhO_memjniPbt-gP0Ew-x0PBQ6MSGwedwGfafGGzRU1HvTbxz0B6J0zJ1TPNALP-gLG-zF73-V0P4aHrfKaGlu18AqsoTTZEPibjLwUjirn0TqpqdTa9_0YhNFj" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="833" data-original-width="1400" height="190" src="https://blogger.googleusercontent.com/img/a/AVvXsEhyXK2OeP8kerVe4_hW9QdJBOZdz_oxcwrf6Me5gt6p85qQSC8F1q3aMUQXWMpet7T4NPhO_memjniPbt-gP0Ew-x0PBQ6MSGwedwGfafGGzRU1HvTbxz0B6J0zJ1TPNALP-gLG-zF73-V0P4aHrfKaGlu18AqsoTTZEPibjLwUjirn0TqpqdTa9_0YhNFj" width="320" /></a></div><div style="text-align: center;">단독 로컬 서버PC에서 LLaVA 서비스 모습</div><div style="text-align: center;"><br /></div><div style="text-align: left;">참고로, ViT는 이미지의 특정 위치에 대한 객체 정보를 인식할 수 있도록 학습하는 기술이다. 예를 들어, GPT-4는 특정 부분의 시각적 특징을 인코딩하기 위해 YOLO모델과 같이 경계 상자를 사용하고, CLIP모델과 같이 해당 부분에 대한 텍스트 임베딩을 입력하여 학습한다(<a href="https://arxiv.org/abs/2304.08485">Visual Instruction Tuning</a>).</div><div style="text-align: left;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhi7Elz2R1supHW156XSoiW9KWPoe0cn5jnCqaHaJl8ZpoQ9nxAs5IOhQbcR7Tdty0Q8Uo60TMCNgHUcog3-LV45cBM-O8Y8-ARgBfZ4mu97S_PMTeSVAoQb1UI-QFZf3NUFMRZ3bqauQwC-lqQDSB9FWWoTxQZIu_dMqIZ6IVslumUrcGHXfh-3Vo4RqL5" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="772" data-original-width="1708" height="181" src="https://blogger.googleusercontent.com/img/a/AVvXsEhi7Elz2R1supHW156XSoiW9KWPoe0cn5jnCqaHaJl8ZpoQ9nxAs5IOhQbcR7Tdty0Q8Uo60TMCNgHUcog3-LV45cBM-O8Y8-ARgBfZ4mu97S_PMTeSVAoQb1UI-QFZf3NUFMRZ3bqauQwC-lqQDSB9FWWoTxQZIu_dMqIZ6IVslumUrcGHXfh-3Vo4RqL5=w400-h181" width="400" /></a></div><div class="separator" style="clear: both; text-align: center;">ViT<span style="text-align: left;">(Visual Instruction Tuning)</span> 개념</div><br /></div><div style="text-align: left;">LLaVA의 NeXT버전은 구글 제미나이 프로 성능을 능가했다고 밝혔으며, 이전 버전인 LLaVA 1.5에 비해 이미지 해상도, OCR 기능 등이 개선되었다고 한다. <br /><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><br /></div></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEiLMmI_gPNY6aUoHOaKVkpVnCt8kP21p6UDVywye0XZMh4waqg_YTmVbonP7lVpjvZr0neXtNQ-9Wpo5rmzS2GBxUU_8GC8Gr40NL78xSAgGKAwn5uAJh2zusK3q912GS3s3h6FdpEV-7BkXMCW4b4iXZ5Yx7TIseQnLUU6qJ99TQqloBZOKTlBbSeKxkPy" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="391" data-original-width="986" height="127" src="https://blogger.googleusercontent.com/img/a/AVvXsEiLMmI_gPNY6aUoHOaKVkpVnCt8kP21p6UDVywye0XZMh4waqg_YTmVbonP7lVpjvZr0neXtNQ-9Wpo5rmzS2GBxUU_8GC8Gr40NL78xSAgGKAwn5uAJh2zusK3q912GS3s3h6FdpEV-7BkXMCW4b4iXZ5Yx7TIseQnLUU6qJ99TQqloBZOKTlBbSeKxkPy" width="320" /></a></div><div class="separator" style="clear: both; text-align: center;">LLaVA 아키텍처</div><div style="text-align: left;"><br /></div><div style="text-align: left;">이 글은 Ollama를 이용해 LLaVA NeXT를 로컬 PC에서 실행하는 방법을 따라해 본다. </div><div style="text-align: left;"><br /></div><b>머리말</b></div><div style="text-align: left;">LLaVA는 대형 멀티모달 모델으로, GPT-4.0과 유사한 LMM을 개발하고자, 마이크로소프트 연구팀에서 오픈소스로 개발되었다. MS는 LLaVA의 논문, github code, demo site 등을 공개하였다. </div><div style="text-align: left;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEht42gG0sjCDS2ccUCB8G9r5LI7Tq-HcNlSqsjhApHU3hOE5E4Ao8Vns-jRFTa3qRk73WEbiwRiQNn5_X9P_dvHqlP6aqi86PnDPRj06NgcMMTwA7Qd9VbQ1S83Vp-58OLS3vJmiGgHvxYDWm6_5zT1Ofw4ktA3uL_4egksn8EvI-wPKWLCPRwzW6magrTf/s753/t3.PNG" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="502" data-original-width="753" height="213" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEht42gG0sjCDS2ccUCB8G9r5LI7Tq-HcNlSqsjhApHU3hOE5E4Ao8Vns-jRFTa3qRk73WEbiwRiQNn5_X9P_dvHqlP6aqi86PnDPRj06NgcMMTwA7Qd9VbQ1S83Vp-58OLS3vJmiGgHvxYDWm6_5zT1Ofw4ktA3uL_4egksn8EvI-wPKWLCPRwzW6magrTf/s320/t3.PNG" width="320" /></a></div><div style="text-align: center;"><a href="https://llava.hliu.cc/"></a><a href="https://llava.hliu.cc/">LLaVA Demo</a></div></div><div style="text-align: left;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj7-GZZm2pUe5631pSg-fPI6xT28LQaz8iXpfo_SksHRwG4G81mi3tadl8_Mnu3OSnNooKbxSivnBadU5j1nBIZ7c4I7wCz5DQscACEsFTk_rsz7rXGXdNlyuO6K6-pfOQrzrVcCH7Eg2yFAQk0wd1iVMyQxmPKCkw1QjzJNzZtIvT_9VcalcSNjTKvwefc/s1181/t2.PNG" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="746" data-original-width="1181" height="202" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj7-GZZm2pUe5631pSg-fPI6xT28LQaz8iXpfo_SksHRwG4G81mi3tadl8_Mnu3OSnNooKbxSivnBadU5j1nBIZ7c4I7wCz5DQscACEsFTk_rsz7rXGXdNlyuO6K6-pfOQrzrVcCH7Eg2yFAQk0wd1iVMyQxmPKCkw1QjzJNzZtIvT_9VcalcSNjTKvwefc/s320/t2.PNG" width="320" /></a></div><div class="separator" style="clear: both; text-align: center;">LLaVA paper(<a href="https://www.microsoft.com/en-us/research/publication/visual-instruction-tuning/" style="text-align: left;">Visual Instruction Tuning - Microsoft Research</a>)</div><div class="separator" style="clear: both; text-align: center;"><br /></div></div><div style="text-align: left;"><div>LLaVA LMM(Large language Multi-modal Model)은 비전 인코더, LLM 모델을 기반으로 개발되었으며, Image to Text에서 인상적인 성능을 보여준다. LLaVA는 비전 인코더로 OpenAI에서 공개한 CLIP 모델을 사용했으며, 메타(페이스북)에서 공개한 LLaMA 기반 Vicuna LLM 모델을 사용했다. 학습은 A100 GPU x 8 x 1 Day 와 60만개 데이터셋을 사용했다.</div><div><br /></div></div><div style="text-align: left;">LLaVA를 설치하고, 실행해 보기 위해서는 다음 개발환경이 컴퓨터에 미리 설치되어 있다고 가정한다(Ubuntu, NVIDIA, CUDA등 설치 방법은 앞의 글 참고). </div><div><ul><li>NVIDIA driver, <a href="https://developer.nvidia.com/cuda-12-0-0-download-archive?target_os=Linux&target_arch=x86_64&Distribution=Ubuntu&target_version=22.04&target_type=deb_network">CUDA</a>, Python, <a href="https://www.anaconda.com/download">anaconda</a>, Ubuntu 22.04</li><li>Tensorflow, PyTorch</li><li><a href="curl -fsSL https://ollama.com/install.sh | sh">Ollama</a></li></ul><div><div>터미널에서 다음 명령을 실행해 LLAVA모델을 설치한다. </div><div><a href="https://ollama.com/library/llava">ollama run llava</a></div><div><br /></div><div>제대로 설치되면, 다음과 같이 프롬프트 질문과 답변을 얻을 수 있다.</div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgf9ty6BcjVemqMUHGzrIHzf6KU--0HUpO8rBpcRtkNSSp8Rc7mD5di9yW_wQVsoxjdVSxZapy1yUGfzc_vb1bGrUYUCBtWX_o-iYt-m-P0twnNq_5uHtvfElHRpjBo5a-PTqZwM8HyPozkNZ3KWNlJaEhA_lnNv7DUZh0pmPCLxZr4s4w739bGdchtwp4n" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="641" data-original-width="889" height="231" src="https://blogger.googleusercontent.com/img/a/AVvXsEgf9ty6BcjVemqMUHGzrIHzf6KU--0HUpO8rBpcRtkNSSp8Rc7mD5di9yW_wQVsoxjdVSxZapy1yUGfzc_vb1bGrUYUCBtWX_o-iYt-m-P0twnNq_5uHtvfElHRpjBo5a-PTqZwM8HyPozkNZ3KWNlJaEhA_lnNv7DUZh0pmPCLxZr4s4w739bGdchtwp4n" width="320" /></a></div><div class="separator" style="clear: both; text-align: center;">LLaVA모델에게 PyThon 코딩 요청한 모습</div><div><br /></div></div></div></div><div style="text-align: left;"><b>개발환경 준비</b></div><div style="text-align: left;">다음 명령을 터미널에서 실행한다. </div><div style="text-align: left;">git clone https://github.com/haotian-liu/LLaVA.git</div><div style="text-align: left;">conda create -n venv_lmm python=3.10 -y</div><div style="text-align: left;">conda activate venv_lmm</div><div style="text-align: left;">pip install --upgrade pip</div><div style="text-align: left;">pip install -e .</div><div style="text-align: left;">pip install cog</div><div style="text-align: left;"><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEh9pSzWFiKdHIZD_1Sw8hygtfklB8lECNRCGdWSlr_CXxM8qVf4NQ4Uay5z8D6fj5u6PbbFKH3LLCUZvBdi5hYlsALTy6qXJ8BCGwOnT1iESezFCjYgMnz64fysdjV8pVd3UFWFRagjFuxoygYJ8osfJVXyD97F5BNuwdIOViVcjf3Kx4DBbQn6pG6cUD8x" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="362" data-original-width="888" height="163" src="https://blogger.googleusercontent.com/img/a/AVvXsEh9pSzWFiKdHIZD_1Sw8hygtfklB8lECNRCGdWSlr_CXxM8qVf4NQ4Uay5z8D6fj5u6PbbFKH3LLCUZvBdi5hYlsALTy6qXJ8BCGwOnT1iESezFCjYgMnz64fysdjV8pVd3UFWFRagjFuxoygYJ8osfJVXyD97F5BNuwdIOViVcjf3Kx4DBbQn6pG6cUD8x=w400-h163" width="400" /></a></div><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEi3vgXKTK3vjjxwdobJI3LkAwiG2lHtMihrSzg1bzpZxhghAtCpmQ1Wluj_FhlIfDbA5MbMY1zv5aBalnmC8Ia8JvBn-g5VYVbr6zmLfiX4ZERuKgqjpPcumZXMXs8LQNs18L2fAOCnAZbYeaKuHvjrTEqC3NDFoEjIKwEzlNawm813Bxw4bJ571xdwhy0_" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="362" data-original-width="888" height="163" src="https://blogger.googleusercontent.com/img/a/AVvXsEi3vgXKTK3vjjxwdobJI3LkAwiG2lHtMihrSzg1bzpZxhghAtCpmQ1Wluj_FhlIfDbA5MbMY1zv5aBalnmC8Ia8JvBn-g5VYVbr6zmLfiX4ZERuKgqjpPcumZXMXs8LQNs18L2fAOCnAZbYeaKuHvjrTEqC3NDFoEjIKwEzlNawm813Bxw4bJ571xdwhy0_=w400-h163" width="400" /></a></div></div></div><br /></div><div style="text-align: left;"><b>LLaVA 실행하기</b></div><div style="text-align: left;">다음 명령을 각 터미널에서 실행한다.</div><div style="text-align: left;">python -m llava.serve.controller --host 0.0.0.0 --port 10000</div><div style="text-align: left;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgWykBlV0tE9GhMKsF5v0rdzL7qQgsMWvEEY7mMZ_qidXP0eBw_6WsufjUOkrko2s_Yh2SlZYkclNVaeiQq_razuXTVj3SG8JVsXNtu25B-dDUh_Gq2QdueQTyI8Lf8w5Bt8LBEjm6cgjwF5d238fKUxtLHne4AKXsFb8IeF8fA0jA_f6Pvd-MGzGebQSaa" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="403" data-original-width="892" height="145" src="https://blogger.googleusercontent.com/img/a/AVvXsEgWykBlV0tE9GhMKsF5v0rdzL7qQgsMWvEEY7mMZ_qidXP0eBw_6WsufjUOkrko2s_Yh2SlZYkclNVaeiQq_razuXTVj3SG8JVsXNtu25B-dDUh_Gq2QdueQTyI8Lf8w5Bt8LBEjm6cgjwF5d238fKUxtLHne4AKXsFb8IeF8fA0jA_f6Pvd-MGzGebQSaa" width="320" /></a></div><br /></div><div style="text-align: left;">python -m llava.serve.model_worker --host 0.0.0.0 --controller http://localhost:10000 --port 40000 --worker http://localhost:40000 --model-path liuhaotian/llava-v1.6-34b --load-4bit</div><div style="text-align: left;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEj__0W2QnlhDNJ8kHEwcvKAL-xNqY8atmzkJ_AlIm45JD0jASUfYzl2LIM2m0sVMhvIEnptHKUyctx5ROfx0hL0xZofTr_xLdbOFdItWfUXTLM4kAto_rFFIY1H1rKRCruiRo5ZoQGBML7WeXFu0e6sa6PhXwpnE7ZMWUxEBkwX5NO22S8Lk4roeNVDUDo1" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="557" data-original-width="893" height="200" src="https://blogger.googleusercontent.com/img/a/AVvXsEj__0W2QnlhDNJ8kHEwcvKAL-xNqY8atmzkJ_AlIm45JD0jASUfYzl2LIM2m0sVMhvIEnptHKUyctx5ROfx0hL0xZofTr_xLdbOFdItWfUXTLM4kAto_rFFIY1H1rKRCruiRo5ZoQGBML7WeXFu0e6sa6PhXwpnE7ZMWUxEBkwX5NO22S8Lk4roeNVDUDo1" width="320" /></a></div>다운로드된 학습 모델은 다음 폴더에 저장된다(불필요한 모델 파일은 rm -rf 명령으로 삭제할 수 있음).</div><div style="text-align: left;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhqAL5vdvmfEj5maxp-NVhbCAY6nnN0FM4SqothUqjhTvcBRTmPlJnQ8J_E8XQ_PH8pHlvUkQ8WmnOfTw9xtVDx_dHqCbekmAt0jsAsLp3rAIDGoxTqi2OdFPS8gVY5DCLmryZKn14wIIfSP0kpLTNCQkhyvhndhvLQTAZoS4-D9U4Acqhw9OEShctX8CIV" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="209" data-original-width="1008" height="66" src="https://blogger.googleusercontent.com/img/a/AVvXsEhqAL5vdvmfEj5maxp-NVhbCAY6nnN0FM4SqothUqjhTvcBRTmPlJnQ8J_E8XQ_PH8pHlvUkQ8WmnOfTw9xtVDx_dHqCbekmAt0jsAsLp3rAIDGoxTqi2OdFPS8gVY5DCLmryZKn14wIIfSP0kpLTNCQkhyvhndhvLQTAZoS4-D9U4Acqhw9OEShctX8CIV" width="320" /></a></div><br /></div><div style="text-align: left;">참고로, 모델 다운로드에 매우 오랜 시간이 걸리므로, 실행해 놓고, 다른 일을 하는 것이 좋다. </div><div style="text-align: left;"><br /></div><div style="text-align: left;">모델 다운로드 후 다음을 실행한다. 그럼 gradio_web_server 웹서버가 실행되고, 해당 주소로 LLaVA UI가 서비스된다. 참고로 <a href="https://www.gradio.app/">Gradio</a>는 머신러닝 파이프라인을 위한 Web App을 빠르게 만들 수 잇는 파이썬 라이브러리이다(참고). </div><div style="text-align: left;"><div>python -m llava.serve.gradio_web_server --controller http://localhost:10000 --model-list-mode reload --share</div><div><br /></div><div>실행되면, 다음과 같이 Gradio 웹 인터페이스가 실행된다. 궁금한 프롬프트를 입력하면, 적절히 잘 대답해준다. </div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgycbUVguFCRszKsTh8ThwCWmoExiPJwit05IterrBuG7hbQYcEiesimgSSeJeHgVBBjdhPP9C1zzzj1Z2bIUYbjMk1arRgyeHyBdKguvfjy79r6dRTHyqwg3sr7nSofw2uPwhImt6ClhivORJ3aw4blR4caT4p_sVAXhINqVU2-luC_Z5CFIsBDRYKJfxe" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="1249" data-original-width="2304" height="288" src="https://blogger.googleusercontent.com/img/a/AVvXsEgycbUVguFCRszKsTh8ThwCWmoExiPJwit05IterrBuG7hbQYcEiesimgSSeJeHgVBBjdhPP9C1zzzj1Z2bIUYbjMk1arRgyeHyBdKguvfjy79r6dRTHyqwg3sr7nSofw2uPwhImt6ClhivORJ3aw4blR4caT4p_sVAXhINqVU2-luC_Z5CFIsBDRYKJfxe=w533-h288" width="533" /></a></div></div><div><br /></div><div>다음과 같이 적절한 이미지를 입력해 보고, 질문해 본다. 그럼, 그림 내용을 묘사 설명해준다.</div></div><div style="text-align: left;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhyXK2OeP8kerVe4_hW9QdJBOZdz_oxcwrf6Me5gt6p85qQSC8F1q3aMUQXWMpet7T4NPhO_memjniPbt-gP0Ew-x0PBQ6MSGwedwGfafGGzRU1HvTbxz0B6J0zJ1TPNALP-gLG-zF73-V0P4aHrfKaGlu18AqsoTTZEPibjLwUjirn0TqpqdTa9_0YhNFj" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="833" data-original-width="1400" height="190" src="https://blogger.googleusercontent.com/img/a/AVvXsEhyXK2OeP8kerVe4_hW9QdJBOZdz_oxcwrf6Me5gt6p85qQSC8F1q3aMUQXWMpet7T4NPhO_memjniPbt-gP0Ew-x0PBQ6MSGwedwGfafGGzRU1HvTbxz0B6J0zJ1TPNALP-gLG-zF73-V0P4aHrfKaGlu18AqsoTTZEPibjLwUjirn0TqpqdTa9_0YhNFj" width="320" /></a></div><br /></div><div style="text-align: left;"><b>LLaVA 모델 에러 처리 방법</b></div><div style="text-align: left;">실제로 LLaVA 모델을 실행해보면 다양한 에러가 발생할 수 있다. 다음은 시행착오를 정리한 것이다.</div><div style="text-align: left;"> </div><div style="text-align: left;">1. llava.service 에러가 발생하면, OLLAMA를 다음과 같이 설치하고 다시 시도한다. </div><div style="text-align: left;">curl -fsSL https://<a href="https://ollama.com/download/linux">ollama.com</a>/install.sh | sh</div><div style="text-align: left;"><div><br /></div><div>2. 패키지 설치가 잘못되었을 수 있다. 이 시점에도 LLaVA모델 코드는 계속 수정되고 있는 상황이다. 이런 이유로 패키지 버전 불일치 문제가 발생할 수 있다. 이 경우, 에러를 확인해 하나씩 적절한 패키지 버전을 설치한다. </div><div>pip install llava cog pydantic fastapi gradio protobuf</div><div><br /></div></div><div style="text-align: left;">3. 앞의 내용 실행 중 허깅페이스 CLI 토큰 입력 요청이 있을 수 있다. 이 경우, 아래 링크를 참고해, 토큰을 발급받고, 입력한다. </div><div style="text-align: left;"><a href="https://huggingface.co/docs/huggingface_hub/main/en/guides/cli">Command Line Interface (CLI) (huggingface.co)</a></div><div style="text-align: left;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjR32ptLhzHNXMqs8DKYoNedfpI2L4tf8ysklO4CIwpcIUbw0MMDS2kgd5Y7Us-2ck8oyo4JigTwZeMr9483dFD3t_tJsOsU5Bocs1BoFL0ItOFHBnRPW7Ndfu2fPYWPjci7IyYAoMYzl85hy8B5qKKbGzq6PwtktqLz8CvvayNBvzmDpm-sjnDBQUNguje" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="530" data-original-width="969" height="175" src="https://blogger.googleusercontent.com/img/a/AVvXsEjR32ptLhzHNXMqs8DKYoNedfpI2L4tf8ysklO4CIwpcIUbw0MMDS2kgd5Y7Us-2ck8oyo4JigTwZeMr9483dFD3t_tJsOsU5Bocs1BoFL0ItOFHBnRPW7Ndfu2fPYWPjci7IyYAoMYzl85hy8B5qKKbGzq6PwtktqLz8CvvayNBvzmDpm-sjnDBQUNguje" width="320" /></a></div><div class="separator" style="clear: both; text-align: center;">Huggingface Token 발급 예시(<a href="https://huggingface.co/settings/tokens" style="text-align: left;">Hugging Face</a> Access Token)</div><br /></div><div style="text-align: left;">4. bitsandbytes 에러가 발생할 수 있다.</div><div style="text-align: left;"><div>UserWarning: The installed version of bitsandbytes was compiled without GPU support. 8-bit optimizers, 8-bit multiplication, and GPU quantization are unavailable" warn("The installed version of bitsandbytes was compiled without GPU support. " 'NoneType' object has no attribute 'cadam32bit_grad_fp</div></div><div style="text-align: left;"><br /></div><div style="text-align: left;">이 경우, 다음 링크를 참고해 해결해야 한다.</div><div style="text-align: left;"><a href="https://www.reddit.com/r/Oobabooga/comments/131smlg/error_the_installed_version_of_bitsandbytes_was/">Error: "The installed version of bitsandbytes was compiled without GPU support" : r/Oobabooga (reddit.com)</a></div><div style="text-align: left;"><br /></div><div style="text-align: left;">5. GPU RAM 에러가 발생할 수 있다. 그럼, 좀 더 작은 사이즈 모델(<a href="https://huggingface.co/collections/liuhaotian/llava-15-653aac15d994e992e2677a7e">참고</a>)을 다운로드받아 서버를 실행한다.</div><div style="text-align: left;"><div>python -m llava.serve.model_worker --host 0.0.0.0 --controller http://localhost:10000 --port 40000 --worker http://localhost:40000 --model-path liuhaotian/llava-v1.5-7b --load-4bit</div><div><br /></div></div><div style="text-align: left;"><b>마무리</b></div><div style="text-align: left;">ChatGPT-4와 같은 LMM을 LLaVA를 이용해 코드 한 줄 없이 로컬 PC에 설치하고, 서비스를 구현할 수 있다. 실행 결과 LLaVA의 꽤 훌륭한 LMM 성능을 확인할 수 있다.</div><div style="text-align: left;"><br /></div><div style="text-align: left;">다만, 아직 특정 전문 분야의 멀티 모달리티 성능은 아직 그리 좋지 못하다. 참고로, MS, Google과 같은 빅테크 기업들은 시장성이 큰 헬스케어 분야에서 대학 병원 등과 협업하여, 이런 격차를 줄이기 위해 노력하고 있어, 조만간 더욱 발전될 것이다.</div><div style="text-align: left;"><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEigZG1rhCSrOnShtIMBqI1XKY3BnIzgoFRwmDKSiTyQzlIrZWb7rH37SnRZcbdPbcuQvate_BTf690Wpr4CdXLTXUQ195w9odbpNB0rmScitcooXm5LmF_wb0FPkIAYQvVTp3HtKenSnYM0vtZslZ8WeKZjn8dof0csxZq3kPgtrFVJ_-shXyIueJ98_zGL" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="199" data-original-width="982" height="103" src="https://blogger.googleusercontent.com/img/a/AVvXsEigZG1rhCSrOnShtIMBqI1XKY3BnIzgoFRwmDKSiTyQzlIrZWb7rH37SnRZcbdPbcuQvate_BTf690Wpr4CdXLTXUQ195w9odbpNB0rmScitcooXm5LmF_wb0FPkIAYQvVTp3HtKenSnYM0vtZslZ8WeKZjn8dof0csxZq3kPgtrFVJ_-shXyIueJ98_zGL=w506-h103" width="506" /></a></div><div class="separator" style="clear: both;">LLAVA-Med (Microsoft)</div><div style="text-align: left;"></div></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEid7kVPeCpVoowwua8FwcU-m9eCEyok46PztJiAh06ebmiwc3iRJLIOofe9ZKMvjtyuTvzJ9Yh32hNSIqtQi4bC3XaqiVmZPK36-fTYcjBisLs04VdOxitoJW47yBx7fJW400df1CVnJgnzSfaUjfe1maU9wEVKtkW7VwTgKXEniWxf3vmsjV73HVerwlqE" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="364" data-original-width="976" height="149" src="https://blogger.googleusercontent.com/img/a/AVvXsEid7kVPeCpVoowwua8FwcU-m9eCEyok46PztJiAh06ebmiwc3iRJLIOofe9ZKMvjtyuTvzJ9Yh32hNSIqtQi4bC3XaqiVmZPK36-fTYcjBisLs04VdOxitoJW47yBx7fJW400df1CVnJgnzSfaUjfe1maU9wEVKtkW7VwTgKXEniWxf3vmsjV73HVerwlqE=w400-h149" width="400" /></a></div><div class="separator" style="clear: both; text-align: center;">LLaVA-Med 학습 예시(헬스케어 분야. Microsoft)</div><br /></div><div style="text-align: left;"><b>레퍼런스</b><br /></div><div style="text-align: left;"><ul style="text-align: left;"><li>Microsoft, 2023.6, <a href="https://syncedreview.com/2023/06/06/microsofts-llava-med-trains-a-large-language-and-vision-assistant-for-biomedicine-within-15-hours/">LLaVA-Med Trains a Large Language-and-Vision Assistant for Biomedicine Within 15 Hours | Synced (syncedreview.com)</a></li><li><a href="https://blog.nomic.ai/posts/nomic-embed-matryoshka">Nomic, 2024.1, Nomic Embed 임베딩 기술</a></li><li><a href="https://ollama.com/blog/vision-models">Vision models · Ollama Blog</a></li><li><a href="https://mer.vin/2024/01/llava-model-ui-setup/">Llava Model UI Setup - Mervin Praison</a></li><li><a href="https://morioh.com/a/070337236712/llava-visual-instruction-tuning">Visual instruction tuning towards large language and vision models with GPT-4 level capabilities considering GPU RAM</a> </li><li><a href="https://towardsdatascience.com/llava-an-open-source-alternative-to-gpt-4v-ision-b06f88ce8efa">Open source alternative to GPT4</a></li><li><a href="https://medium.com/@dimas230220020/introduce-how-to-using-llava-large-language-and-vision-assistant-439a1aacfdb2">Introduce how to using LLaVA : Large Language and Vision Assistant | by Dimas Mulya | Medium</a></li><li><a href="https://morioh.com/a/070337236712/lit_gpt/model.py">LLaVA: Visual Instruction Tuning (morioh.com)</a>, <a href="https://fornewchallenge.tistory.com/entry/Ollama%EC%99%80-%EB%8C%80%EA%B7%9C%EB%AA%A8-%EC%96%B8%EC%96%B4-%EB%AA%A8%EB%8D%B8-Llama2-uncensored%EB%A5%BC-%ED%99%9C%EC%9A%A9%ED%95%9C-PDF-%EC%9A%94%EC%95%BD%EA%B3%BC-%EC%9D%8C%EC%84%B1%EB%B3%80%ED%99%98">PDF-Text-Voice</a>, <a href="https://fornewchallenge.tistory.com/entry/LLaVA-NeXT-%EC%A0%9C%EB%AF%B8%EB%82%98%EC%9D%B4-%ED%94%84%EB%A1%9C%EB%A5%BC-%EB%9B%B0%EC%96%B4%EB%84%98%EB%8A%94-%EC%98%A4%ED%94%88%EC%86%8C%EC%8A%A4-%EB%A9%80%ED%8B%B0%EB%AA%A8%EB%8B%AC-AI">LLaVA</a>, <a href="https://fornewchallenge.tistory.com/entry/LiteLLM%EC%9C%BC%EB%A1%9C-Mistral-7B%EC%99%80-%EB%8C%80%ED%99%94%ED%95%98%EB%8A%94-%EC%9B%B9-%EC%B1%97%EB%B4%87-%EB%A7%8C%EB%93%A4%EA%B8%B0">LiteLLM</a></li><li><a href="https://sites.research.google/videopoet/">VideoPoet – Google Research</a></li><li><a href="https://www.gradio.app/guides/running-gradio-on-your-web-server-with-nginx">Running Gradio On Your Web Server With Nginx</a></li></ul></div>Daddy Makerhttp://www.blogger.com/profile/02275758703365576780noreply@blogger.com0tag:blogger.com,1999:blog-5201956450461596914.post-49321588434673100762024-02-25T07:00:00.000-08:002024-03-17T00:10:46.422-07:00OpenAI SORA와 유사한 생성AI Text To Video 기술 개발 동향과 사용법 소개<div style="text-align: left;">이 글은 생성AI Text To Video 모델 개발하는 방법을 정리한다.<br /></div><div style="text-align: left;"><br /></div><div style="text-align: left;"><b>머리말</b></div><div style="text-align: left;">얼마전 공개된 OpenAI의 SORA는 생성AI 기술이 혁신적으로 발전되고 있음을 보여준다. 현재, 이와 관련된 다양한 오픈소스 프로젝트가 진행중에 있어, 이를 간략히 살펴보도록 한다. </div><div style="text-align: left;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh9Zpu1kq43PTxx3zI5nOSauHESuCpIEHaXYmoCLnsqMzkIdHTRNY6vQFKw9Fi7WIvD1ANou5xyGBLdOgZrhCs_ctUJuaVNODfpW6I2yBab-69SVllv7Z5H9bvOX0zaxZoB3x29uK4pbNQjf5QPI0NdSVbdvHFWmACtThMVANld0IqAN-wdqakuTj5_cydJ/s807/g1.PNG" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="485" data-original-width="807" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh9Zpu1kq43PTxx3zI5nOSauHESuCpIEHaXYmoCLnsqMzkIdHTRNY6vQFKw9Fi7WIvD1ANou5xyGBLdOgZrhCs_ctUJuaVNODfpW6I2yBab-69SVllv7Z5H9bvOX0zaxZoB3x29uK4pbNQjf5QPI0NdSVbdvHFWmACtThMVANld0IqAN-wdqakuTj5_cydJ/w400-h240/g1.PNG" width="400" /></a></div><div class="separator" style="clear: both; text-align: center;">Text to Video(Lumiere, 2024)</div><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: left;">이 기술은 간단히 말하면, 기존 이미지-텍스트 생성AI 기술인 스테이블 디퓨전에서 시간축을 학습 시 함께 고려한 것이다. 이와 관련된 좀 더 상세한 설명은 다음 링크를 참고한다.</div><div class="separator" style="clear: both; text-align: center;"><iframe allowfullscreen="" class="BLOG_video_class" height="266" src="https://www.youtube.com/embed/fG3IE9dkyKY" width="320" youtube-src-id="fG3IE9dkyKY"></iframe></div><div class="separator" style="clear: both; text-align: center;">오픈AI 소라 동작 메커니즘 설명</div><ul><li><a href="https://daddynkidsmakers.blogspot.com/2017/02/blog-post_24.html" style="color: #992211; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; text-decoration-line: none;"><span>머신러닝 딥러닝 신경망 개념, 종류 및 개발</span></a></li><li><a href="https://daddynkidsmakers.blogspot.com/2021/10/blog-post.html" style="font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif;"><span>어텐션 기반 트랜스포머 딥러닝 모델 이해, 활용 사례 및 파치토치를 통한 간단한 사용방법 소개</span></a></li><li><a href="https://daddynkidsmakers.blogspot.com/2024/02/blog-post.html"><span>트랜스포머 디코더 핵심 코드 구현을 통한 동작 메커니즘 이해하기</span></a></li><li><a href="https://daddynkidsmakers.blogspot.com/2024/02/llama-2.html"><span>오픈소스 기반 LLM의 민주화, LLAMA-2 논문 분석 및 기술 요약</span></a></li><li><a href="https://daddynkidsmakers.blogspot.com/2024/02/clip.html"><span>생성AI 멀티모달 모델 개발의 시작. OpenAI의 CLIP모델 이해, 코드 분석, 개발, 사용하기</span></a></li><li><a href="https://github.com/mac999/computer_vision_deeplearning/tree/main" style="color: #992211; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; text-decoration-line: none;"><span>Computer vision deep learning: computer vision based on deep learning lecture materials, github</span></a></li><li><a href="https://daddynkidsmakers.blogspot.com/2022/11/3.html" style="color: #992211; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; text-decoration-line: none;"><span>딥러닝 기반 3차원 스캔 데이터, 포인트 클라우드 학습</span></a></li><li><a href="https://daddynkidsmakers.blogspot.com/2024/02/ai-stable-diffusion.html">파이토치로 멀티모달 생성AI 모델, Stable Diffusion 아키텍처를 코딩, 구현해보기</a></li></ul><div class="separator" style="clear: both; text-align: center;"><br /></div></div><div style="text-align: left;"><b>Generative Models by Stability AI</b></div><div style="text-align: left;">2023년 11월에 릴리즈한 이 오픈소스 프로젝트는 Stability AI에서 개발한 것이다.</div><div style="text-align: left;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgxpof6bv1O6S7LqgOvB5LGdNRetYGvjTnYN7ZoywhscknvSSMEGfiEiQpXT2jivhU7ndvSeOA0_ABlhPv8wRDaTzn73j02MRvtU5LcRbKW826M9tAUpKE_gO4nI6GLCRZMsPW3vOtBQt3xzilr6_v0BtPaLqqovruVtgqUWi8eDNwwJU0TK9uH1mgXPxVP" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="576" data-original-width="1024" height="180" src="https://blogger.googleusercontent.com/img/a/AVvXsEgxpof6bv1O6S7LqgOvB5LGdNRetYGvjTnYN7ZoywhscknvSSMEGfiEiQpXT2jivhU7ndvSeOA0_ABlhPv8wRDaTzn73j02MRvtU5LcRbKW826M9tAUpKE_gO4nI6GLCRZMsPW3vOtBQt3xzilr6_v0BtPaLqqovruVtgqUWi8eDNwwJU0TK9uH1mgXPxVP" width="320" /></a></div><div class="separator" style="clear: both; text-align: center;"><a href="https://github.com/Stability-AI/generative-models">Generative Models by Stability AI</a></div><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: left;">이 기술은 공개한 SDXL기술에 기반하여, ADD(Adversarial Diffusion Distillation)을 적용하여, 높은 해상도의 영상을 생성한다(<a href="https://static1.squarespace.com/static/6213c340453c3f502425776e/t/65663480a92fba51d0e1023f/1701197769659/adversarial_diffusion_distillation.pdf">논문 참고</a>). ADD기술을 통해, 디퓨전 시 문제되는 흐릿한 영상 생성 이슈를 해결하고, 엉뚱하게 생성되는 손가락 문제 등을 해결한다. </div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEiBEAAzOISsA5fSLM5ED4XHFJqY0Ch3yprlpHMb8ub3h6L5z092HY1jwKX_cySk-2bcepdrRILSETNO1FisX3C1xNCpkn08Hh0ytucdqnUj0meh8LG9N8hCkzCQkw0TCLyVxFGAfT77mKYHj4CkI5WvxcEfqghtMYqVOCNiG1CP-ALxV4_vjwiWFqoLvGCF" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="433" data-original-width="504" height="240" src="https://blogger.googleusercontent.com/img/a/AVvXsEiBEAAzOISsA5fSLM5ED4XHFJqY0Ch3yprlpHMb8ub3h6L5z092HY1jwKX_cySk-2bcepdrRILSETNO1FisX3C1xNCpkn08Hh0ytucdqnUj0meh8LG9N8hCkzCQkw0TCLyVxFGAfT77mKYHj4CkI5WvxcEfqghtMYqVOCNiG1CP-ALxV4_vjwiWFqoLvGCF" width="279" /></a></div><div class="separator" style="clear: both; text-align: center;">ADD</div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;">이 기술은 오픈소스로 깃허브에 공개되었다.</div><div class="separator" style="clear: both; text-align: left;"><ul style="text-align: left;"><li>Stability AI. <a href="https://github.com/Stability-AI">https://github.com/Stability-AI</a></li></ul></div><div class="separator" style="clear: both; text-align: left;"><br /></div><b>CogVideo</b></div><div class="separator" style="clear: both; text-align: left;">칭화대학 연구소에서 개발한 CogVideo는 막대한 학습 비용 문제를 해결하기 위해, 사전학습된 모델을 상혹하여 9B 파라메터 모델 수준에서 학습이 가능한 CogVideo모델을 제안한다. </div><div class="separator" style="clear: both; text-align: center;"><br /></div></div><div style="text-align: left;"><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhjOFtxF0Rir9iLCMlnKPhXTDOu8M7ZEt1zSs-pMnHwWjrH4QuH9qmkpheb0NNWHjYIYliHN6raboovathbzqM4TkJory-uN7vRKOlOtWE9a2GtuNvlOW5bYAL4eJJKt-kceymmOLCckfPr3Lb81IFGIxRxqDg9hGVLWNtrOdFYDQj5UnNnF7srSlRpjUeE" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="524" data-original-width="898" height="234" src="https://blogger.googleusercontent.com/img/a/AVvXsEhjOFtxF0Rir9iLCMlnKPhXTDOu8M7ZEt1zSs-pMnHwWjrH4QuH9qmkpheb0NNWHjYIYliHN6raboovathbzqM4TkJory-uN7vRKOlOtWE9a2GtuNvlOW5bYAL4eJJKt-kceymmOLCckfPr3Lb81IFGIxRxqDg9hGVLWNtrOdFYDQj5UnNnF7srSlRpjUeE=w400-h234" width="400" /></a></div><div class="separator" style="clear: both; text-align: center;"><a href="https://arxiv.org/abs/2205.15868">CogVideo: Large-scale Pretraining for Text-to-Video Generation via Transformers</a></div><br /><div style="text-align: left;">이 기술은 동영상의 각 프레임을 이미지 토큰화하고, 고정 길이 훈련 시퀀스로 만든다. 프레임 사이는 보간되도록 모델을 처리한다. 고품질 텍스트-비디오 학습 데이터를 수집하는 것은 매우 많은 비용이 소모되므로, 사전 훈련된 이미지 생성 모델을 사용한다. 트랜스포머 어텐션은 듀얼 채널로 확장되었다. </div><div style="text-align: left;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEiYSDSlAznDC-aZ-kU_RdssryZvSz18n8JuarRyCXdp5tm5vRrI6-kVyceDzvZl3a4LduVHwsMrvj3uu5bbEgxDwqcVQadQ-LimI8YaKByMPl5PMqpbHhtxhJOTe7aKOZMfltqVBEE1ms4wLHnHy4TUkzuXgsT20wuFAvUJkVvewsirlrb0w4MzwLKISeyF" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="289" data-original-width="280" height="320" src="https://blogger.googleusercontent.com/img/a/AVvXsEiYSDSlAznDC-aZ-kU_RdssryZvSz18n8JuarRyCXdp5tm5vRrI6-kVyceDzvZl3a4LduVHwsMrvj3uu5bbEgxDwqcVQadQ-LimI8YaKByMPl5PMqpbHhtxhJOTe7aKOZMfltqVBEE1ms4wLHnHy4TUkzuXgsT20wuFAvUJkVvewsirlrb0w4MzwLKISeyF=w310-h320" width="310" /></a></div><br />이 기술 또한 오픈소스로 공개되어 있다.</div><div style="text-align: left;"><ul style="text-align: left;"><li>CogVideo, <a href="https://github.com/THUDM/CogVideo">https://github.com/THUDM/CogVideo</a></li></ul></div><div style="text-align: left;">이외에 다음과 같은 생성 AI 모델이 공개되어 있다.</div><div style="text-align: left;"><ul style="text-align: left;"><li><a href="https://text2video-zero.github.io/">Text2Video-Zero</a>: 스테이블 디퓨전 모델을 이용해, 저비용으로 모델 학습 방법을 적용함. <a href="https://github.com/Picsart-AI-Research/Text2Video-Zero">깃허브 코드 공개</a></li></ul><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgs_TWSFYJ2WdoOssmt1eDOSBF6cft0AjXwawXNYomrKsVVrLdsg3kOtIRbn9Ezkw2ctWyHq9rsnoZ3iw-I1uHTaSkc-qBTtNSAoQLZ25QbZgezx0qaxPnzYexbd1hGl1sg7iNbZ7WSCynMX_OaqM3Vl7G7ryrz3bgTUK_izt7fl6zrx3KC2o9qO4WnpKNE" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="2420" data-original-width="8462" height="184" src="https://blogger.googleusercontent.com/img/a/AVvXsEgs_TWSFYJ2WdoOssmt1eDOSBF6cft0AjXwawXNYomrKsVVrLdsg3kOtIRbn9Ezkw2ctWyHq9rsnoZ3iw-I1uHTaSkc-qBTtNSAoQLZ25QbZgezx0qaxPnzYexbd1hGl1sg7iNbZ7WSCynMX_OaqM3Vl7G7ryrz3bgTUK_izt7fl6zrx3KC2o9qO4WnpKNE=w640-h184" width="640" /></a></div></div><ul style="text-align: left;"><li><a href="https://arxiv.org/abs/2009.02018">TiVGAN</a> 및 <a href="https://github.com/sergeytulyakov/mocogan">MoCoGAN</a>(<a href="https://github.com/sergeytulyakov/mocogan?tab=readme-ov-file">code</a>): GAN 모델 기반 영상 생성 방법. 초기 기술 연구 방향을 확인해 볼 수 있음</li></ul></div><div style="text-align: left;"><br /></div><div style="text-align: left;"><b>텍스트-to-비디오 생성AI 프로그램 개발해 보기</b></div><div style="text-align: left;">이와 관련된 서비스 개발 시 쉽게 사용할 수 있는 모델 위주로 라이브러리를 간단히 사용해본다. 이 글에서는 허깅페이스에서 개발한 트랜스포머의 디퓨전 라이브러리를 사용한다. </div><div style="text-align: left;"><br /></div><div style="text-align: left;">터미널에서 다음을 실행한다.</div><div style="text-align: left;"><div>pip install transformers diffusion accelerate</div><div><br /></div></div><div style="text-align: left;">새로운 파이썬 파일 text-video.py를 만들고, 다음 코드를 입력한다.</div><div style="text-align: left;"><div>from transformers import AutoTokenizer, AutoModelForCLIP</div><div>from diffusers.pipelines import TextToVideoZeroSDXLPipeline</div><div>from torchvision import video</div></div><div style="text-align: left;">from transformers import AutoTokenizer, AutoModelForCLIP</div><div style="text-align: left;"><div>from diffusers.pipelines import TextToVideoZeroSDXLPipeline</div><div>from torchvision import video</div><div><br /></div><div>text_prompt = "A group of friends enjoy a picnic in a park on a sunny day."</div><div><br /></div><div>text_encoder = AutoModelForCLIP.from_pretrained("laion/CLIP-ViT-bigG-14-laion2B-39B-b160k")</div><div>pipeline = TextToVideoZeroSDXLPipeline(text_encoder=text_encoder)</div><div><br /></div><div>video_path = pipeline.generate_video(text=text_prompt, resolution="1920x1080")</div><div>video = video.VideoReader(video_path)</div><div>video.write_videofile("generated_video.mp4")</div><div><br /></div><div>실행하면, 다음과 같이 입력된 텍스트에 맞는 영상이 생성되는 것을 확인할 수 있다.</div></div><div style="text-align: left;"><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEi3Y0DuaIOmDq6OhGPXdbkxZkOreHBO3-5YmaoA4IiwujViwZZ1zkbqAIap5fs9y5O3_uzcTcJn-aO4qu_m5-W1HJ-zU0vXEshdd1cCFQ478hxduUgPJe8C2txcv_Wuqs90ggMOm95Zh-QdnhSw6t372JxhxnMSzgb6I2TTcLuAvuLsVFwY2lN6m9agrS8M" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="444" data-original-width="497" height="240" src="https://blogger.googleusercontent.com/img/a/AVvXsEi3Y0DuaIOmDq6OhGPXdbkxZkOreHBO3-5YmaoA4IiwujViwZZ1zkbqAIap5fs9y5O3_uzcTcJn-aO4qu_m5-W1HJ-zU0vXEshdd1cCFQ478hxduUgPJe8C2txcv_Wuqs90ggMOm95Zh-QdnhSw6t372JxhxnMSzgb6I2TTcLuAvuLsVFwY2lN6m9agrS8M" width="269" /></a></div></div><br /></div></div></div></div><div style="text-align: left;"><b>레퍼런스</b></div><div style="text-align: left;"><ul style="text-align: left;"><li><a href="https://github.com/ChenHsing/Awesome-Video-Diffusion-Models">ChenHsing/Awesome-Video-Diffusion-Models: [Arxiv] A Survey on Video Diffusion Models</a></li><li><a href="https://abdulkaderhelwan.medium.com/generating-videos-from-text-with-text2video-zero-b119e2b6c388">Text to Video. Generating Videos From Text with Text2Video-Zero</a></li><li><a href="https://arxiv.org/abs/2401.12945">Omer Bar-Tal et al, 2024.2, Lumiere: A Space-Time Diffusion Model for Video Generation</a></li><li><a href="https://github.com/Stability-AI/generative-models">Stability-AI/generative-models: Generative Models by Stability AI </a></li><li><a href="https://arxiv.org/abs/2205.15868" style="text-align: center;">CogVideo: Large-scale Pretraining for Text-to-Video Generation via Transformers</a></li><li><a href="https://github.com/THUDM/CogVideo">THUDM/CogVideo: Text-to-video generation. The repo for ICLR2023 paper "CogVideo: Large-scale Pretraining for Text-to-Video Generation via Transformers" </a></li><li><a href="https://sites.research.google/videopoet/">VideoPoet – Google Research</a></li><li><a href="https://humanaigc.github.io/animate-anyone/">Animate Anyone</a></li><li><a href="https://arxiv.org/abs/2301.04655">ChatGPT is not all you need. A State of the Art Review of large Generative AI models</a></li><li><a href="https://arxiv.org/abs/2311.05556">LCM-LoRA: A Universal Stable-Diffusion Acceleration Module</a></li><li><a href="https://github.com/Vchitect/Latte">Vchitect/Latte: Latte: Latent Diffusion Transformer for Video Generation</a></li></ul></div>Daddy Makerhttp://www.blogger.com/profile/02275758703365576780noreply@blogger.com0tag:blogger.com,1999:blog-5201956450461596914.post-13381335962286148542024-02-18T17:21:00.000-08:002024-02-24T01:58:12.379-08:00파이토치로 멀티모달 생성AI 모델, Stable Diffusion 아키텍처를 코딩, 구현해보기<div style="text-align: left;">이 글은 Text-To-Image 생성AI 기술을 대중화시킨 Stable Diffusion 아키텍처를 파이토치로 직접 개발해본다. 이를 통해, 앞서 분석한 스테이블 디퓨전 아키텍처를 어떻게 동작하는 코드로 맵핑했는 지를 확인한다. <br /><br /></div><div style="text-align: left;"><div class="separator" style="clear: both;"><div class="separator" style="clear: both;">스테이블 디퓨전 모델은 기존 자연어 처리 분야에서 개발된 트랜스포머, 컴퓨터 비전 딥러닝 모델 기술을 적극 사용한다. 이와 관련된 내용을 깊게 이해하고 싶다면 다음을 참고한다. </div><div class="separator" style="clear: both;"><ul><li><a href="https://daddynkidsmakers.blogspot.com/2017/02/blog-post_24.html" style="color: #992211; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; text-decoration-line: none;"><span>머신러닝 딥러닝 신경망 개념, 종류 및 개발</span></a></li><li><a href="https://github.com/mac999/computer_vision_deeplearning/tree/main" style="color: #992211; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; text-decoration-line: none;"><span>Computer vision deep learning: computer vision based on deep learning lecture materials, github</span></a></li><li><a href="https://daddynkidsmakers.blogspot.com/2022/11/3.html" style="color: #992211; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; text-decoration-line: none;"><span>딥러닝 기반 3차원 스캔 데이터, 포인트 클라우드 학습</span></a></li></ul><div>이 글은 스테이블 디퓨전의 핵심 모델인 트랜스포머, CLIP 등의 이해를 필수로 한다. 이는 다음 글을 참고한다. </div><div><ul><li><a href="https://daddynkidsmakers.blogspot.com/2021/10/blog-post.html" style="font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif;"><span>어텐션 기반 트랜스포머 딥러닝 모델 이해, 활용 사례 및 파치토치를 통한 간단한 사용방법 소개</span></a></li><li><a href="https://daddynkidsmakers.blogspot.com/2024/02/clip.html"><span>생성AI 멀티모달 모델 개발의 시작. OpenAI의 CLIP모델 이해, 코드 분석, 개발, 사용하기</span></a></li><li><a href="https://daddynkidsmakers.blogspot.com/2024/02/blog-post.html"><span>트랜스포머 디코더 핵심 코드 구현을 통한 동작 메커니즘 이해하기</span></a></li><li><a href="https://daddynkidsmakers.blogspot.com/2024/02/llama-2.html"><span>오픈소스 기반 LLM의 민주화, LLAMA-2 논문 분석 및 기술 요약</span></a></li></ul></div><div>이 글은 멀티모달, 디퓨전과 관련된 다양한 문헌을 참고해 정리된 것이다. 관련 내용은 이 글의 레퍼런스를 참고한다. 참고로, 이 글의 소스 파일은 github에 공개된 Binxu의 코드(<a href="https://github.com/Animadversio">참고</a>. Kempner Institute, <a href="https://www.harvard.edu/kempner-institute/leadership/#researchers">Harvard University</a>)와 Fareed Khan 코드(<a href="https://levelup.gitconnected.com/building-stable-diffusion-from-scratch-using-python-f3ebc8c42da3">2024</a>)를 참고한 것이다. </div></div></div><div class="separator" style="clear: both; text-align: center;"><div style="text-align: left;"><br /></div><div style="text-align: left;">이 글 소스 파일은 다음 github 링크를 통해 다운로드 가능하다. 참고로, 이 코드는 GPU 드라이버가 설치되어 있는 컴퓨터, PyTorch가 설치된 conda 개발 환경에서 실행될 수 있다. </div><div style="text-align: left;"><ul style="text-align: left;"><li><a href="https://github.com/mac999/mini_stable_diffusion.git">https://github.com/mac999/mini_stable_diffusion</a></li></ul></div><div style="text-align: left;"><div class="separator" style="clear: both;"><div class="separator" style="clear: both;"><div>스테이블 디퓨전 아키텍처 모델의 깊은 이해가 아닌, 프로그램 설치 사용 만을 원한다면 아래 링크를 참고한다.</div><ul><li><a href="https://daddynkidsmakers.blogspot.com/2023/03/generative-ai.html" style="color: #992211; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; text-decoration-line: none;"><span>생성(Generative) AI 오픈소스 딥러닝 모델 Stable Diffusion, ControlNet 개념 및 ComfyUI 사용법</span></a></li><li><a href="https://daddynkidsmakers.blogspot.com/2023/09/llama2.html" style="font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif;"><span>ChatGPT와 같은 생성AI 서비스 개발을 위한 간단한 LLAMA-2 설치와 사용법</span></a></li></ul></div></div><div class="separator" style="clear: both; text-align: center;"></div></div><div style="text-align: left;"><b>스테이블 디퓨전 아키텍처 구성요소</b></div><div style="text-align: left;"><div class="separator" style="clear: both;">앞의 글에서 설명했듯이, 스테이블 디퓨전은 다음 그림과 같이 디퓨전 모델, U-Net, 오토인코더(Autoencoder), 트랜스포머(Transformer) 어텐션 모델(Attention)을 사용해, 학습한 모델을 역으로 계산해 주어진 텍스트 조건에서 이미지가 생성될 수 있도록 한다.</div><div class="separator" style="clear: both;"><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both;"><div class="separator" style="clear: both;"><div class="separator" style="clear: both;"><div class="separator" style="clear: both;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh-lfoffneRCvrRr7BWz_qEs5U8OuRcndSuOYGBtWmMl7Bp-s7lttHu9dAUJOKVPaXTan6tx8NzEH7cVXbxIjheKwYLIgjhifkPzBfBoUOAyGH1juw5ToCpq63kKOA6IyKzaXM5OneizwmqrruA7wf2cacW4h-0vXSAANpT6DHqJ2JqoxMy7koySA2AHgxH/s781/t9.PNG" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="256" data-original-width="781" height="184" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh-lfoffneRCvrRr7BWz_qEs5U8OuRcndSuOYGBtWmMl7Bp-s7lttHu9dAUJOKVPaXTan6tx8NzEH7cVXbxIjheKwYLIgjhifkPzBfBoUOAyGH1juw5ToCpq63kKOA6IyKzaXM5OneizwmqrruA7wf2cacW4h-0vXSAANpT6DHqJ2JqoxMy7koySA2AHgxH/w563-h184/t9.PNG" width="563" /></a></div></div></div></div></div><div class="separator" style="clear: both; text-align: center;">스테이블 디퓨전 아키텍처 기반 Text To Image 생성(Inference) 및 학습 과정</div><div><br /></div><div>이 글에서 구현할 아키텍처 구성요소를 나열해 본다. </div></div></div></div></div><div style="text-align: left;"><div class="separator" style="clear: both; text-align: center;"><div style="text-align: left;"><div class="separator" style="clear: both;"><div style="text-align: left;"><ul style="text-align: left;"><li>VAE 오토인코더: 입출력 데이터를 잠재공간차원에 맵핑. 계산 성능을 높임</li><li>순방향 확산(forward diffusion): 입력 이미지에서 점진적으로 노이즈 이미지로 계산. 학습용 데이터로 사용.</li><li>역방향 확산(Reverse diffusion): 노이즈에서 이미지를 생성</li><li>U-Net: 노이즈 예측 학습용</li><li>컨디셔닝: 텍스트에 따른 조건부 이미지 생성용. CLIP모델 같은 트랜스포머 어텐션 사용</li></ul></div></div></div></div></div><p>앞의 관련글에서 설명하였듯이, 스테이블 디퓨전 데이터 학습은 A100 x 50에서도 최소 몇 일은 걸리는 작업이다. 참고로, 독일 뮌헨 대학에서 학습한 규모의 이미지 데이터량을 학습하려면, 개인이 하기에는 비싼 비용과 시간이 필요하므로, 이 글에서는 MNIST와 같은 소형 데이터셋을 대상으로 학습하여, 학습되는 파라메터 수를 GPU 2GB 내에서 계산될 수 있도록 한다. </p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjNMjYIu79CIRhxOcbAYEhR75qyeEwvhBxmEI-0FnjbpsPekwVSQuaiNc_QcVgoicPIh-YDghgJUSipS5wlKtJQKQyQAN8VVWZLK-4ncrSSIPf8mP8Ve6bEcVxpHlvUkwT6_OB13Cq1Yw0Qd35cOXooDu-JVjMlhLalI8kuTmFZmNESbJx_Okmc6yIa5EiS" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="378" data-original-width="836" height="181" src="https://blogger.googleusercontent.com/img/a/AVvXsEjNMjYIu79CIRhxOcbAYEhR75qyeEwvhBxmEI-0FnjbpsPekwVSQuaiNc_QcVgoicPIh-YDghgJUSipS5wlKtJQKQyQAN8VVWZLK-4ncrSSIPf8mP8Ve6bEcVxpHlvUkwT6_OB13Cq1Yw0Qd35cOXooDu-JVjMlhLalI8kuTmFZmNESbJx_Okmc6yIa5EiS=w400-h181" width="400" /></a></div><div class="separator" style="clear: both; text-align: center;">이 글에서 테스트된 스테이블 디퓨전 학습 시 필요한 GPU RAM 표시 </div><p></p><p>아키텍처와 관련된 상세한 설명은 다음 링크를 참고한다. </p><p></p><ul style="text-align: left;"><li><a href="https://daddynkidsmakers.blogspot.com/2024/02/stable-diffusion.html">대중화된 멀티모달 생성AI 모델, Stable Diffusion 아키텍처 분석과 동작 원리 이해</a> </li></ul><p></p><div style="text-align: left;"><div class="separator" style="clear: both; text-align: center;"><div style="text-align: left;"><div class="separator" style="clear: both;"><div><b>학습 데이터 준비</b></div><div>MNIST 데이터를 준비한다. 이를 위해, torchvision 라이브러리를 사용해 데이터를 다운로드하고, 확인해 본다. </div><div><br /></div><div><div>import torch, torchvision, matplotlib.pyplot as plt</div><div>from torchvision import transforms</div><div><br /></div><div>transform = transforms.Compose([transforms.ToTensor(), transforms.Normalize((0.5,), (0.5,))])</div><div>train_dataset = torchvision.datasets.MNIST(root='./data', train=True, transform=transform, download=True)</div><div>train_loader = torch.utils.data.DataLoader(train_dataset, batch_size=64, shuffle=True)</div><div><br /></div><div>unique_images, unique_labels = next(iter(train_loader))</div><div>unique_images = unique_images.numpy()</div><div><br /></div><div>row, column = 4, 16</div><div>fig, axes = plt.subplots(row, column, figsize=(16, 4), sharex=True, sharey=True) </div><div><br /></div><div>for i in range(row): </div><div> for j in range(column): </div><div> index = i * column + j </div><div> axes[i, j].imshow(unique_images[index].squeeze(), cmap='gray') </div><div>plt.show()</div></div><div><br /></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEiLDODTx44d-oim-zZ7aofFPGpMfPnlWHeMzFhFqR4PMO19zhJIsMBsk3u8FZUYqZqfMj1slgBoRHTYzzoOAzYotc9kPACNFSi9cQbJxTVjT7QA5r9KxnizE6ykfm3bj-eZj2HK8VWNvOUjBZ-q7OzW7OJj1MtJTsJKyh0MbkB_P_m04xdemgsL6nhezzle" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="441" data-original-width="1620" height="109" src="https://blogger.googleusercontent.com/img/a/AVvXsEiLDODTx44d-oim-zZ7aofFPGpMfPnlWHeMzFhFqR4PMO19zhJIsMBsk3u8FZUYqZqfMj1slgBoRHTYzzoOAzYotc9kPACNFSi9cQbJxTVjT7QA5r9KxnizE6ykfm3bj-eZj2HK8VWNvOUjBZ-q7OzW7OJj1MtJTsJKyh0MbkB_P_m04xdemgsL6nhezzle=w400-h109" width="400" /></a></div></div></div></div><div style="text-align: left;"><br /></div></div></div><div style="text-align: left;"><div class="separator" style="clear: both; text-align: center;"><div style="text-align: left;"><div class="separator" style="clear: both; text-align: center;"><div style="text-align: left;"><div><b>디퓨전 모델 학습 방법 설계<br /></b>스테이블 디퓨전의 학습 목표는 역확산을 통해 잡음을 제거하여 원하는 데이터를 생성하는 방법을 배우는 것이다. 그러므로, 잡음 제거를 목표로 하여 샘플의 잡음을 제거하도록 신경망을 훈련한다. 참고로, 여기서는 앞서 설명한 전확산, 역확산 과정을 입력 데이터 형식과 학습 성능을 고려해, 수정된 확산 모델을 사용하기로 한다. </div><div><br /></div><div>이를 위해, 다음 노이즈 제거 목적 방정식을 정의한다.</div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEiHC0KfjWTc5Eu7bEgdMb_CfOUs3Vtm90BhbiOYX8cqjDHyYcKxReNavW2cfcmI6is93GDGi7IFooAjOlAULqygTv4ioK4T1Uk55c0C_GP8z4axCG2KLpfG1XHPRFMbx-Wst1iuaBJVnWTDorAABcYlXIWLlfcJXiESOIHat43f06eRBxhhN3H1wK5mYfIJ" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="58" data-original-width="836" height="37" src="https://blogger.googleusercontent.com/img/a/AVvXsEiHC0KfjWTc5Eu7bEgdMb_CfOUs3Vtm90BhbiOYX8cqjDHyYcKxReNavW2cfcmI6is93GDGi7IFooAjOlAULqygTv4ioK4T1Uk55c0C_GP8z4axCG2KLpfG1XHPRFMbx-Wst1iuaBJVnWTDorAABcYlXIWLlfcJXiESOIHat43f06eRBxhhN3H1wK5mYfIJ=w517-h37" width="517" /></a></div>여기서, p0(x0)는 목표의 분포(예. 고양이 이미지), x(noised)는 순방향 확산 후 목표 분포 x0의 샘플을 의미한다. 즉, [x(노이즈) - x0)는 정규 분포 확률 변수가 된다. </div><div><br /></div><div>이를 구현 가능한 방식으로 표현하면 다음과 같다. </div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhJ9BvorgxClTlP5yfVk5lB8VflAkaOQF0PBnW5uFRk7vnu4_9xdJ_If9DbF3pxHokCrYzh_mZPkIRsbT3Ap9TCOKvMmVO2OXyHePLITEtQVO64lGcjIm3_r7Df-a5Q4iAkCOvz8O2lOXKCPGbWkq8cSYnsH_CGb9LRMxms1SUM6U7O3u4aYvcdoQeiaK77" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="59" data-original-width="833" height="39" src="https://blogger.googleusercontent.com/img/a/AVvXsEhJ9BvorgxClTlP5yfVk5lB8VflAkaOQF0PBnW5uFRk7vnu4_9xdJ_If9DbF3pxHokCrYzh_mZPkIRsbT3Ap9TCOKvMmVO2OXyHePLITEtQVO64lGcjIm3_r7Df-a5Q4iAkCOvz8O2lOXKCPGbWkq8cSYnsH_CGb9LRMxms1SUM6U7O3u4aYvcdoQeiaK77=w538-h39" width="538" /></a></div><div class="separator" style="clear: both; text-align: center;">노이즈 제거 목적 방정식</div><div><br /></div><div>이기서, J는 노이즈 제거 목표, E는 기대치, t는 시간 매개변수, x0는 목표 분포 p0(x0)의 샘플, x(noise)는 한 단계 순확산 후 목표 분포 샘플 x0, s()는 score 함수, σ(t)는 시간 함수, ε는 정규확률분포변수이다. </div><br />학습 목표는 확산 과정의 모든 시간 t와 원래 분포(예. 고양이, 가족 이미지 등)의 모든 x0에 대한 샘플에 추가되는 노이즈 양을 효과적으로 예측하는 것이다. </div><div><br /></div><div>score 함수를 손실함수로 사용한다는 의미는 무작위 노이즈를 의미 있는 데이터 형태로 변환하는 과정을 학습한다는 뜻이 된다. 이를 위해, 신경망을 이용해 score 함수를 근사화한다. </div><div><br /></div><div>확산 모델 score 함수를 고려한 손실함수 구현은 순서 상 이 글 마지막에 다룬다.</div><div><br /></div><div><b>시간 임베딩</b></div><div>score 함수는 시간에 따라 정확히 신경망이 반응하도록 구현해야 한다. 이를 위해, 시간 임베딩을 사용해 학습시킨다. </div><div><br /></div><div>시간 임베딩은 트랜스포머의 위치 임베딩과 유사한 정현파를 사용해 시간 특징을 계산할 수 있다. 다양한 시간 표현을 학습 시 입력함으로써 시간 변화에 따라 확산 과정을 학습시키도록 한다. 이를 통해, 시간종속적인 s(x, t)를 손실함수의 일부로 학습시킨다. 이를 위해, 다음 두 개 클래스를 만든다. </div><div><br /></div><div>1. 가우스 랜덤 기능 모듈 </div><div>이 모듈은 학습 컨텐스트에서 시간 단계를 표현하는 데 사용된다. 이를 이용하면, 각 시간단계 전반에 걸쳐 임의의 주파수가 생성된다. 이를 위해, 시간단계 별 sine, cosine 투영하여 시간 패턴 특징을 계산할 수 있다. 이 결과는 신경망 학습에 입력된다. </div><div><br /></div><div><div>class GaussianFourierProjection(nn.Module): # 시간 특징 계산 클래스</div><div> def __init__(self, embed_dim, scale=30.): # 임베딩 차원, 랜덤 가중치(주파수)를 위한 스케일 변수</div><div> super().__init__()</div><div><br /></div><div> self.W = nn.Parameter(torch.randn(embed_dim // 2) * scale, requires_grad=False) # 랜덤 샘플링. 훈련 파라메터 아님.</div><div><br /></div><div> def forward(self, x): # 매 시간단위 텐서 입력</div><div> x_proj = x[:, None] * self.W[None, :] * 2 * np.pi # Cosine(2 pi freq x), Sine(2 pi freq x)</div><div> return torch.cat([torch.sin(x_proj), torch.cos(x_proj)], dim=-1) # 최종 sine, cosine 결과 벡터 결합</div></div><div><br /></div><div>2. 특징 텐서 계산</div><div>입력을 4D 특징 텐서로 출력하는 모듈이다. 이 차원 재구성 작업은 다음 컨볼루션 계층 처리에 적합한 맵으로 변환하기 위함이다. </div></div></div></div></div></div><div style="text-align: left;">class Dense(nn.Module): # 특징 계산 클래스<br /> def __init__(self, input_dim, output_dim): # 입력 특징 차원, 출력 특징 차원<br /> super().__init__()<br /> self.dense = nn.Linear(input_dim, output_dim) # 선형 완전연결 레이어<br /> def forward(self, x): # 입력 텐서<br /> return self.dense(x)[..., None, None] # 학습 후 4D텐서</div><div style="text-align: left;"><br /></div><div style="text-align: left;"><div class="separator" style="clear: both; text-align: center;"><div style="text-align: left;"><div class="separator" style="clear: both; text-align: center;"><div style="text-align: left;"><div><b>U-Net 구현</b></div><div>이미지를 다룰 때, 이미지 특징을 캡쳐하기 위해, U-Net 네트워크를 사용한다. U-Net 은 다른 공간 스케일에 대한 이미지 특징을 학습하는 것에 초점을 둔다. </div><div><br /></div><div>U-Net는 시간에 따라 어떻게 데이터가 변경되는 지를 학습해야 한다. 이 모델은 이 패턴을 학습한다. U-Net의 인코딩 경로는 이미지 해상도 다운샘플링, 특징 캡쳐를 위한 컨볼루션 레이어인 h1, h2, h3, h4로 구성된다. 디코딩 경로는 transpose 컨볼루션 레이어이며, 텐서 h가 h4에서 h1 레이어를 통과하여, 업샘플링된다. </div><div><br /></div><div><div><div><span style="font-size: x-small;">class UNet(nn.Module): # U-Net 모델 정의. nn.Module 클래스 상속</span></div><div><span style="font-size: x-small;"> def __init__(self, marginal_prob_std, channels=[32, 64, 128, 256], embed_dim=256): # marginal_prob_std: 시간 t에 대한 표준편차 반환 함수, channels: 각 해상도의 특징 맵의 채널 수, embed_dim: 가우시안 랜덤 특징 임베딩의 차원</span></div><div><span style="font-size: x-small;"> super().__init__()</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;"> # 시간에 대한 가우시안 랜덤 특징 임베딩 계층</span></div><div><span style="font-size: x-small;"> self.time_embed = nn.Sequential(</span></div><div><span style="font-size: x-small;"> GaussianFourierProjection(embed_dim=embed_dim),</span></div><div><span style="font-size: x-small;"> nn.Linear(embed_dim, embed_dim)</span></div><div><span style="font-size: x-small;"> )</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;"> # 인코딩 레이어 (해상도 감소)</span></div><div><span style="font-size: x-small;"> self.conv1 = nn.Conv2d(1, channels[0], 3, stride=1, bias=False)</span></div><div><span style="font-size: x-small;"> self.dense1 = Dense(embed_dim, channels[0])</span></div><div><span style="font-size: x-small;"> self.gnorm1 = nn.GroupNorm(4, num_channels=channels[0])</span></div><div><span style="font-size: x-small;"> self.conv2 = nn.Conv2d(channels[0], channels[1], 3, stride=2, bias=False)</span></div><div><span style="font-size: x-small;"> self.dense2 = Dense(embed_dim, channels[1])</span></div><div><span style="font-size: x-small;"> self.gnorm2 = nn.GroupNorm(32, num_channels=channels[1])</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;"> # 추가 인코딩 레이어 (원본 코드에서 복사)</span></div><div><span style="font-size: x-small;"> self.conv3 = nn.Conv2d(channels[1], channels[2], 3, stride=2, bias=False)</span></div><div><span style="font-size: x-small;"> self.dense3 = Dense(embed_dim, channels[2])</span></div><div><span style="font-size: x-small;"> self.gnorm3 = nn.GroupNorm(32, num_channels=channels[2])</span></div><div><span style="font-size: x-small;"> self.conv4 = nn.Conv2d(channels[2], channels[3], 3, stride=2, bias=False)</span></div><div><span style="font-size: x-small;"> self.dense4 = Dense(embed_dim, channels[3])</span></div><div><span style="font-size: x-small;"> self.gnorm4 = nn.GroupNorm(32, num_channels=channels[3])</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;"> # 해상도 증가를 위한 디코딩 레이어 </span></div><div><span style="font-size: x-small;"> self.tconv4 = nn.ConvTranspose2d(channels[3], channels[2], 3, stride=2, bias=False)</span></div><div><span style="font-size: x-small;"> self.dense5 = Dense(embed_dim, channels[2])</span></div><div><span style="font-size: x-small;"> self.tgnorm4 = nn.GroupNorm(32, num_channels=channels[2])</span></div><div><span style="font-size: x-small;"> self.tconv3 = nn.ConvTranspose2d(channels[2], channels[1], 3, stride=2, bias=False, output_padding=1)</span></div><div><span style="font-size: x-small;"> self.dense6 = Dense(embed_dim, channels[1])</span></div><div><span style="font-size: x-small;"> self.tgnorm3 = nn.GroupNorm(32, num_channels=channels[1])</span></div><div><span style="font-size: x-small;"> self.tconv2 = nn.ConvTranspose2d(channels[1], channels[0], 3, stride=2, bias=False, output_padding=1)</span></div><div><span style="font-size: x-small;"> self.dense7 = Dense(embed_dim, channels[0])</span></div><div><span style="font-size: x-small;"> self.tgnorm2 = nn.GroupNorm(32, num_channels=channels[0])</span></div><div><span style="font-size: x-small;"> self.tconv1 = nn.ConvTranspose2d(channels[0], 1, 3, stride=1)</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;"> # 스위치 활성화 함수</span></div><div><span style="font-size: x-small;"> self.act = lambda x: x * torch.sigmoid(x)</span></div><div><span style="font-size: x-small;"> self.marginal_prob_std = marginal_prob_std</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;"> def forward(self, x, t, y=None): # U-Net 아키텍처를 통과한 출력 텐서 반환. x: 입력 텐서, t: 시간 텐서, y: 타겟 텐서 (이 전방 통과에서 사용되지 않음). h: U-Net 아키텍처를 통과한 출력 텐서</span></div><div><span style="font-size: x-small;"> # t에 대한 가우시안 랜덤 특징 임베딩 획득</span></div><div><span style="font-size: x-small;"> embed = self.act(self.time_embed(t))</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;"> # 인코딩 경로</span></div><div><span style="font-size: x-small;"> h1 = self.conv1(x) + self.dense1(embed)</span></div><div><span style="font-size: x-small;"> h1 = self.act(self.gnorm1(h1))</span></div><div><span style="font-size: x-small;"> h2 = self.conv2(h1) + self.dense2(embed)</span></div><div><span style="font-size: x-small;"> h2 = self.act(self.gnorm2(h2))</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;"> # 추가 인코딩 경로 레이어 (원본 코드에서 복사)</span></div><div><span style="font-size: x-small;"> h3 = self.conv3(h2) + self.dense3(embed)</span></div><div><span style="font-size: x-small;"> h3 = self.act(self.gnorm3(h3))</span></div><div><span style="font-size: x-small;"> h4 = self.conv4(h3) + self.dense4(embed)</span></div><div><span style="font-size: x-small;"> h4 = self.act(self.gnorm4(h4))</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;"> # 디코딩 경로</span></div><div><span style="font-size: x-small;"> h = self.tconv4(h4)</span></div><div><span style="font-size: x-small;"> h += self.dense5(embed)</span></div><div><span style="font-size: x-small;"> h = self.act(self.tgnorm4(h))</span></div><div><span style="font-size: x-small;"> h = self.tconv3(h + h3)</span></div><div><span style="font-size: x-small;"> h += self.dense6(embed)</span></div><div><span style="font-size: x-small;"> h = self.act(self.tgnorm3(h))</span></div><div><span style="font-size: x-small;"> h = self.tconv2(h + h2)</span></div><div><span style="font-size: x-small;"> h += self.dense7(embed)</span></div><div><span style="font-size: x-small;"> h = self.act(self.tgnorm2(h))</span></div><div><span style="font-size: x-small;"> h = self.tconv1(h + h1)</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;"> # 정규화 출력</span></div><div><span style="font-size: x-small;"> h = h / self.marginal_prob_std(t)[:, None, None, None]</span></div><div><span style="font-size: x-small;"> return h</span></div><div><br /></div></div></div><div><b>포워드 디퓨전 프로세스</b></div><div>전방향 확산 모델을 효과적으로 계산하기 위해, 다음 방정식을 고려한다. </div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEisB0Use3PiyDsX2Z7V1_felAdp3m1ydqEDLNPxAJ9B8IHNI7Rqe0PsPrIsJWaZR8d3Ksf_grELECx6jJhh601PrWvR_45SkPQt7hXMlpC4l_LmTlVbRIJy1Qe-6RUJnT_bxM09uCw7wg83bfTbug_B96fiaGbLba9J9rjBRnEpn-NxHSzqJHuewDioloeH" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="117" data-original-width="557" height="24" src="https://blogger.googleusercontent.com/img/a/AVvXsEisB0Use3PiyDsX2Z7V1_felAdp3m1ydqEDLNPxAJ9B8IHNI7Rqe0PsPrIsJWaZR8d3Ksf_grELECx6jJhh601PrWvR_45SkPQt7hXMlpC4l_LmTlVbRIJy1Qe-6RUJnT_bxM09uCw7wg83bfTbug_B96fiaGbLba9J9rjBRnEpn-NxHSzqJHuewDioloeH=w117-h24" width="117" /></a></div><div><br /></div>이 방정식은 변수 x에 대한 변화가 시간 t에 대한 노이즈가 dw에 에 비례하는 방식으로 동작한다. 노이즈 수준은 파라메터 σ에 결정되며, 지수적으로 증가한다. </div><div><br /></div><div>이 프로세스는 처음 x(0) 초기값이 주어지고, x(t)에 대한 분석적인 솔류션을 찾을 수 있다. <br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEh7BC8j6CHLPzO4qCi3BXLfF7BarQjwxuyQ-H7qlVen3XmWWRxlne4-YxYlc81dTTVQIhpS4m5W7pyroeTrsXRFese-3MbANS0BUajFMTJseP8HDrgPfYiYg2-rC4GjFeAOBx2-EQNYkMn9ewvYRmiduDtHHKks0GB-c5D5z5ODQZpUnGcFfuhFu1pYxymU" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="84" data-original-width="779" height="27" src="https://blogger.googleusercontent.com/img/a/AVvXsEh7BC8j6CHLPzO4qCi3BXLfF7BarQjwxuyQ-H7qlVen3XmWWRxlne4-YxYlc81dTTVQIhpS4m5W7pyroeTrsXRFese-3MbANS0BUajFMTJseP8HDrgPfYiYg2-rC4GjFeAOBx2-EQNYkMn9ewvYRmiduDtHHKks0GB-c5D5z5ODQZpUnGcFfuhFu1pYxymU=w240-h27" width="240" /></a></div><br />이 모델에서, σ(t)는 표준 편차(marginal standard deviation)로 사용되며, x(t)는 분산의 변동으로 사용된다. σ(t)은 다음과 같이 계산된다. </div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhWhvY_UeOfQlBx-ACtuffj7wWhc7ko0cli-8nw9CxBzvHdja-0xIOCXtf6ogPu4DRIwZER6xfkBn_sTD4GBztMZT0ZKWulzqYdT7hXQXCqpWHkoMlv7hjERryYDBZiL2tcc8Ps-GaI2B34GHsakCgAbqFXNS9u378l-WUE2rs-IbUqjKI3-52WBcsd3Fch" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="120" data-original-width="788" height="43" src="https://blogger.googleusercontent.com/img/a/AVvXsEhWhvY_UeOfQlBx-ACtuffj7wWhc7ko0cli-8nw9CxBzvHdja-0xIOCXtf6ogPu4DRIwZER6xfkBn_sTD4GBztMZT0ZKWulzqYdT7hXQXCqpWHkoMlv7hjERryYDBZiL2tcc8Ps-GaI2B34GHsakCgAbqFXNS9u378l-WUE2rs-IbUqjKI3-52WBcsd3Fch=w280-h43" width="280" /></a></div><br />이 모델은 시간에 따라 어떻게 잡음 수준 σ가 정해지는 지 이해를 제공한다. 다음은 이를 코딩한 것이다. </div><div><div>device = "cuda"</div><div><br /></div><div>def marginal_prob_std(t, sigma): # 시간 t에 대한 표준편차 반환 함수</div><div> t = torch.tensor(t, device=device) # 시간 텐서 </div><div> return torch.sqrt((sigma**(2 * t) - 1.) / 2. / np.log(sigma)) # 시간 t에 대한 표준편차 반환</div><div><br /></div><div>def diffusion_coeff(t, sigma): # 확산 계수 함수. t: 시간 텐서, sigma: SDE의 시그마</div><div> return torch.tensor(sigma**t, device=device) # 확산 계수 반환</div><div><br /></div><div>sigma = 25.0</div><div>marginal_prob_std_fn = functools.partial(marginal_prob_std, sigma=sigma)</div><div>diffusion_coeff_fn = functools.partial(diffusion_coeff, sigma=sigma)</div></div><div><br /></div><div><b>손실 함수 구현</b></div><div>U-Net 구현에 앞서, 언급되었던 score 함수를 학습할 수 있도록, loss 함수를 구현한다.</div><div><br /></div><div>이 함수는 임의의 시간단위를 샘플링하고, 여기서 잡음 수준을 얻은 후, 이 잡음을 데이터와 더한다. 그리고, 실제 데이터와 에측 데이터간의 오차를 계산하여, 에러를 줄이를 방향으로 학습하도록 한다.</div><div><br /></div><div><div>def loss_fn(model, x, marginal_prob_std, eps=1e-5): # 손실함수. score-based generative models 훈련용. model: 시간 의존 스코어 모델, x: 훈련 데이터 미니배치, marginal_prob_std: 표준편차 반환 함수, eps: 수치 안정성을 위한 허용값</div><div> random_t = torch.rand(x.shape[0], device=x.device) * (1. - 2 * eps) + eps # 미니배치 크기만큼 랜덤 시간 샘플링</div><div><br /></div><div> std = marginal_prob_std(random_t) # 랜덤 시간에 대한 표준편차 계산</div><div> z = torch.randn_like(x) # 미니배치 크기만큼 정규 분포 랜덤 노이즈 생성 </div><div> perturbed_x = x + z * std[:, None, None, None] # 노이즈로 입력 데이터 왜곡</div><div><br /></div><div> score = model(perturbed_x, random_t) # 왜곡 데이터, 시간 입력, 계산된 스코어 획득 </div><div> loss = torch.mean(torch.sum((score * std[:, None, None, None] + z)**2, dim=(1, 2, 3))) # score 함수와 잡음에 기반한 손실값 계산</div><div> </div><div> return loss</div></div><div><br /></div></div></div></div><div style="text-align: left;"><div><b>샘플러 코딩</b></div><div>Stable Diffusion는 임의 시점에서 이미지를 생성한다. 노이즈 예측기(noise predictor)는 얼마나 이미지에 노이즈를 줄 것인지를 예측한다. 이 예측된 노이즈는 이미지로 부터 제거된다. 전체 사이클은 몇 번 반복되어 깨끗한 이미지를 생성하게 된다. </div><div><br /></div><div>이 clearning-up 프로세스를 '샘플링'으로 알려져 있고, 매 학습 단계마다 새로운 이미지를 생성되도록 한다. 이를 샘플러, 샘플링 방법이라 한다.</div><div><br /></div><div>스테이블 디퓨전은 이미지 샘플링을 생성하기 위한 다양한 방법이 있다. 여기서는 Euler-Maruyama 방법론을 사용한다.</div><div><br /></div><div><div># number of steps</div><div>num_steps = 500</div><div><br /></div><div>def Euler_Maruyama_sampler(score_model,</div><div> marginal_prob_std,</div><div> diffusion_coeff,</div><div> batch_size=64,</div><div> x_shape=(1, 28, 28),</div><div> num_steps=num_steps,</div><div> device='cuda',</div><div> eps=1e-3, y=None): # Euler-Maruyama sampler 함수. score-based 모델을 사용해 샘플 생성. score_model: 시간 의존 스코어 모델, marginal_prob_std: 표준편차 반환 함수, diffusion_coeff: 확산 계수 함수, batch_size: 한번 호출시 생성할 샘플러 수, x_shape: 샘플 형태, num_steps: 샘플링 단계 수, device: 'cuda' 또는 'cpu', eps: 수치 안정성을 위한 허용값, y: 타겟 텐서 (이 함수에서 사용되지 않음) </div><div><br /></div><div> t = torch.ones(batch_size, device=device)</div><div> init_x = torch.randn(batch_size, *x_shape, device=device) * marginal_prob_std(t)[:, None, None, None] # Initial sample</div><div> </div><div> time_steps = torch.linspace(1., eps, num_steps, device=device)</div><div> step_size = time_steps[0] - time_steps[1] # Step size 시리즈</div><div> x = init_x # 시간 t에 대한 초기 샘플</div><div> </div><div> with torch.no_grad(): # Euler-Maruyama 샘플링</div><div> for time_step in tqdm(time_steps):</div><div> batch_time_step = torch.ones(batch_size, device=device) * time_step</div><div> g = diffusion_coeff(batch_time_step)</div><div> mean_x = x + (g**2)[:, None, None, None] * score_model(x, batch_time_step, y=y) * step_size</div><div> x = mean_x + torch.sqrt(step_size) * g[:, None, None, None] * torch.randn_like(x)</div><div> </div><div> return mean_x</div></div><div><br /></div><div><b>U-Net 학습 및 결과 시각화</b></div></div><div style="text-align: left;">앞서 구현된 U-Net 모델을 학습한다. 학습은 50 에폭, 2024 미니배치크기로 진행하며, 데이터셋은 MNIST를 사용한다. 학습된 모델은 chpt.pth 로 저장된다. </div><div style="text-align: left;"><div><br /></div><div><div>batch_size = 2048</div><div>transform = transforms.Compose([</div><div> transforms.ToTensor() # Convert image to PyTorch tensor</div><div> # transforms.Normalize((0.5,), (0.5,)) # Normalize images with mean and std</div><div>])</div><div>dataset = torchvision.datasets.MNIST('.', train=True, transform=transform, download=True)</div><div>data_loader = torch.utils.data.DataLoader(dataset, batch_size=batch_size, shuffle=True)</div><div><br /></div><div>n_epochs = 50</div><div>lr = 5e-4</div></div><div><br /></div><div>optimizer = Adam(score_model.parameters(), lr=lr)</div><div><br /></div><div>tqdm_epoch = trange(n_epochs)</div><div>for epoch in tqdm_epoch:</div><div> avg_loss = 0.</div><div> num_items = 0</div><div> for x, y in tqdm(data_loader):</div><div> x = x.to(device)</div><div> loss = loss_fn(score_model, x, marginal_prob_std_fn)</div><div> optimizer.zero_grad()</div><div> loss.backward()</div><div> optimizer.step()</div><div> avg_loss += loss.item() * x.shape[0]</div><div> num_items += x.shape[0]</div><div> tqdm_epoch.set_description('Average Loss: {:5f}'.format(avg_loss / num_items))</div><div> torch.save(score_model.state_dict(), 'ckpt.pth')</div><div><br /></div><div>학습된 결과를 다음과 같이 시각화해본다. 샘플러는 앞서 정의한 Euler_Maruyama_sampler이며, 이 함수는 앞서 U-Net으로 학습된 score_function을 호출해 그 결과를 리턴한다(샘플링 결과). 결과를 보면 알겠지만, 노이즈 입력에 대한 필기체가 출력된 것을 확인할 수 있다. </div><div><div>sample_batch_size = 64</div><div>num_steps = 500</div><div><br /></div><div>sampler = Euler_Maruyama_sampler # Euler-Maruyama sampler 사용</div><div>samples = sampler(score_model, marginal_prob_std_fn,</div><div> diffusion_coeff_fn, sample_batch_size,</div><div> num_steps=num_steps, device=device, y=None)</div><div>samples = samples.clamp(0.0, 1.0)</div><div><br /></div><div>import matplotlib.pyplot as plt</div><div>sample_grid = make_grid(samples, nrow=int(np.sqrt(sample_batch_size)))</div><div>plt.figure(figsize=(6, 6))</div><div>plt.axis('off')</div><div>plt.imshow(sample_grid.permute(1, 2, 0).cpu(), vmin=0., vmax=1.)</div><div>plt.show() </div></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEiXoYQO4ur1Z2WV6uG-gt5Ls89qFL-YnM3_TiewTTVe06jmMIJvsDGqBAFh9LV2pB6mlibxvWrrwTJ9FD894jtfgjlQQyOLvczjzR9phQzRhdNe07QYZslUSgD0Qxp0gPkPVSaV_LYciXBe-WEB7yZUt5Q2j8FsmYqfrdvY69nwvVC0iW-cZ1A8HOxRjCZ4" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="489" data-original-width="507" height="240" src="https://blogger.googleusercontent.com/img/a/AVvXsEiXoYQO4ur1Z2WV6uG-gt5Ls89qFL-YnM3_TiewTTVe06jmMIJvsDGqBAFh9LV2pB6mlibxvWrrwTJ9FD894jtfgjlQQyOLvczjzR9phQzRhdNe07QYZslUSgD0Qxp0gPkPVSaV_LYciXBe-WEB7yZUt5Q2j8FsmYqfrdvY69nwvVC0iW-cZ1A8HOxRjCZ4" width="249" /></a></div><br /></div><div>이제 텍스트(여기서는 숫자)를 입력하면, 이미지가 생성되도록 어텐션 레이어를 추가해 본다.</div></div><div style="text-align: left;"><br /></div><div style="text-align: left;"><div><div class="separator" style="clear: both; text-align: center;"><div style="text-align: left;"><b>멀티모달 처리를 위한 어텐션 레이어와 트랜스포머 모듈 개발</b></div></div></div><div>트랜스포머 아키텍처의 어텐션 레이어는 입력에 대한 cross attention, spatial transformer (공간 트랜스포머)를 구현해야 한다.</div><div><br /></div><div>어텐션 모델은 QKV(Query, Key, Value) 벡터로 학습된 컨텍스트 KV에 대한 질의 Q에 대한 스코어를 계산한다. 이런 특징은 멀티모달 생성AI를 가능하게 하는 핵심이 된다(예. KV는 이미지 학습, Q는 음성 입력). 이 벡터 QKV는 다음과 같이 차원 공간에서 기저벡터 e로 표현될 수 있다. </div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjunhIPX4fMNfYpESULnEO01nnbysB5B0KouAM2ODB3sRnFYnq_xT0UEkqGfeXeno1bL_HCgUE2aWPTWtm92B_ub7xpxN_DYaRTGh-Df5sysbKYOk52LVDpfQQiRMg6zYDIK454P4ffRGFYHXeks8vxTUDBEGws6y2Lyjk4spRB0EZSWR-1rCM7fcNMx0Rk" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="72" data-original-width="817" height="28" src="https://blogger.googleusercontent.com/img/a/AVvXsEjunhIPX4fMNfYpESULnEO01nnbysB5B0KouAM2ODB3sRnFYnq_xT0UEkqGfeXeno1bL_HCgUE2aWPTWtm92B_ub7xpxN_DYaRTGh-Df5sysbKYOk52LVDpfQQiRMg6zYDIK454P4ffRGFYHXeks8vxTUDBEGws6y2Lyjk4spRB0EZSWR-1rCM7fcNMx0Rk" width="320" /></a></div><br /><div>핵심은 Q와 KV의 유사도 계산한 상호 벡터간 거리가 가까워지는 방향으로 학습 모델을 설계하는 것에 있다(상세한 설명은 글 앞의 트랜스포머 설명 링크를 참고). 여기서는 Q가 MNIST의 각 숫자 번호가 될 것이다. 참고로, 셀프 어텐션은 입력 토큰 내 관계를 학습하며, 교차 어텐션의 경우 입력 토큰과 컨텍스트 특징 간의 관계를 학습한다. 이 계산 결과로 컨텍스트 벡터를 리턴한다. </div><div></div></div><div><br /></div><div>어텐션은 torch.einsum 함수를 사용해(참고 - <a href="https://pytorch.org/docs/stable/generated/torch.einsum.html">Einstein summation</a> 함수), 다음과 같이 코딩될 수 있다.</div></div><div style="text-align: left;"><div>class CrossAttention(nn.Module):</div><div> def __init__(self, embed_dim, hidden_dim, context_dim=None, num_heads=1): # 임베딩 차원, 은닉 차원, 컨텍스트 차원(self attention이면 None), 어텐션 헤드 수</div><div> super(CrossAttention, self).__init__()</div><div><br /></div><div> self.hidden_dim = hidden_dim</div><div> self.context_dim = context_dim</div><div> self.embed_dim = embed_dim</div><div><br /></div><div> self.query = nn.Linear(hidden_dim, embed_dim, bias=False) # Query에 대한 학습을 위한 선형 레이어</div><div> </div><div> if context_dim is None:</div><div> self.self_attn = True</div><div> self.key = nn.Linear(hidden_dim, embed_dim, bias=False)</div><div> self.value = nn.Linear(hidden_dim, hidden_dim, bias=False)</div><div> else:</div><div> self.self_attn = False</div><div> self.key = nn.Linear(context_dim, embed_dim, bias=False)</div><div> self.value = nn.Linear(context_dim, hidden_dim, bias=False)</div><div><br /></div><div> def forward(self, tokens, context=None): # 토큰들[배치, 시퀀스 크기, 은닉 차원], 컨텍스트 정보[배치, 컨텍스트 시퀀스 크기, 컨텍스트 차원]. self_attn이 True면 컨텍스트는 무시됨</div><div> if self.self_attn: # Self-attention case</div><div> Q = self.query(tokens)</div><div> K = self.key(tokens)</div><div> V = self.value(tokens)</div><div> else: # Cross-attention case</div><div> Q = self.query(tokens)</div><div> K = self.key(context)</div><div> V = self.value(context)</div><div><br /></div><div> # Compute score matrices, attention matrices, and context vectors</div><div> scoremats = torch.einsum("BTH,BSH->BTS", Q, K) # Q, K간 내적 계산. 스코어 행렬 획득</div><div> attnmats = F.softmax(scoremats / math.sqrt(self.embed_dim), dim=-1) # 스코어 행렬의 softmax 계산</div><div> ctx_vecs = torch.einsum("BTS,BSH->BTH", attnmats, V) # 어텐션 행렬 적용된 V벡터 계산 </div><div> return ctx_vecs</div></div><div style="text-align: left;"><br /></div><div style="text-align: left;">앞의 어텐션 레이어를 포함한 트랜스포머 모듈을 개발한다. </div><div style="text-align: left;"><br /></div><div style="text-align: left;">이 트랜스포머 모듈은 교차 어텐션, 피드포워드 신경망을 통합한다. 차원 형태는 [batch, sequence_len, hidden_dim] 입력 텐서, [batch, context_seq_len, context_dim]인 컨텍스트 텐서를 사용한다. 셀프-어텐션 및 크로스 어텐션 모듈 처리 다음에 레이어 정규화, 잔차 연결이 계산된다. 비선형 변환을 위한 GELU, MLP 레이어가 포함된다. </div><div style="text-align: left;"><div><br /></div><div>class TransformerBlock(nn.Module):</div><div> def __init__(self, hidden_dim, context_dim): # 은닉 차원, 컨텍스트 차원</div><div> super(TransformerBlock, self).__init__()</div><div><br /></div><div> self.attn_self = CrossAttention(hidden_dim, hidden_dim)</div><div> self.attn_cross = CrossAttention(hidden_dim, hidden_dim, context_dim)</div><div><br /></div><div> self.norm1 = nn.LayerNorm(hidden_dim)</div><div> self.norm2 = nn.LayerNorm(hidden_dim)</div><div> self.norm3 = nn.LayerNorm(hidden_dim)</div><div><br /></div><div> self.ffn = nn.Sequential(</div><div> nn.Linear(hidden_dim, 3 * hidden_dim),</div><div> nn.GELU(),</div><div> nn.Linear(3 * hidden_dim, hidden_dim)</div><div> ) # Feed forward neural network. 2개의 레이어로 구성. 첫번째 레이어는 3 * hidden_dim개의 은닉 유닛을 가지고 nn.GELU 비선형성 함수를 사용. 두번째 레이어는 hidden_dim개의 은닉 유닛을 가짐</div><div><br /></div><div> def forward(self, x, context=None): # x: 입력 텐서[배치, 시퀀스 크기, 은닉 차원], context: 컨텍스트 텐서[배치, 컨텍스트 시퀀스 크기, 컨텍스트 차원]</div><div> x = self.attn_self(self.norm1(x)) + x # self-attention 적용 후 layer normalization과 잔차 연결 적용</div><div> x = self.attn_cross(self.norm2(x), context=context) + x # cross-attention 적용 후 layer normalization과 잔차 연결 적용</div><div> x = self.ffn(self.norm3(x)) + x # feed forward neural network 적용 후 layer normalization과 잔차 연결 적용</div><div><br /></div><div> return x</div><div><br /></div></div><div style="text-align: left;">이제, 공간 트랜스포머 모듈로 앞의 모듈을 통합한다. </div><div style="text-align: left;"><br /></div><div style="text-align: left;">class SpatialTransformer(nn.Module):</div><div style="text-align: left;"><div> def __init__(self, hidden_dim, context_dim):</div><div> super(SpatialTransformer, self).__init__()</div><div> </div><div> self.transformer = TransformerBlock(hidden_dim, context_dim)</div><div><br /></div><div> def forward(self, x, context=None): # x: 입력 텐서[배치, 채널, 높이, 너비], context: 컨텍스트 텐서[배치, 컨텍스트 시퀀스 크기, 컨텍스트 차원]</div><div> b, c, h, w = x.shape</div><div> x_in = x</div><div><br /></div><div> x = rearrange(x, "b c h w -> b (h w) c") # 입력 텐서의 차원을 재배열</div><div> x = self.transformer(x, context) # 트랜스포머 블록 적용</div><div> x = rearrange(x, 'b (h w) c -> b c h w', h=h, w=w) # 텐서의 차원을 원래대로 복원</div><div><br /></div><div> return x + x_in # 공간 변환기의 출력과 입력의 잔차 연결</div></div><div style="text-align: left;"><br /></div><div style="text-align: left;"><b>U-Net과 공간 트랜스포머 통합</b></div><div style="text-align: left;">앞서 설명된 U-Net과 공간 트랜스포머를 통합해, 특정 조건에 따라(예. 입력 텍스트, 숫자 등), 이미지가 생성되도록 U-Net 학습 모델을 일부 수정한다. 적색으로 표시된 부분이 공간 트랜스포머로 파라메터(예. 텍스트, 숫자 등) 컨디셔닝한 부분이다. </div><div style="text-align: left;"><br /></div><div style="text-align: left;"><div><span style="font-size: x-small;">class UNet_Tranformer(nn.Module): # 시간 의존된 스코어 기반 U-NET 모델</span></div><div><span style="font-size: x-small;"> def __init__(self, marginal_prob_std, channels=[32, 64, 128, 256], embed_dim=256,</span></div><div><span style="font-size: x-small;"> text_dim=256, nClass=10): # marginal_prob_std: 시간 t에 대한 표준편차 반환 함수, channels: 각 해상도의 특징 맵의 채널 수, embed_dim: 가우시안 랜덤 특징 임베딩의 차원, text_dim: 텍스트/숫자의 임베딩 차원, nClass: 모델링할 클래스 수</span></div><div><span style="font-size: x-small;"> super().__init__()</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;"> # 시간에 대한 가우시안 랜덤 특징 임베딩 계층</span></div><div><span style="font-size: x-small;"> self.time_embed = nn.Sequential(</span></div><div><span style="font-size: x-small;"> GaussianFourierProjection(embed_dim=embed_dim),</span></div><div><span style="font-size: x-small;"> nn.Linear(embed_dim, embed_dim)</span></div><div><span style="font-size: x-small;"> )</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;"> # 인코딩 레이어 (해상도 감소)</span></div><div><span style="font-size: x-small;"> self.conv1 = nn.Conv2d(1, channels[0], 3, stride=1, bias=False)</span></div><div><span style="font-size: x-small;"> self.dense1 = Dense(embed_dim, channels[0])</span></div><div><span style="font-size: x-small;"> self.gnorm1 = nn.GroupNorm(4, num_channels=channels[0])</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;"> self.conv2 = nn.Conv2d(channels[0], channels[1], 3, stride=2, bias=False)</span></div><div><span style="font-size: x-small;"> self.dense2 = Dense(embed_dim, channels[1])</span></div><div><span style="font-size: x-small;"> self.gnorm2 = nn.GroupNorm(32, num_channels=channels[1])</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;"> self.conv3 = nn.Conv2d(channels[1], channels[2], 3, stride=2, bias=False)</span></div><div><span style="font-size: x-small;"> self.dense3 = Dense(embed_dim, channels[2])</span></div><div><span style="font-size: x-small;"> self.gnorm3 = nn.GroupNorm(32, num_channels=channels[2])</span></div><div><span style="color: red; font-size: x-small;"> self.attn3 = SpatialTransformer(channels[2], text_dim) # 컨텍스트 정보, 텍스트 임베딩 차원을 공간 트랜스포머에 설정</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;"> self.conv4 = nn.Conv2d(channels[2], channels[3], 3, stride=2, bias=False)</span></div><div><span style="font-size: x-small;"> self.dense4 = Dense(embed_dim, channels[3])</span></div><div><span style="font-size: x-small;"> self.gnorm4 = nn.GroupNorm(32, num_channels=channels[3])</span></div><div><span style="color: red; font-size: x-small;"> self.attn4 = SpatialTransformer(channels[3], text_dim)</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;"> # 디코딩 레이어. 해상도 증가</span></div><div><span style="font-size: x-small;"> self.tconv4 = nn.ConvTranspose2d(channels[3], channels[2], 3, stride=2, bias=False)</span></div><div><span style="font-size: x-small;"> self.dense5 = Dense(embed_dim, channels[2])</span></div><div><span style="font-size: x-small;"> self.tgnorm4 = nn.GroupNorm(32, num_channels=channels[2])</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;"> self.tconv3 = nn.ConvTranspose2d(channels[2], channels[1], 3, stride=2, bias=False, output_padding=1)</span></div><div><span style="font-size: x-small;"> self.dense6 = Dense(embed_dim, channels[1])</span></div><div><span style="font-size: x-small;"> self.tgnorm3 = nn.GroupNorm(32, num_channels=channels[1])</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;"> self.tconv2 = nn.ConvTranspose2d(channels[1], channels[0], 3, stride=2, bias=False, output_padding=1)</span></div><div><span style="font-size: x-small;"> self.dense7 = Dense(embed_dim, channels[0])</span></div><div><span style="font-size: x-small;"> self.tgnorm2 = nn.GroupNorm(32, num_channels=channels[0])</span></div><div><span style="font-size: x-small;"> self.tconv1 = nn.ConvTranspose2d(channels[0], 1, 3, stride=1)</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;"> # The swish activation function</span></div><div><span style="font-size: x-small;"> self.act = nn.SiLU()</span></div><div><span style="font-size: x-small;"> self.marginal_prob_std = marginal_prob_std</span></div><div><span style="font-size: x-small;"> self.cond_embed = nn.Embedding(nClass, text_dim)</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;"> def forward(self, x, t, <span style="color: red;">y</span>=None): # U-Net 아키텍처를 통과한 출력 텐서 반환. x: 입력 텐서, t: 시간 텐서, y: 타겟 텐서 (텍스트 토큰. 예. MNIST 번호). h: U-Net 아키텍처를 통과한 출력 텐서</span></div><div><span style="font-size: x-small;"> embed = self.act(self.time_embed(t))</span></div><div><span style="font-size: x-small;"> y_embed = self.cond_embed(y).unsqueeze(1)</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;"> # Encoding path</span></div><div><span style="font-size: x-small;"> h1 = self.conv1(x) + self.dense1(embed)</span></div><div><span style="font-size: x-small;"> h1 = self.act(self.gnorm1(h1))</span></div><div><span style="font-size: x-small;"> h2 = self.conv2(h1) + self.dense2(embed)</span></div><div><span style="font-size: x-small;"> h2 = self.act(self.gnorm2(h2))</span></div><div><span style="font-size: x-small;"> h3 = self.conv3(h2) + self.dense3(embed)</span></div><div><span style="font-size: x-small;"> h3 = self.act(self.gnorm3(h3))</span></div><div><span style="color: red; font-size: x-small;"> h3 = self.attn3(h3, y_embed)</span></div><div><span style="font-size: x-small;"> h4 = self.conv4(h3) + self.dense4(embed)</span></div><div><span style="font-size: x-small;"> h4 = self.act(self.gnorm4(h4))</span></div><div><span style="color: red; font-size: x-small;"> h4 = self.attn4(h4, y_embed)</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;"> # Decoding path</span></div><div><span style="font-size: x-small;"> h = self.tconv4(h4) + self.dense5(embed)</span></div><div><span style="font-size: x-small;"> h = self.act(self.tgnorm4(h))</span></div><div><span style="font-size: x-small;"> h = self.tconv3(h + h3) + self.dense6(embed)</span></div><div><span style="font-size: x-small;"> h = self.act(self.tgnorm3(h))</span></div><div><span style="font-size: x-small;"> h = self.tconv2(h + h2) + self.dense7(embed)</span></div><div><span style="font-size: x-small;"> h = self.act(self.tgnorm2(h))</span></div><div><span style="font-size: x-small;"> h = self.tconv1(h + h1)</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;"> # Normalize output</span></div><div><span style="font-size: x-small;"> h = h / self.marginal_prob_std(t)[:, None, None, None]</span></div><div><span style="font-size: x-small;"> return h</span></div><div> </div></div><div style="text-align: left;">스테이블 디퓨전 손실 함수 수정 및 최종 학습</div><div style="text-align: left;">이제 손실 함수도 이에 따라 수정한다. </div><div style="text-align: left;"><div><span style="font-size: x-small;">def loss_fn_cond(model, x, y, marginal_prob_std, eps=1e-5): # model: 시간 의존된 스코어 기반 모델, x: 입력 데이터 미니배치, y: 조건 정보(타겟 텐서. 예. 입력 텍스트, 숫자), marginal_prob_std: 표준편차 반환 함수, eps: 수치 안정성을 위한 허용값</span></div><div><span style="font-size: x-small;"> random_t = torch.rand(x.shape[0], device=x.device) * (1. - eps) + eps # 미니배치 크기만큼 랜덤 시간 샘플링</span></div><div><span style="font-size: x-small;"> z = torch.randn_like(x) # 미니배치 크기만큼 정규 분포 랜덤 노이즈 생성</span></div><div><span style="font-size: x-small;"> std = marginal_prob_std(random_t) # 랜덤 시간에 대한 표준편차 계산</span></div><div><span style="font-size: x-small;"> perturbed_x = x + z * std[:, None, None, None] # 노이즈로 입력 데이터 왜곡</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;"> score = model(perturbed_x, random_t, <span style="color: red;">y=y</span>) # 모델을 사용해 왜곡된 데이터와 시간에 대한 스코어 획득. 트랜스포머 어텐션에 Q로 입력되는 Y벡터 추가됨.</span></div><div><span style="font-size: x-small;"> loss = torch.mean(torch.sum((score * std[:, None, None, None] + z)**2, dim=(1, 2, 3))) # score 함수와 잡음에 기반한 손실 계산</span></div><div><span style="font-size: x-small;"> return loss</span></div></div><div style="text-align: left;"><br /></div><div style="text-align: left;">그리고, 앞서 동일한 방식으로, 다음과 같이 학습한다. 앞의 손실 함수와 다른점은 y(MNIST 필기체 숫자. 적색표시)가 파라메터화된 조건으로 트랜스포머 어텐션 모델에 입력되었다는 것이다.</div><div style="text-align: left;"><div><span style="font-size: x-small;">score_model = torch.nn.DataParallel(UNet_Tranformer(marginal_prob_std=marginal_prob_std_fn))</span></div><div><span style="font-size: x-small;">score_model = score_model.to(device)</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;">n_epochs = 100 </span></div><div><span style="font-size: x-small;">batch_size = 1024 </span></div><div><span style="font-size: x-small;">lr = 10e-4 </span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;">transform = transforms.Compose([</span></div><div><span style="font-size: x-small;"> transforms.ToTensor() # Convert image to PyTorch tensor</span></div><div><span style="font-size: x-small;">])</span></div><div><span style="font-size: x-small;">dataset = torchvision.datasets.MNIST('.', train=True, transform=transform, download=True)</span></div><div><span style="font-size: x-small;">data_loader = torch.utils.data.DataLoader(dataset, batch_size=batch_size, shuffle=True)</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;">optimizer = Adam(score_model.parameters(), lr=lr)</span></div><div><span style="font-size: x-small;">scheduler = LambdaLR(optimizer, lr_lambda=lambda epoch: max(0.2, 0.98 ** epoch))</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;">tqdm_epoch = trange(n_epochs)</span></div><div><span style="font-size: x-small;">for epoch in tqdm_epoch:</span></div><div><span style="font-size: x-small;"> avg_loss = 0.</span></div><div><span style="font-size: x-small;"> num_items = 0</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;"> for x, y in tqdm(data_loader):</span></div><div><span style="font-size: x-small;"> x = x.to(device)</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="color: red; font-size: x-small;"> loss = loss_fn_cond(score_model, x, y, marginal_prob_std_fn)</span></div><div><span style="font-size: x-small;"> optimizer.zero_grad()</span></div><div><span style="font-size: x-small;"> loss.backward()</span></div><div><span style="font-size: x-small;"> optimizer.step()</span></div><div><span style="font-size: x-small;"> avg_loss += loss.item() * x.shape[0]</span></div><div><span style="font-size: x-small;"> num_items += x.shape[0]</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;"> scheduler.step()</span></div><div><span style="font-size: x-small;"> lr_current = scheduler.get_last_lr()[0]</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;"> print('{} Average Loss: {:5f} lr {:.1e}'.format(epoch, avg_loss / num_items, lr_current))</span></div><div><span style="font-size: x-small;"> tqdm_epoch.set_description('Average Loss: {:5f}'.format(avg_loss / num_items))</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;"> torch.save(score_model.state_dict(), 'ckpt_transformer.pth')</span></div><div><br /></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEg5TEuHKnLxeIeXQZt7L3pdGUmdkw_WZIVBntN7IlOXZ1WNstROBqgE4w8h6Ch_UWq3W1On_iFHaijMWGWKVVUci2hCj_o0zvcYhBEK6t_DfcSZYVrdMa0W_pWqpgKQTpBTEX_AHXd2ZqsqZcUgVEze6DHcL3rAvubfJoLPkDNztN36ETpBYfuvotEB1P4F" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="370" data-original-width="1070" height="139" src="https://blogger.googleusercontent.com/img/a/AVvXsEg5TEuHKnLxeIeXQZt7L3pdGUmdkw_WZIVBntN7IlOXZ1WNstROBqgE4w8h6Ch_UWq3W1On_iFHaijMWGWKVVUci2hCj_o0zvcYhBEK6t_DfcSZYVrdMa0W_pWqpgKQTpBTEX_AHXd2ZqsqZcUgVEze6DHcL3rAvubfJoLPkDNztN36ETpBYfuvotEB1P4F=w400-h139" width="400" /></a></div></div><div class="separator" style="clear: both; text-align: center;">스테이블 디퓨전 학습 과정</div><div class="separator" style="clear: both; text-align: center;"><br /></div><div><b>학습된 스테이블 디퓨전 모델 기반 생성AI 테스트</b></div><div>이제 학습된 모델을 이용해, 각 입력조건(예. MNIST 필기체 숫자)에 따라 적절한 이미지를 생성하는 지를 확인한다. </div><div><div><span style="font-size: x-small;">ckpt = torch.load('ckpt_transformer.pth', map_location=device)</span></div><div><span style="font-size: x-small;">score_model.load_state_dict(ckpt)</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;">digit = 9 # 생성AI 입력 조건. 0~9까지 숫자 생성 가능</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;">sample_batch_size = 64 </span></div><div><span style="font-size: x-small;">num_steps = 250</span></div><div><span style="font-size: x-small;">sampler = Euler_Maruyama_sampler</span></div><div><span style="font-size: x-small;"># score_model.eval()</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;">samples = sampler(score_model,</span></div><div><span style="font-size: x-small;"> marginal_prob_std_fn,</span></div><div><span style="font-size: x-small;"> diffusion_coeff_fn,</span></div><div><span style="font-size: x-small;"> sample_batch_size,</span></div><div><span style="font-size: x-small;"> num_steps=num_steps,</span></div><div><span style="font-size: x-small;"> device=device,</span></div><div><span style="font-size: x-small;"> y=digit*torch.ones(sample_batch_size, dtype=torch.long))</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;"># 생성 결과 확인</span></div><div><span style="font-size: x-small;">samples = samples.clamp(0.0, 1.0)</span></div><div><span style="font-size: x-small;">sample_grid = make_grid(samples, nrow=int(np.sqrt(sample_batch_size)))</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;">plt.figure(figsize=(6,6))</span></div><div><span style="font-size: x-small;">plt.axis('off')</span></div><div><span style="font-size: x-small;">plt.imshow(sample_grid.permute(1, 2, 0).cpu(), vmin=0., vmax=1.)</span></div><div><span style="font-size: x-small;">plt.show()</span></div></div><div><br /></div><div>결과와 같이, 제대로 입력 조건에 따라 이미지가 생성되는 것을 확인할 수 있다. 이로써, 스테이블 디퓨전 모델이 어떻게 멀티모달 조건에서 생성할 데이터를 학습하는 지를 확인할 수 있다.</div></div><div style="text-align: left;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjcj-DnwrY0DIlwoiWs_ULvqiD2e-jTy9Xmv28ih8PHLk22atA4w2TPUZZibBbOfvrXcVWle6pqQUULCR_dtfJtxz6_La5MXIlK6GTgSpI8lI55ajyBljoiyjcmH_1u0utz1h_AuLvzWI68RVgrgcaSkeTzX33XGa5qizyHSd7i9lUgrCO65whLNwLjuJKg" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="474" data-original-width="481" height="240" src="https://blogger.googleusercontent.com/img/a/AVvXsEjcj-DnwrY0DIlwoiWs_ULvqiD2e-jTy9Xmv28ih8PHLk22atA4w2TPUZZibBbOfvrXcVWle6pqQUULCR_dtfJtxz6_La5MXIlK6GTgSpI8lI55ajyBljoiyjcmH_1u0utz1h_AuLvzWI68RVgrgcaSkeTzX33XGa5qizyHSd7i9lUgrCO65whLNwLjuJKg" width="244" /></a></div><div class="separator" style="clear: both; text-align: center;">멀티모달 입력에 대한 생성 결과('9' 입력 > 생성 이미지)</div><br /></div><div style="text-align: left;"><b>마무리</b></div></div></div><div style="text-align: left;">이 글에서는 멀티모달리티를 구현하는 생성AI의 아키텍처인 스테이블 디퓨전의 구현 방법을 확인하고, 실행 과정을 확인함으로써, 생성AI의 동작원리를 좀 더 깊게 살펴보았다. </div><div style="text-align: left;"><br /></div><div style="text-align: left;"><div>독일 뮌헨 대학에서 학습한 규모의 이미지 데이터량을 학습하려면, 개인이 하기에는 비싼 비용과 시간이 필요하므로, 이 글에서는 MNIST와 같은 소형 데이터셋을 대상으로 학습하여, 학습되는 파라메터 수를 GPU 2GB 내에서 계산될 수 있도록 하였다. </div><div><br /></div></div><div style="text-align: left;">스테이블 디퓨전은 기존에 개발되었던 오토임베딩, 잠재공간표현, U-Net, ResNet, 파라메터 컨디셔닝, 멀티 모달, CLIP, 디퓨전, 트랜스포머 어텐션 모델이 모두 적용된 멀티모달 Text To Image 모델이다. </div><div style="text-align: left;"><br /></div><div style="text-align: left;">현재 시점에서는 멀티모달에 대한 핵심 컴포넌트가 개발되어 성능이 이미 검증되었기 때문에, 앞으로도 OpenAI SORA와 같은 Text-To-Video와 같은 Multi-Modal Gen AI 모델은 더욱 다양하게 개발될 것이라 예상된다. </div><div class="separator" style="clear: both; text-align: center;"><iframe allowfullscreen="" class="BLOG_video_class" height="266" src="https://www.youtube.com/embed/HK6y8DAPN_0" width="320" youtube-src-id="HK6y8DAPN_0"></iframe></div><div style="text-align: center;">OpenAI Text To Video Gen AI - SORA</div><div style="text-align: left;"><br /></div><div style="text-align: left;"><b>레퍼런스</b><br /><ul><li><a href="https://arxiv.org/abs/2112.10752">Robin Rombach, Andreas Blattmann, Dominik Lorenz, Patrick Esser, Björn Ommer, 2022.4, High-Resolution Image Synthesis with Latent Diffusion Models</a></li><li>MosaicML, 2023, <a href="https://www.databricks.com/blog/stable-diffusion-2">Training Stable Diffusion from Scratch for $50k with MosaicML (Part 2) | Databricks Blog</a></li><li>Umar Jamil, 2023.10, <a href="https://www.youtube.com/watch?v=ZBKpAp_6TGI">Coding Stable Diffusion from scratch in PyTorch (youtube.com)</a> and <a href="https://github.com/hkproj/pytorch-stable-diffusion">pytorch-stable-diffusion: Stable Diffusion implemented from scratch in PyTorch (github.com)</a></li><li><a href="https://levelup.gitconnected.com/building-stable-diffusion-from-scratch-using-python-f3ebc8c42da3">Fareed Khan, 2024</a>, <a href="https://levelup.gitconnected.com/building-stable-diffusion-from-scratch-using-python-f3ebc8c42da3">Coding Stable Diffusion from Scratch</a> </li><li><span style="text-align: center;">Alexej Klushyn, 2019.12, </span><a href="https://argmax.ai/blog/vhp-vae/">Learning Hierarchical Priors in VAEs</a></li><li><a href="https://en.wikipedia.org/wiki/Langevin_equation">Langevin equation, Wikipedia</a></li><li id="cite_note-1" style="animation-delay: -0.01ms; animation-duration: 0.01ms; animation-iteration-count: 1; break-inside: avoid-column; counter-increment: mw-ref-extends-parent 1 mw-references 1; counter-reset: mw-ref-extends-child 0; margin-bottom: 0.1em; scroll-behavior: auto; transition-duration: 0ms;"><span class="reference-text" style="animation-delay: -0.01ms; animation-duration: 0.01ms; animation-iteration-count: 1; scroll-behavior: auto; transition-duration: 0ms;">Langevin, P. (1908). <a href="http://www.fisica.unlp.edu.ar/Members/sanchez/Ej2%20viejo/lemons1997_langevin.pdf">Sur la théorie du mouvement brownien [On the Theory of Brownian Motion]</a>. <i style="animation-delay: -0.01ms; animation-duration: 0.01ms; animation-iteration-count: 1; scroll-behavior: auto; transition-duration: 0ms;">C. R. Acad. Sci. Paris</i>. <b style="animation-delay: -0.01ms; animation-duration: 0.01ms; animation-iteration-count: 1; scroll-behavior: auto; transition-duration: 0ms;">146</b>: 530–533.</span></li><li>M<a href="https://www.miniphysics.com/brownian-motion.html" style="text-align: center;">iniphysics, Brownian Motion - Learn Physics From The Physics Authority</a></li><li>Q<a href="https://www.youtube.com/watch?app=desktop&v=P9qar8mv3Tk">uantpie, 2019, Diffusion Equation - Derivation and Explanation using Brownian</a></li><li>Yang Song et al, 2011.2, <a href="https://arxiv.org/abs/2011.13456">Score-Based Generative Modeling through Stochastic Differential Equations</a></li><li><span style="text-align: center;">Lgnacio Aristimuno, 2023.11,</span><span style="text-align: center;"> </span><a href="https://blog.marvik.ai/2023/11/28/an-introduction-to-diffusion-models-and-stable-diffusion/">An Introduction to Diffusion Models and Stable Diffusion</a></li><li><a href="https://medium.com/@steinsfu/stable-diffusion-clearly-explained-ed008044e07e">Steins</a>, 2023, <a href="https://medium.com/@steinsfu/stable-diffusion-clearly-explained-ed008044e07e">Stable Diffusion Clearly Explained</a></li><li>Jay Alammar, 2022, <a href="https://jalammar.github.io/illustrated-stable-diffusion/">The Illustrated Stable Diffusion</a></li><li>Thomas, 2023.2, <a href="https://wandb.ai/capecape/train_sd/reports/How-To-Train-a-Conditional-Diffusion-Model-From-Scratch--VmlldzoyNzIzNTQ1">How To Train a Conditional Diffusion Model From Scratch | train_sd – Weights & Biases</a></li><li>Hugging face, <a href="https://huggingface.co/docs/diffusers/en/tutorials/basic_training">Train a diffusion model</a></li><li><a href="https://theaisummer.com/diffusion-models/">Sergios Karagiannakos, 2022.9, How diffusion models work: the math from scratch | AI Summer</a></li><li><a href="https://tree.rocks/make-diffusion-model-from-scratch-easy-way-to-implement-quick-diffusion-model-e60d18fd0f2e">Seachaos</a>, 2023, <a href="https://tree.rocks/make-diffusion-model-from-scratch-easy-way-to-implement-quick-diffusion-model-e60d18fd0f2e">Make Diffusion model from scratch</a> and <a href="https://github.com/gmongaras/Diffusion_models_from_scratch">Diffusion_models_from_scratch(</a>github)</li><li>Olaf Ronneberger et al, 2015, <a href="https://arxiv.org/abs/1505.04597">U-Net: Convolutional Networks for Biomedical Image Segmentation (arxiv.org)</a></li><li><a href="https://arxiv.org/pdf/2006.11239.pdf">Jonathan Ho et al</a>, 2020.12, Denoising Diffusion Probabilistic Models</li><li><a href="https://medium.com/@vasco-dev/history-and-literature-on-latent-stable-diffusion-dbca69fd54d5">Vasco Meerman, 2023, History and literature on Latent (Stable) Diffusion</a></li><li><a href="https://huggingface.co/blog/text-to-video">A Dive into Text-to-Video Models (huggingface.co)</a></li><li><a href="https://medium.com/@AIBites/stable-video-diffusion-convert-text-and-images-to-videos-e0f41b7f6986">Shrinivasan Sankar</a>, 2023.12, <a href="https://medium.com/@AIBites/stable-video-diffusion-convert-text-and-images-to-videos-e0f41b7f6986">Stable Video Diffusion — Convert Text and Images to Videos</a></li><li><a href="https://huggingface.co/blog/text-to-video">Adirik Alara Dirik, 2023.5, A Dive into Text-to-Video Models</a></li><li><a href="https://byby.dev/ai-text-to-video-models">Top 7 text-to-video generative AI models</a>, 2023.11</li><li><a href="https://arxiv.org/abs/2302.05543">Lvmin Zhang, Anyi Rao, Maneesh Agrawala, 2023.11, Adding Conditional Control to Text-to-Image Diffusion Models</a></li><li>Ekrem Çetinkaya, 2022.12, <a href="https://www.marktechpost.com/2022/12/07/bytedance-ai-researchers-introduce-magicvideo-an-efficient-text-to-video-generation-framework-based-on-latent-diffusion-models/">ByteDance AI Researchers Introduce 'MagicVideo,' an Efficient Text-to-Video Generation Framework based on Latent Diffusion Models - MarkTechPost</a></li><li><a href="https://openai.com/sora">Sora, Openai.com</a></li><li><a href="https://openai.com/research/video-generation-models-as-world-simulators?fbclid=IwAR3F1oNQZ0GHKf8C6zQiTmvWCJN5QLoVKi9T6RY5jgg9n29nid5ic9DuBkE">Video generation models as world simulators (openai.com)</a></li></ul></div>Daddy Makerhttp://www.blogger.com/profile/02275758703365576780noreply@blogger.com0tag:blogger.com,1999:blog-5201956450461596914.post-57984881976812759702024-02-17T18:20:00.000-08:002024-02-18T04:27:04.488-08:00불필요한 폴더 파일 정리 후 컴퓨터 용량 확보하기요즘 컴퓨터 용량이 점점 줄어들어, 대대적으로 디스크 정리를 하였다(딥러닝, 오픈소스 등 작업할 때 로컬에 데이터 다운로드가 필수라 용량 부족하면 작업이 안된다).<div><br /></div><div>순서는 다음과 같다.</div><div><br /></div><div><div>1. 탐색기에서 디스크 정리한다.</div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEh4aH0-im0BdH-bI4z0McFDkYDltInfwggvjjqDIFnNgFv-fLBC3KMdpdhJtIjJZ6_4crsvlsJT6NTXqRs8VbxpzFmQqRt6W53olWvwNi6yRfHq-h1fRPhejD2kNMbwEC4YsIk2e5DVp_dzabBOrJc37ygJWpZojvLg13h4Ays6I90-o_S9mZlITrfVG1kQ" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="537" data-original-width="527" height="240" src="https://blogger.googleusercontent.com/img/a/AVvXsEh4aH0-im0BdH-bI4z0McFDkYDltInfwggvjjqDIFnNgFv-fLBC3KMdpdhJtIjJZ6_4crsvlsJT6NTXqRs8VbxpzFmQqRt6W53olWvwNi6yRfHq-h1fRPhejD2kNMbwEC4YsIk2e5DVp_dzabBOrJc37ygJWpZojvLg13h4Ays6I90-o_S9mZlITrfVG1kQ" width="236" /></a></div><br /></div><div>2. du 란 disk usage 용량 보여주는 프로그램을 다운로드해, 윈도우즈 폴더에 풀어준다.</div></div><div><ul style="text-align: left;"><li>다운로드: <a href="https://learn.microsoft.com/en-us/sysinternals/downloads/du">Disk Usage - Sysinternals | Microsoft Learn</a></li></ul></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhfz-tSSO_7Y2v7_gyho1YuFXvEQ5Os_NBqb8XGA2tfw6i7JEge81OHFwj0lahFwkmmVWkVYVERvhS2S7_2CmFZpTFyCQU7TGG-IWkZt_CVdNLdZUeSpr5vFUPUgu4EArtJdjPWuXwzpcW22sLTno5CyMpmFSGyHaXCvWYBkptW9t5QR3HgXm7YPp-mNnJB" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="208" data-original-width="374" height="160" src="https://blogger.googleusercontent.com/img/a/AVvXsEhfz-tSSO_7Y2v7_gyho1YuFXvEQ5Os_NBqb8XGA2tfw6i7JEge81OHFwj0lahFwkmmVWkVYVERvhS2S7_2CmFZpTFyCQU7TGG-IWkZt_CVdNLdZUeSpr5vFUPUgu4EArtJdjPWuXwzpcW22sLTno5CyMpmFSGyHaXCvWYBkptW9t5QR3HgXm7YPp-mNnJB=w288-h160" width="288" /></a></div><br /></div><div>3. c:\Users\[user name\AppData\LocalLow\Temp를 삭제한다. </div><div><br /></div><div>4. cmd를 실행해, C:\Users\MAC\AppData\Roaming 로 이동한다. 그리고 다음 명령을 입력한다. 그럼, 현재 하위 폴더의 용량을 요약해 보여준다.</div><div>du -l 1 . </div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgWnUJLngsFVp1TVjiZSdcTHMNeQrrqIWkhyBFAlld2Scd7j3R8TkYovftl5DU7Jh-eNAGWOSzR1hL10EdE3nYAXxJdOU-uijTF1neDTHLOPh5E1ZR0waSwkmBGk6S5_vM8zCAh5P-2RqJEU_dmGz7u4ioQno1tcNEookdFxA-gJ-owhPWl7q0IsAA-Z2Eq" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="545" data-original-width="875" height="295" src="https://blogger.googleusercontent.com/img/a/AVvXsEgWnUJLngsFVp1TVjiZSdcTHMNeQrrqIWkhyBFAlld2Scd7j3R8TkYovftl5DU7Jh-eNAGWOSzR1hL10EdE3nYAXxJdOU-uijTF1neDTHLOPh5E1ZR0waSwkmBGk6S5_vM8zCAh5P-2RqJEU_dmGz7u4ioQno1tcNEookdFxA-gJ-owhPWl7q0IsAA-Z2Eq=w474-h295" width="474" /></a></div><div><br /></div>5. 각 폴더 중 불필요한 폴더들은 삭제한다. 예를 들어, Apple Computer\itunes 폴더는 백업이 있어, 과도한 용량을 차지하므로, 해당 백업을 삭제하여, 30GB를 확보하였다. Temp 폴더 등도 삭제한다.</div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEi_CfP6d05W0pWWK8t7WXLjav_kheyG7BWCp8YLNJyJ6QDSl1XY7W5NVa4s-8-0Tx5YhTA61zaCi5D14Tj4gs6qy6cGJiW9ZQA2oD4tF_0SPOe_Jp8fo1CpeUPjtyFJgVfSreoT57qLDctFlZixL8sgKk8rBAYzEyEShvLgeMINWYXAYG2X1T4V0WEUuAO0" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="256" data-original-width="505" height="162" src="https://blogger.googleusercontent.com/img/a/AVvXsEi_CfP6d05W0pWWK8t7WXLjav_kheyG7BWCp8YLNJyJ6QDSl1XY7W5NVa4s-8-0Tx5YhTA61zaCi5D14Tj4gs6qy6cGJiW9ZQA2oD4tF_0SPOe_Jp8fo1CpeUPjtyFJgVfSreoT57qLDctFlZixL8sgKk8rBAYzEyEShvLgeMINWYXAYG2X1T4V0WEUuAO0" width="320" /></a></div><br />이렇게 하여, 전체 사용 가능 용량을 2배 이상 더 확보할 수 있다.</div><div><br /><br /></div>Daddy Makerhttp://www.blogger.com/profile/02275758703365576780noreply@blogger.com0tag:blogger.com,1999:blog-5201956450461596914.post-24385247569630829632024-02-14T21:08:00.000-08:002024-02-16T21:08:05.225-08:00오픈소스 기반 LLM의 민주화, LLAMA-2 논문 분석 및 기술 요약하기<div style="text-align: left;">이 글은 개발에 많은 노력이 드는 LLM(Large Language Model) 기술을 개발하고 GitHub에 공개한 메타(페이스북)의 LLAMA-2(라마) 논문을 분석하고, 핵심을 요약한다. 이 지식은 LLM 기반 서비스 및 생성AI 개발 시 유용하다. 참고로, META AI 연구진(리더 Yann LeCun 교수)은 LLM 민주화를 위해 라마를 LLM 커뮤니티에 공개하고, 관련 실행 코드를 GITHUB에 업로드하였다. </div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgt2gb5zFz05igoIWVUWH4fXn2OhHuSVr7crJhxUpe-j-PJXx_XEIo4zr8re8SjJ4Q7lfWGsomRPEevbTdbf1K2S7WV-NEru0Kv9HlqjSdHH3-sFVEgR7xPGUeB5Hqkgl7pU4rCPNBiOM8v5PfVR_0xnnJJxr8s80kwoBrfzjQMmXO452nrTPLbrr-F6c62/s972/t8.PNG" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="972" data-original-width="815" height="400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgt2gb5zFz05igoIWVUWH4fXn2OhHuSVr7crJhxUpe-j-PJXx_XEIo4zr8re8SjJ4Q7lfWGsomRPEevbTdbf1K2S7WV-NEru0Kv9HlqjSdHH3-sFVEgR7xPGUeB5Hqkgl7pU4rCPNBiOM8v5PfVR_0xnnJJxr8s80kwoBrfzjQMmXO452nrTPLbrr-F6c62/w335-h400/t8.PNG" width="335" /></a></div><div class="separator" style="clear: both; text-align: center;">LLAMA-2</div><div class="separator" style="clear: both; text-align: center;"><br /></div><div style="text-align: left;">라마-2 설치 및 활용에만 관심이 있다면, 다음 링크를 참고한다.</div><div style="text-align: left;"><ul style="text-align: left;"><li><a href="https://daddynkidsmakers.blogspot.com/2023/09/llama2.html">ChatGPT와 같은 생성AI 서비스 개발을 위한 간단한 LLAMA-2 설치와 사용법</a></li></ul></div><div style="text-align: left;">라마2 기술에 대해 분석하기 전에 라마1 기술을 먼저 정리해 본다.</div><div style="text-align: left;"><br /></div><div style="text-align: left;"><span style="font-size: medium;"><b>LLAMA-1</b></span></div><div style="text-align: left;"><b><br /></b></div><div style="text-align: left;"><b>서론</b></div><div style="text-align: left;">7B에서 65B 매개변수를 가진 LLM 모델인 라마는 수조개의 토큰을 학습하고, 이를 이용해 다양한 AI 에이전트 서비스를 개발할 수 있다. LLAMA-13B는 대부분 벤치마킹에서 GPT-3(175B)보다 성능이 뛰어나다. </div><div style="text-align: left;"><br /></div><div style="text-align: left;">라마는 LLM을 개발할 때 목표를 훈련 속도가 아닌 추론 속도가 가장 빠른 모델을 개발하는 것으로 한다. 예를 들어, LLAMA-13B는 경쟁 모델보다 10배 더 작음에도 불구하고, GPT-3보다 뛰어나다. 이 모델은 단일 GPU에 실행할 수 있다. 이러한 가성비를 통해, LLM 민주화에 도움이 될 것이라 믿는다. 참고로, 라마를 모델을 개발하는 데 5개월이 걸렸으며(아이디어, 문서 작업 등 제외), 한 모델을 학습하는 데 21일이 걸렸다. </div><div style="text-align: left;"><br /></div><div style="text-align: left;"><b>데이터 훈련 접근 방식</b></div><div style="text-align: left;">라마의 데이터 훈련 방식은 이전 LLM연구 논문인 OpenAI, Google, DeepMind 의 LLM 개발과정을 참조해 유사하게 진행되었다. </div><div style="text-align: left;"><ul style="text-align: left;"><li>Brown et al., 2020, <a href="https://arxiv.org/abs/2005.14165">Language models are few-shot learners</a>, OpenAI</li><li>Chowdhery et al., 2022, <a href="https://arxiv.org/abs/2204.02311">Scaling Language Modeling with Pathways</a>, Google</li><li>Hoffmann et al., 2022, <a href="https://arxiv.org/pdf/2203.15556.pdf">Training Compute-Optimal Large Language Models</a>, DeepMind</li></ul></div><div style="text-align: left;">라마의 훈련 데이터는 여러 소스가 혼합되어 있다. 다음 표는 이를 보여준다. 라마는 공개된 데이터만 사용하였다. 낮은 품질의 컨텐츠는 fastText 선형 분류기, n-gram 모델을 이용해 사전 필터링되었다. 학습에 문제가 있는 노이즈 데이터는 사전 필터링된다. </div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjEo7YIdlB3Q0AV2PVD9u8JkeO9K7zu1Ai9COZKzQKv0szWGgK5Lqs81hJCxr1nTrSWNacbOAiJy_4b5938zlCdymL0bzHsJ_WjR97GkhyphenhyphenrJuUp40LF0xrD9Zk-CoV2xSQu-lGkU8qY5GKAe-rfPEQZzBqy9GaAq2rpmcP_J7T_XeUdIIGpyDK07D27N8b2/s440/t6.PNG" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="377" data-original-width="440" height="231" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjEo7YIdlB3Q0AV2PVD9u8JkeO9K7zu1Ai9COZKzQKv0szWGgK5Lqs81hJCxr1nTrSWNacbOAiJy_4b5938zlCdymL0bzHsJ_WjR97GkhyphenhyphenrJuUp40LF0xrD9Zk-CoV2xSQu-lGkU8qY5GKAe-rfPEQZzBqy9GaAq2rpmcP_J7T_XeUdIIGpyDK07D27N8b2/w270-h231/t6.PNG" width="270" /></a></div><div class="separator" style="clear: both; text-align: center;">학습 데이터셋</div><div style="text-align: left;"><br /></div><div style="text-align: left;">다음은 라마에서 사용된 학습 데이터의 예시이다.</div><div style="text-align: left;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEivmbKHVVTugBN-Jmp2Ja4pwbZD8s6-TmcETDFuEA3Cw1vTNAnHtmEMzC3dq4c9FbrhY8YjVcV9D4M769JbhNUaeVLJ8lC7jKipZ-npDU5bXfJlDIjbuxOVwR9l9LXHMHhhL5Vv1IaVAfHMaUzlkgDQpR86KmRcCfrtSnFvZkFi7nHCewFfS7Jh1f2Butmu" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="265" data-original-width="1532" height="84" src="https://blogger.googleusercontent.com/img/a/AVvXsEivmbKHVVTugBN-Jmp2Ja4pwbZD8s6-TmcETDFuEA3Cw1vTNAnHtmEMzC3dq4c9FbrhY8YjVcV9D4M769JbhNUaeVLJ8lC7jKipZ-npDU5bXfJlDIjbuxOVwR9l9LXHMHhhL5Vv1IaVAfHMaUzlkgDQpR86KmRcCfrtSnFvZkFi7nHCewFfS7Jh1f2Butmu=w488-h84" width="488" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjgBuK28m35TNEmXJv8nfP0gLQoE05cVYVRiaUYYjvk2qk-U30NMHUguyXp8En2NmHJyuLE5VpmqCI8irXJglZ9oQXhH_WzaevhnQe521MuDq2bFcO4971o6C_eij2HpkEK3QZpBLMs4Mal_Av9804q4UBekygxrQ5Dxry6EBnKwO6P9je_3F6DzhGP5IUN" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="592" data-original-width="1067" height="178" src="https://blogger.googleusercontent.com/img/a/AVvXsEjgBuK28m35TNEmXJv8nfP0gLQoE05cVYVRiaUYYjvk2qk-U30NMHUguyXp8En2NmHJyuLE5VpmqCI8irXJglZ9oQXhH_WzaevhnQe521MuDq2bFcO4971o6C_eij2HpkEK3QZpBLMs4Mal_Av9804q4UBekygxrQ5Dxry6EBnKwO6P9je_3F6DzhGP5IUN" width="320" /></a></div>문장 생성 학습 데이터셋<br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEh2WSXzfInwOr0Y1_Z5WEIjvoO0DIt5PMv9_yY6MOb2JaQx4cqz-w3x6w3qlCzhF63D8CDhSlcNsJGzA0JDpm2izicTqdkZKSePwcSFRDADnFDpQt8MkXuYJhvnkjxnejiMbmlytNLg7XGPOIX790xc5Uqv7Q1PWJIl21HoLDwxy1Vjs-kS75HNSCFPx295" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="287" data-original-width="1099" height="84" src="https://blogger.googleusercontent.com/img/a/AVvXsEh2WSXzfInwOr0Y1_Z5WEIjvoO0DIt5PMv9_yY6MOb2JaQx4cqz-w3x6w3qlCzhF63D8CDhSlcNsJGzA0JDpm2izicTqdkZKSePwcSFRDADnFDpQt8MkXuYJhvnkjxnejiMbmlytNLg7XGPOIX790xc5Uqv7Q1PWJIl21HoLDwxy1Vjs-kS75HNSCFPx295" width="320" /></a></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEiJnWeSuE3qSlwz9L2zLl1fqwwvOYBTHmC3nz_Y5E9ke0Kzyry1nrDe1u-2IZ87Kmbuu4mNq80GUft3agMyBg0utb2sJX8l7XArCjOUSzhoseSNhBV5TaKF-Acvm_ksvoyRp7m68lVlrGRjKUeEKOWXVXbFVYaOUTzVgFbrfji5kGthRCXIQUzelOSkGoPf" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="451" data-original-width="1099" height="131" src="https://blogger.googleusercontent.com/img/a/AVvXsEiJnWeSuE3qSlwz9L2zLl1fqwwvOYBTHmC3nz_Y5E9ke0Kzyry1nrDe1u-2IZ87Kmbuu4mNq80GUft3agMyBg0utb2sJX8l7XArCjOUSzhoseSNhBV5TaKF-Acvm_ksvoyRp7m68lVlrGRjKUeEKOWXVXbFVYaOUTzVgFbrfji5kGthRCXIQUzelOSkGoPf" width="320" /></a></div>코딩 학습 데이터셋<br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEg3cErhJQE5OLeA7wRm5HRveFfI8I-x9XOzuQ6e6vTveddGmOPYNv2WMXH9bQzM4SXfhFprMY5sbayKK1vx4fObhbOD-I23iv0jiRjpU2Xf4v2996tVoqqI_tbj8hsVIMMJjZnWCU-KD29rcB_f5_ZNtY9oceVTsigpdqVJL0RhgdXYyraSE-oy1HxCde-3" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="364" data-original-width="1018" height="114" src="https://blogger.googleusercontent.com/img/a/AVvXsEg3cErhJQE5OLeA7wRm5HRveFfI8I-x9XOzuQ6e6vTveddGmOPYNv2WMXH9bQzM4SXfhFprMY5sbayKK1vx4fObhbOD-I23iv0jiRjpU2Xf4v2996tVoqqI_tbj8hsVIMMJjZnWCU-KD29rcB_f5_ZNtY9oceVTsigpdqVJL0RhgdXYyraSE-oy1HxCde-3" width="320" /></a></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEiHKPbinGPjT7wjgrJSfmQhMw4QnefeijVGufmHucmrwLOQIC7-boeh_rlbaDCGr39IEl1-7X9fyfqLPxP3VZMzdKZpapsSziM7xk6PQyXPXPRgLOcL914gAAoc-X3eEV5ZMe2d_cTvhwGr9z7sn2hil4g4chuIL00L8bD8NFrbrFcxC-ZXHrxhbJ2NhF21" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="361" data-original-width="1077" height="107" src="https://blogger.googleusercontent.com/img/a/AVvXsEiHKPbinGPjT7wjgrJSfmQhMw4QnefeijVGufmHucmrwLOQIC7-boeh_rlbaDCGr39IEl1-7X9fyfqLPxP3VZMzdKZpapsSziM7xk6PQyXPXPRgLOcL914gAAoc-X3eEV5ZMe2d_cTvhwGr9z7sn2hil4g4chuIL00L8bD8NFrbrFcxC-ZXHrxhbJ2NhF21" width="320" /></a></div>대화 데이터셋<br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgv9kqfEivlv17-czML0Da1dBCJR2pXpDpEWsGDKXjWSNhb8pNORedsJuQrnbdEKRBqQKJUIFfrxaVw4kGmWAZYyfrvHUrzQMAhBI3mUXH_Z-yhmJRd1BTuZvsgk6m3y8SP-3CgmKOUWEkz4QT01u09CMzRiBPMbbhnKFFnkoqnZWQuhv8RRrsimp-2gmYI" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="430" data-original-width="1051" height="131" src="https://blogger.googleusercontent.com/img/a/AVvXsEgv9kqfEivlv17-czML0Da1dBCJR2pXpDpEWsGDKXjWSNhb8pNORedsJuQrnbdEKRBqQKJUIFfrxaVw4kGmWAZYyfrvHUrzQMAhBI3mUXH_Z-yhmJRd1BTuZvsgk6m3y8SP-3CgmKOUWEkz4QT01u09CMzRiBPMbbhnKFFnkoqnZWQuhv8RRrsimp-2gmYI" width="320" /></a></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEi4ahvELwM9EaAhdMhv_XSq0vpvZCXHVUW3ED_4ATxxJK_QGD20vefQorPaz3quRffImKydNe-gAoJKmszWMqisBhDpTg0-_FV5hhLamRNz5C2FWoNd7-yR4U8EtMBkEpsXjf9DMy7vqYO0HpyiLC2Nv-3XlBBO6ahDv617CbwQWoX0iBsYiB7SsrnV2IBP" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="481" data-original-width="1070" height="144" src="https://blogger.googleusercontent.com/img/a/AVvXsEi4ahvELwM9EaAhdMhv_XSq0vpvZCXHVUW3ED_4ATxxJK_QGD20vefQorPaz3quRffImKydNe-gAoJKmszWMqisBhDpTg0-_FV5hhLamRNz5C2FWoNd7-yR4U8EtMBkEpsXjf9DMy7vqYO0HpyiLC2Nv-3XlBBO6ahDv617CbwQWoX0iBsYiB7SsrnV2IBP" width="320" /></a></div><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both;">QA 데이터셋(참고. <a href="https://competitions.codalab.org/competitions/17208">https://competitions.codalab.org/competitions/17208</a>)</div><div class="separator" style="clear: both;"><br /></div></div></div></div><div style="text-align: left;"><b>학습 모델 아키텍처</b></div><div style="text-align: left;">학습 모델의 핵심 컴포넌트인 트랜스포머를 사용하였다(Google, 2017). 학습 안정화를 위해 Zhang, Sennrich (2019)가 소개한 RMSNorm을 사용하였다. ReLU 함수는 SwiGLU 활성화 함수로 대체하였다(Shazeer, 2020). 이외, 위치 임베딩은 RoPE(로터리 임베딩. Su et al, 2021)을 사용한다. </div><div style="text-align: left;"><br /></div><div style="text-align: left;">옵티마이저는 AdamW(Loshchilov and Hutter, 2017)을 사용해 훈련한다. 베타1=0.9, 베타2=0.95를 사용하여, 어텐션 스코어 계산을 위한 코사인 학습을 일정히 유지한다. 라마의 최대 학습률은 10%이다. 참고로, 0.1 가중치 감쇠를 사용하였다. </div><div style="text-align: left;"><br /></div><div style="text-align: left;">선형 레이어는 PyTorch autograd 대신 레이어를 직접 사용해 메모리 사용량을 줄인다(Korthikantiet al, 2022). 64B 모델에서 380개 토큰/초/GPU가 처리된다. 80GB를 가진 A100 GPU의 경우, 21일 정도가 학습에 소요된다. </div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjOAUQ3X6lMggRRVLhSNuYsfsSHXRASbhV-MwxVyXwPtYc65HSA2Hi2DNOLy-SwB27tNtgHApBFwBcs8TtJQUJStrSzFoA99qyPktIHwJkusS1kt1u3K3HRSxkT8-lSI2GSW7WiQtkgik0XGk-tfjNAlra-4VHI_j4D-ZnnuljixpLo-kmXFVC450oJzZaG/s433/t7.PNG" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="428" data-original-width="433" height="316" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjOAUQ3X6lMggRRVLhSNuYsfsSHXRASbhV-MwxVyXwPtYc65HSA2Hi2DNOLy-SwB27tNtgHApBFwBcs8TtJQUJStrSzFoA99qyPktIHwJkusS1kt1u3K3HRSxkT8-lSI2GSW7WiQtkgik0XGk-tfjNAlra-4VHI_j4D-ZnnuljixpLo-kmXFVC450oJzZaG/s320/t7.PNG" width="320" /></a></div><div class="separator" style="clear: both; text-align: center;">7B, 13B, 33B, 65B 모델에서 훈련 토큰 대비 학습 손실</div><div style="text-align: center;"><br /></div><div style="text-align: left;"><div>훈련 속도 개선을 위해, <a href="https://github.com/facebookresearch/xformers">xformers 라이브러리</a> 사용 시 어텐션 가중치를 저장하지 않았으며, 역방향 가중치 업데이트 시 체크포인트를 사용하였다(Rabe & Staats, 2021. Dao et al, 2022). </div><div><div>이런 과정을 거쳐, 하이퍼파라메터를 최적화하였다.</div><div style="font-weight: bold;"><br /></div></div><div><b>학습 모델 성능 테스트</b></div><div>1. QA 데이터셋</div><div>학습된 모델을 이용해 <a href="https://en.wikipedia.org/wiki/Zero-shot_learning">제로샷(Zero-Shot)</a> 및 퓨샷(<a href="https://en.wikipedia.org/wiki/Few-shot_learning">Few-Shot</a>. 소량 학습 데이터만으로 학습하는 방식) 테스트를 수행하였고, <a href="https://huggingface.co/datasets/google/boolq">BoolQ</a>, <a href="https://huggingface.co/datasets/piqa">PIQA</a>등이 테스트에 사용되었다. 참고로, 제로샷 학습은 학습하지 않은 데이터를 이해하고, 올바른 결과를 출력하도록 학습하는 방법이다.</div><div><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEiEPjs8x2QB68OBiRbucGM6bZWx3BvXhbzJ182kYBCEQZHQi-KUf_9jmB9xUwazN-PJgI5RbY0mAssiPdD5fJVwcMO87X2lSYGTy6_Za25GDctEvmMuQU5zNg9-3bjsAYHY-RIfv9g0F3TvLyq3uPXNUscQgVIUlJEOxofAk_7E-ih_VhgSatmLlS99S2v5" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="286" data-original-width="648" height="176" src="https://blogger.googleusercontent.com/img/a/AVvXsEiEPjs8x2QB68OBiRbucGM6bZWx3BvXhbzJ182kYBCEQZHQi-KUf_9jmB9xUwazN-PJgI5RbY0mAssiPdD5fJVwcMO87X2lSYGTy6_Za25GDctEvmMuQU5zNg9-3bjsAYHY-RIfv9g0F3TvLyq3uPXNUscQgVIUlJEOxofAk_7E-ih_VhgSatmLlS99S2v5=w400-h176" width="400" /></a></div><a href="https://en.wikipedia.org/wiki/Zero-shot_learning" style="text-align: left;">제로샷(Zero Shot)</a> 테스트 결과</div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEg0k4Bc6XfuLwDh9LiQ4MYTqzOchs3MjxgdjixJyIjGHWM4zwPt7oD8g5C4IBF_9O2EjZ5MQWoRjhr55akYJX6ZhXJJvVZGTZXbwTHqHutMGNr0EPO_d5BB3VcsfFthbsIQfTt46eYdOG8J9g81UNaaMKvIn2R3GjDszzaD0DJM8k72GVpjSZGqiFOI_3he" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="773" data-original-width="1276" height="243" src="https://blogger.googleusercontent.com/img/a/AVvXsEg0k4Bc6XfuLwDh9LiQ4MYTqzOchs3MjxgdjixJyIjGHWM4zwPt7oD8g5C4IBF_9O2EjZ5MQWoRjhr55akYJX6ZhXJJvVZGTZXbwTHqHutMGNr0EPO_d5BB3VcsfFthbsIQfTt46eYdOG8J9g81UNaaMKvIn2R3GjDszzaD0DJM8k72GVpjSZGqiFOI_3he=w400-h243" width="400" /></a></div><div class="separator" style="clear: both; text-align: center;">Zero Shot 테스트 예제 중 <a href="https://huggingface.co/datasets/google/boolq" style="text-align: left;">BoolQ</a> (<a href="https://paperswithcode.com/sota/question-answering-on-boolq">성능</a>)</div><br />NaturalQuestions, TriviaQA 등 데이터셋에서도 테스트하여, 비교 모델에 비해 성능이 떨어지지 않는 다는 것을 확인하였다. </div><div><br /></div><div>2. 수학 문제 </div><div>Math 웹 페이지, <a href="https://github.com/openai/grade-school-math">GSM8k </a>데이터셋을 이용해, 수학 문제 풀이를 테스트해 보았다. 결과, GSM8k에서는 라바-65B모델이 Minerva-62B 보다 성능이 뛰어나다. </div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhFhBVYKl2mgi5wWA4kGON0cop1gkXR1hnGJgIk22u22sk8Ti5chI0RL6Ba9MH3ZNDBLBfTimdp5ONKowRjUbpxrXXirPTu41dPmHLVS779XXQNpNbQ68dC4JvjhwF08FVR_jV0EqlxPWXSVVicXQ6Zv2oXmVY6-IUKG54JZ5BaCC30uHn4u08U55NZHwG7" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="1334" data-original-width="2512" height="201" src="https://blogger.googleusercontent.com/img/a/AVvXsEhFhBVYKl2mgi5wWA4kGON0cop1gkXR1hnGJgIk22u22sk8Ti5chI0RL6Ba9MH3ZNDBLBfTimdp5ONKowRjUbpxrXXirPTu41dPmHLVS779XXQNpNbQ68dC4JvjhwF08FVR_jV0EqlxPWXSVVicXQ6Zv2oXmVY6-IUKG54JZ5BaCC30uHn4u08U55NZHwG7=w378-h201" width="378" /></a></div><div class="separator" style="clear: both; text-align: center;"><a href="https://github.com/openai/grade-school-math" style="text-align: left;">GSM8k</a> 데이터셋</div><br /></div><div>3. 코드 생성 테스트</div><div>자연어로 설명된 입력에 대한 코드 생성 성능을 테스트했다. 코드는 파이썬으로 생성된다. 결과를 보았듯이 <a href="https://ai.google/discover/palm2/">PaLM</a>(Google이 개발한 LLM) 모델보다 성능이 뛰어나다.</div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEj-0x5DJISgXoGzIsaSUG0VsudrzchcjQ1nuLIV56yE1TnHmXO8smyVN6-67mGnVimXHZypIesKBhvxx8heVZ_Q6e9Hiso_wL9s4atx2FAURcLju5C8_e_8Dfg3MwWYelK_8IwsGwVRSPr0_6apeMz9_-1B9ks3gAQLus1xMCfW8Z1fMKe196me5jmvtPkR" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="374" data-original-width="334" height="240" src="https://blogger.googleusercontent.com/img/a/AVvXsEj-0x5DJISgXoGzIsaSUG0VsudrzchcjQ1nuLIV56yE1TnHmXO8smyVN6-67mGnVimXHZypIesKBhvxx8heVZ_Q6e9Hiso_wL9s4atx2FAURcLju5C8_e_8Dfg3MwWYelK_8IwsGwVRSPr0_6apeMz9_-1B9ks3gAQLus1xMCfW8Z1fMKe196me5jmvtPkR" width="214" /></a></div><div class="separator" style="clear: both; text-align: center;">코딩 성능 결과</div><div><br /></div><div>4. 대량 멀티태스크 언어 이해 테스트</div><div>MMLU(Hendrycks, 2020)에 의해 소개된 대량 멀티태스크 언어 이해 테스트를 수행해 보았다. 이는 인문학, STEM, 사회과학을 포함한 다양한 지식 영역을 다룬다. 라마는 ArXiv, Gutenberg, Books3를 학습하여, Gopher, Chinchilla, PaLM 와 유사하거나, 어떤 부분은 뛰어나다.</div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEieQQjeYFwKvAMDI1C9gyqHkLk3kAnlgfZrxxkmiBuYBmEeeFvY1tPTbQZohAcXrDogqYNdytOgDaju5EuVs6fe4s9Lk3ZwA_7OQx9Kfs9FFXpBcrJnPlfCqbo6VQ6b3W5OIPVgxt1TXT7UtLK8bKs8Uax7-xnK4UdGPGoo1Kfdl_CwajSQw1dqtTNSe6xH" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="314" data-original-width="562" height="179" src="https://blogger.googleusercontent.com/img/a/AVvXsEieQQjeYFwKvAMDI1C9gyqHkLk3kAnlgfZrxxkmiBuYBmEeeFvY1tPTbQZohAcXrDogqYNdytOgDaju5EuVs6fe4s9Lk3ZwA_7OQx9Kfs9FFXpBcrJnPlfCqbo6VQ6b3W5OIPVgxt1TXT7UtLK8bKs8Uax7-xnK4UdGPGoo1Kfdl_CwajSQw1dqtTNSe6xH" width="320" /></a></div><div class="separator" style="clear: both; text-align: center;">MMLU 성능</div><br />5. 기타</div><div>이외, 미세조정, 편견 테스트, 독성 언어, 종교 편향, 젠더 테스트, TruthfulQA(Lin et al, 2021), 탄소 배출(Wh = GPU-h x (GPU 소비전력) x PUE. Wu et al, 2022) 등 테스트가 수행되었다.</div></div><div><br /></div></div><div style="text-align: left;"><b>마무리</b></div><div style="text-align: left;">라마-13B는 GPT-3보다 성능이 뛰어나며, 크기는 10배 이상 작다. LLaMA-65B는 Chinchilla-70B, PaLM-540B와 거의 유사한 성능을 보인다. 학습 데이터는 공개 커뮤니티에서 수집해 사용되었다. 라마 기술은 xformers 개발 팀, 데이터 정재 팀, 학습 모델 조율팀, 페이스북 AI 인프라 팀 등 많은 사람들의 도움으로 개발되었다.</div><div style="text-align: left;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjEl8jnODxcd0Ymx3mvqJT6xWIE1_U5nqFg3UdAbha_lmqVrrnN6Lt1LmsdtzLkaXBA8BKEKQ2aBQji13bl1ZbEWnhQ0l2cgEt7WxLJLuyP-YYk_tEBcV7TAtOUXrW3D9PooaaHIhzFhqLb6sXxbhK2arfMW4sNAT_kK79BdYM3zRGK71-rKfuezGuiuaOn" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="254" data-original-width="334" height="178" src="https://blogger.googleusercontent.com/img/a/AVvXsEjEl8jnODxcd0Ymx3mvqJT6xWIE1_U5nqFg3UdAbha_lmqVrrnN6Lt1LmsdtzLkaXBA8BKEKQ2aBQji13bl1ZbEWnhQ0l2cgEt7WxLJLuyP-YYk_tEBcV7TAtOUXrW3D9PooaaHIhzFhqLb6sXxbhK2arfMW4sNAT_kK79BdYM3zRGK71-rKfuezGuiuaOn=w234-h178" width="234" /></a></div><br /><div><span style="font-size: medium;"><b>LLAMA-2</b></span></div><div><b><br /></b></div><div><b>서론</b></div></div><div style="text-align: left;">라마-2는 이전 모델의 사전 훈련 결과와 미세 조정을 통해 LLM 성능을 개선한다. 라마2는 챗봇에 최적화되었고, 대부분의 오픈소스 LLM에 비해 성능이 뛰어났다. </div><div style="text-align: left;"><br /></div><div style="text-align: left;"><b>학습 방법</b></div><div style="text-align: left;">라마-2는 라마-1의 미세조정을 통한 성능 개선 버전, 라마-2-Chat이란 챗봇에 특화된 버전을 다음과 같이 공개한다. </div><div style="text-align: left;"><div><ul style="text-align: left;"><li><a href="https://ai.meta.com/resources/models-and-libraries/llama/">https://ai.meta.com/resources/models-and-libraries/llama/</a></li><li><a href="https://ai.meta.com/llama">https://ai.meta.com/llama</a></li><li><a href="https://github.com/facebookresearch/llama">https://github.com/facebookresearch/llama</a></li></ul></div><div>라마-2는 사전훈련 모델을 이용해 시작된다. 이어, 라마-2-챗 초기버전을 개발한다. 강화학습 환경에서 학습 모델을 반복적으로 개선한다. <span style="text-align: center;">RLHF(</span><span style="text-align: center;">Reinforcement Learning with Human </span><span style="text-align: center;">Feedback), 강화학습의 PPO 정책을 통해, 반복적으로 보상 모델링 데이터를 축적하고, 이를 통해, 라마-2 모델을 개선한다. 다음 그림은 이 과정을 보여준다. </span></div></div><div style="text-align: left;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjwYbhx6imKeLiP85S9GsvBJ7CVnS_3gSDoyN2_vUlnaD6Y_kLIq_S8u6b5cVdYk406Ce6I-EXL78l8LoUibD5guEwpyhgpV7_fs7mhN7GocKRQoQCUYcaxBYoMwwj7x6GTYkG_QirOCwDVONVv134FCVcmIIyT3FMJVx6EkuP61kLUWOEgEzIMBSNioVYE" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="506" data-original-width="1144" height="279" src="https://blogger.googleusercontent.com/img/a/AVvXsEjwYbhx6imKeLiP85S9GsvBJ7CVnS_3gSDoyN2_vUlnaD6Y_kLIq_S8u6b5cVdYk406Ce6I-EXL78l8LoUibD5guEwpyhgpV7_fs7mhN7GocKRQoQCUYcaxBYoMwwj7x6GTYkG_QirOCwDVONVv134FCVcmIIyT3FMJVx6EkuP61kLUWOEgEzIMBSNioVYE=w626-h279" width="626" /></a></div><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both;">Reinforcement Learning with Human</div><div class="separator" style="clear: both;"> Feedback (RLHF)</div></div><br /><b>아키텍처 및 사전 훈련</b></div><div style="text-align: left;">아키텍터 대부분은 라마-1과 유사하다. 토크나이저도 동일한 것을 사용했으며, BPE(byte pair encoding) 알고리즘을 사용해 처리되었다. 모든 숫자는 개별 숫자로 분할하고, 알수 없는 글은 UTF-8 문자로 분리한다. 어휘 크기는 32K 토큰이다. </div><div style="text-align: left;"><br /></div><div style="text-align: left;">사전 훈련을 위해, 신뢰성있는 데이터 정리 프로세스를 수행하고, 데이터를 혼합했으며, 40% 더 증가된 데이터를 학습하였다. </div><div style="text-align: left;"><br /></div><div style="text-align: left;">토큰 컨텍스트 길이는 2배로 늘렸으며, GQA(grouped-query attention)을 사용해, 추론 성능을 개선했다. </div><div style="text-align: left;"><br /></div><div style="text-align: left;"><b>학습 과정</b></div><div style="text-align: left;">학습에 사용된 것은 NVIDIA A100s이며, RoCE기반 솔류션을 사용해 GPU간 200Gbps로 데이터를 교환한다. </div><div style="text-align: left;"><br /></div><div style="text-align: left;">성능 향상을 위해 감독된 파인 튜닝(SFT)를 다음과 같이 수행하였다.</div><div style="text-align: left;"><br /></div><div style="text-align: left;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEiX64pmA_fshKdPLA5vGT5qDqLbQjkprAokrRxegJg6Gny4CCAC8HhvsISL1F15uBz9Z2xoELaHPUgI41GQGGK1Lk9ZCzZ3me2Qt6tl-zoaX78oLGmPATopjNR-kv126TkS7v9JWDxqPsKtVMlR0Uexv04WP7v20GDoYxh49HmEEsThsv5UYwbtL9nNnN-Q" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="468" data-original-width="877" height="224" src="https://blogger.googleusercontent.com/img/a/AVvXsEiX64pmA_fshKdPLA5vGT5qDqLbQjkprAokrRxegJg6Gny4CCAC8HhvsISL1F15uBz9Z2xoELaHPUgI41GQGGK1Lk9ZCzZ3me2Qt6tl-zoaX78oLGmPATopjNR-kv126TkS7v9JWDxqPsKtVMlR0Uexv04WP7v20GDoYxh49HmEEsThsv5UYwbtL9nNnN-Q=w420-h224" width="420" /></a></div>미세 조정은 가중치 감소 0.1, 배치 크기 64, 시퀀스 길이 4096을 사용해, 코사인 유사도 학습을 진행한다. 그리고, 2세대만 미세 조정을 한다.</div><div style="text-align: left;"><br /></div><div style="text-align: left;">이후, 강화학습 기반 RLHF를 실행한다. 강화학습 보상 모델은 모델 응답과 프롬프트 입력을 사용해, 스칼라 점수로 모델 생성의 품질을 표현하도록 하였다. 인간의 선호도를 반영하여, 유용성과 안전성을 보상에 추가한다. 이런 이유로 보상 모델은 유용성 보상, 안전성 보상 2개로 구분되어 강화학습한다.</div><div style="text-align: left;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEi2euPbJp53Uc-XidB7JrXB3OatNaJnFBm3ee3I0ac8HJgbo259ZjMC2RdHAaEUj-7E5irJjtD-S-kNy_Ubd3-uKRrgjVYbSTDXhDP3hR__NyK66um3vcT9qDyDExaXIzHzNhzRntwsKeIFBSPrn_ZRYCHDwzgg_niLzRANGljVn0uINmJN_2EwiooBiTFf" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="355" data-original-width="850" height="134" src="https://blogger.googleusercontent.com/img/a/AVvXsEi2euPbJp53Uc-XidB7JrXB3OatNaJnFBm3ee3I0ac8HJgbo259ZjMC2RdHAaEUj-7E5irJjtD-S-kNy_Ubd3-uKRrgjVYbSTDXhDP3hR__NyK66um3vcT9qDyDExaXIzHzNhzRntwsKeIFBSPrn_ZRYCHDwzgg_niLzRANGljVn0uINmJN_2EwiooBiTFf" width="320" /></a></div><br /></div><div style="text-align: left;">채팅 시 초기 명령을 잊어버리는 문제 해결을 위해 Ghost Attention(GAtt)를 사용한다. 다음은 그 결과이다.</div><div style="text-align: left;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEg-CBGDGzEaL-wKiB8sgSiygfqHBXfkpGphfG8iF6F32zI5PmwOJgoFAFz-nyor7Z6wihF1SiSKG1w75hK_KnF7-23cOfxXxMH8m8SibkWp2KuacodRI3DPRdF-fJlBt9ccWwVosysIj6KDxeaB0SXMS23aMXg-FFUZG0t1DhZnmQSnC2_TXBz4FJXxXsdO" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="425" data-original-width="759" height="206" src="https://blogger.googleusercontent.com/img/a/AVvXsEg-CBGDGzEaL-wKiB8sgSiygfqHBXfkpGphfG8iF6F32zI5PmwOJgoFAFz-nyor7Z6wihF1SiSKG1w75hK_KnF7-23cOfxXxMH8m8SibkWp2KuacodRI3DPRdF-fJlBt9ccWwVosysIj6KDxeaB0SXMS23aMXg-FFUZG0t1DhZnmQSnC2_TXBz4FJXxXsdO=w369-h206" width="369" /></a></div><div class="separator" style="clear: both; text-align: center;">개선된 모델</div><div class="separator" style="clear: both; text-align: center;"><br /></div><div style="text-align: left;">생성 결과의 안전성을 위해, 학습 과정은 RLHF로 진행된다. 이 결과, 다음과 같은 개선이 있었다. </div><div style="text-align: left;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhKcvkbJjABOfPernY4QzH65z6V73dBiyxL2Hh3citf8TJGcWFKWw4e4otnib1LMI_ShftB1CBHy4QaM-hG_WB-EgzRy69OGUm3ZZbH8w18vf52sAU4OrWQ1nuo8uiGdeNSrM1_ufcFxybPRHeP-UTmCJCiZchC6yCMl6Zbz4AcHig8ceSTwdyp7CTVw3dG" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="514" data-original-width="897" height="183" src="https://blogger.googleusercontent.com/img/a/AVvXsEhKcvkbJjABOfPernY4QzH65z6V73dBiyxL2Hh3citf8TJGcWFKWw4e4otnib1LMI_ShftB1CBHy4QaM-hG_WB-EgzRy69OGUm3ZZbH8w18vf52sAU4OrWQ1nuo8uiGdeNSrM1_ufcFxybPRHeP-UTmCJCiZchC6yCMl6Zbz4AcHig8ceSTwdyp7CTVw3dG" width="320" /></a></div><div style="text-align: left;"><br /></div><div style="text-align: left;">라마-2 와 <a href="https://www.mosaicml.com/mpt">MPT </a>등 모델 간 성능 비교는 다음과 같다.</div><div style="text-align: left;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEi6Oek69vWd4yYO-qH8oSNVNbbWapKLGbcRterJ6_spZp5zesjjp57JIw9r2yt06xOQPIXeA70iwf-gWbKEIk41ldIf3jAt2omBBD8CpRUN3UHuyR3ZbfCwrArXk2wTEnn52G4Po9fQZfz9UCQKjO-fd1P7nmLrw86Sg_IZoZcPOQYcL7P3DGnUC3KpqTfD" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="324" data-original-width="820" height="177" src="https://blogger.googleusercontent.com/img/a/AVvXsEi6Oek69vWd4yYO-qH8oSNVNbbWapKLGbcRterJ6_spZp5zesjjp57JIw9r2yt06xOQPIXeA70iwf-gWbKEIk41ldIf3jAt2omBBD8CpRUN3UHuyR3ZbfCwrArXk2wTEnn52G4Po9fQZfz9UCQKjO-fd1P7nmLrw86Sg_IZoZcPOQYcL7P3DGnUC3KpqTfD=w451-h177" width="451" /></a></div><br /></div><b>마무리</b></div><div style="text-align: left;">라마-2는 다양한 방법을 통해 개선되었으나, 기본 아키텍처는 라마-1과 유사하다. 인간 감독을 포함한 강화학습기법을 사용한 것은 GPT에서 진행한 것과 동일하다. 라마-2의 챗 버전은 챗봇 서비스에 유용하다. </div><div style="text-align: left;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgE2OW0I2P5usZcw62e8t4TwumQPuHGOp6g4xTJhG5v8YsiPxap4AhGsjnOpeZ7m_ahmknqHHhJMzB6yHe1MGHt8K4Tya93kJ4x37VqOGRQVeJwhKzdIHi6aMlAoKIsYnYFEFZXAf4_Hq580pO-LUDF8Ojb98TEofceHZzN-nwIhbEj84tbqFDQnvzZfgwM" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="573" data-original-width="896" height="241" src="https://blogger.googleusercontent.com/img/a/AVvXsEgE2OW0I2P5usZcw62e8t4TwumQPuHGOp6g4xTJhG5v8YsiPxap4AhGsjnOpeZ7m_ahmknqHHhJMzB6yHe1MGHt8K4Tya93kJ4x37VqOGRQVeJwhKzdIHi6aMlAoKIsYnYFEFZXAf4_Hq580pO-LUDF8Ojb98TEofceHZzN-nwIhbEj84tbqFDQnvzZfgwM=w376-h241" width="376" /></a></div><div class="separator" style="clear: both; text-align: center;">챗봇 성능 개선 결과 일부</div></div></div><div style="text-align: left;"><br /></div><div style="text-align: left;"><span style="font-size: medium;"><b>결론</b></span></div><div style="text-align: left;">라마-1, 라마-2 기술 분석을 위해, 논문을 조사해, 핵심적인 내용을 정리하였다. 이를 통해, 라마 LLM을 좀 더 잘 이해할 수 있다. 라마는 Meta AI 리더인 Yann LeCun 교수의 딥러닝 민주화 철학에 영향을 받았다. 라마는 윤리적 문제 등 다양한 이해당사자들의 입장을 고려해 개발되었으며, 꾸준히 발전되고 있다.</div><div style="text-align: left;"><br /></div><div style="text-align: left;"><b><span style="font-size: medium;">레퍼런스</span></b></div><div style="text-align: left;"><ul style="line-height: 1.4; list-style-image: initial; list-style-position: initial; margin: 0.5em 0px; padding: 0px 2.5em;"><li style="border: none; color: #333333; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; font-size: 14.85px; margin: 0px 0px 0.25em; padding: 0px;">Facebook, 2023, <a href="https://ai.meta.com/research/publications/llama-open-and-efficient-foundation-language-models/" style="color: #992211; text-decoration-line: none;">LLaMA: Open and Efficient Foundation Language Models | Research - AI at Meta</a></li><li style="border: none; color: #333333; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; font-size: 14.85px; margin: 0px 0px 0.25em; padding: 0px;">Medium, 2023, <a href="https://towardsdatascience.com/how-to-build-an-ai-assistant-with-openai-python-8b3b5a636f69" style="color: #992211; text-decoration-line: none;">How to Build an AI Assistant with OpenAI & Python | by Shaw Talebi | Towards Data Science</a></li><li style="border: none; color: #333333; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; font-size: 14.85px; margin: 0px 0px 0.25em; padding: 0px;">OpenAI, 2021, <a href="https://arxiv.org/abs/2103.00020" style="color: #992211; text-align: center; text-decoration-line: none;">Learning Transferable Visual Models From Natural Language Supervision</a></li><li style="border: none; color: #333333; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; font-size: 14.85px; margin: 0px 0px 0.25em; padding: 0px;">Huggin Face, 2023, <a href="https://huggingface.co/docs/transformers/main/model_doc/llama" style="color: #992211; text-decoration-line: none;">LLaMA: Open and Efficient Foundation Language Models</a></li><li style="border: none; margin: 0px 0px 0.25em; padding: 0px;"><span face="Arial, Tahoma, Helvetica, FreeSans, sans-serif" style="color: #333333;">Facebook, </span>Hugo Touvron, Thibaut Lavril, Gautier Izacard, Xavier Martinet, Marie-Anne Lachaux, Timothee Lacroix, Baptiste Rozière, Naman Goyal, Eric Hambro, Faisal Azhar, Aurelien Rodriguez, Armand Joulin, Edouard Grave, Guillaume Lample, <span face="Arial, Tahoma, Helvetica, FreeSans, sans-serif" style="color: #333333;">2023, </span><a href="https://arxiv.org/pdf/2302.13971.pdf" style="color: #992211; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; text-decoration-line: none;">LLaMA: OpenandEfficient Foundation Language Models</a> (<a href="https://academia.stackexchange.com/questions/7/does-publishing-a-paper-on-arxiv-prevent-me-from-submitting-it-to-a-non-open-acc">journal</a>)</li><li style="border: none; color: #333333; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; font-size: 14.85px; margin: 0px 0px 0.25em; padding: 0px;">Facebook, 2023, <a href="https://arxiv.org/pdf/2307.09288.pdf" style="color: #992211; text-decoration-line: none;">Llama 2: OpenFoundation and Fine-Tuned Chat Models</a></li></ul><div><span face="Arial, Tahoma, Helvetica, FreeSans, sans-serif" style="color: #333333;"><span style="font-size: 14.85px;"><br /></span></span></div><div><span face="Arial, Tahoma, Helvetica, FreeSans, sans-serif" style="color: #333333;"><span style="font-size: 14.85px;"><b>추신</b>. </span></span></div><div><span face="Arial, Tahoma, Helvetica, FreeSans, sans-serif" style="color: #333333;"><span style="font-size: 14.85px;">연말 연구 과제 평가 행정 대략 마무리 후, 미루고 쌓아 놓은 기술, 논문, 코드 급하게 소화 중... 회사 연구일?이 오히려 진짜 연구에 방해되는 현상은 이 바닥 사람들이라면 다 아는 팩트(굳건하게 만들어진 시스템이라 어쩔 수 없어요). 월급 받는 직딩이니 일은 제대로 하고, 남는 시간에 찐 공부, 연구, 개발할 수 밖에.ㅎ - 2.17</span></span></div><div><span face="Arial, Tahoma, Helvetica, FreeSans, sans-serif" style="color: #333333;"><span style="font-size: 14.85px;"><br /></span></span></div><div><span face="Arial, Tahoma, Helvetica, FreeSans, sans-serif" style="color: #333333;"><span style="font-size: 14.85px;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhGxKtWZQhCVZ1xS_y6czCLhD_h-1ACgxU6JkGqZ6oXTaeyNy_OETvlU3CGQ_Em4HZX9qJhqHVnRB_vnhVJ59ZFyUqatD7DJO0LhXoR8wnHvCti2ip02i8ohXZVQ7bVjxJ1LfWNcqTGpd9PiQVDe7c4v9SsVZhgOsM8nnsneGFJ-G8vlgukaRGK8SnAXGmt" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="465" data-original-width="700" height="213" src="https://blogger.googleusercontent.com/img/a/AVvXsEhGxKtWZQhCVZ1xS_y6czCLhD_h-1ACgxU6JkGqZ6oXTaeyNy_OETvlU3CGQ_Em4HZX9qJhqHVnRB_vnhVJ59ZFyUqatD7DJO0LhXoR8wnHvCti2ip02i8ohXZVQ7bVjxJ1LfWNcqTGpd9PiQVDe7c4v9SsVZhgOsM8nnsneGFJ-G8vlgukaRGK8SnAXGmt" width="320" /></a></div><br /><br /></span></span></div></div>Daddy Makerhttp://www.blogger.com/profile/02275758703365576780noreply@blogger.com0tag:blogger.com,1999:blog-5201956450461596914.post-5911256901659996812024-02-14T18:15:00.000-08:002024-03-13T23:01:41.653-07:00로컬 호스트 LLM 오픈소스 OLLAMA 기반 PDF 지식 챗봇 서비스 간단히 만들어보기<div style="text-align: left;"><div class="separator" style="clear: both; text-align: left;">요즘 LLM 모델을 사용하는 방법이 점차 간편해 지고 있어, 자체적으로 LLM을 구축해, 챗봇, 전문가 시스템 등을 본인 서버에서 제공하는 경우가 많아지고 있다. 이 글은 GPU있는 PC에서 직접 실행해 볼 수 있도록, 로컬 호스트 LLM(Large Language Model) 오픈소스 기반 PDF 지식 챗봇 서비스를 간단히 개발해 본다. </div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;">이를 위해, 기존 BIM pdf 파일을 검색해 학습하고, LLM에 증강 학습한 후, 이를 간단한 UI로 웹서비스 하는 과정을 간략히 보여주고, 구현한다.</div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;">이 글은 로컬 LLM의 편한 개발을 지원하는 <a href="https://ollama.com/">OLLAMA</a>, LLM 프롬프트 엔지니어링 프레임웍인 <a href="https://www.langchain.com/">LangChain</a>, 텍스트 임베딩 벡터 데이터베이스 <a href="https://www.trychroma.com/">Chroma</a>, 손쉬운 web app 개발 지원 도구인 <a href="https://streamlit.io/">Streamlit</a>을 사용한다. 이를 이용해, 간단한 BIM 전문 지식을 PDF로 학습한 챗봇을 간단히 개발한다. </div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEje4xj6odUujFeXfbkOgl4MeOsaMq7uxIcDwOcz6kUsQ9LlEvJUqeRephU-Euhw2MvAuqVMKJLvFd2zDyWW1xWjloy2xCAm80dU9yztOvWPgrJLQar1R8HzfWbA3AJZQczGrRbsBeFdngJnxIqKb0un48YEvCIm_maoMpsqhq5MA-hu6vCmjIUb9_GsugLs" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="593" data-original-width="929" height="204" src="https://blogger.googleusercontent.com/img/a/AVvXsEje4xj6odUujFeXfbkOgl4MeOsaMq7uxIcDwOcz6kUsQ9LlEvJUqeRephU-Euhw2MvAuqVMKJLvFd2zDyWW1xWjloy2xCAm80dU9yztOvWPgrJLQar1R8HzfWbA3AJZQczGrRbsBeFdngJnxIqKb0un48YEvCIm_maoMpsqhq5MA-hu6vCmjIUb9_GsugLs" width="320" /></a></div></div></div>로컬 호스트 LLM 챗봇 아키텍처<br /></div><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEj98OaHw6wgPUXBvECiTRkkJqfrjdRVWqR-Wo_04OFVe_7stUP6rP88XRUjvofeycfietJIqEalRBjGtxumQU7XacKUxaYCjbDiF4zz-ZbNTqiBgXFMx2GHvZy06DRZyHHGDXMXL8Hpux_39MGuWiYeD9sINcf3Teg7_lUOxgYkiC85cQCJPGFw7wGq8jI1" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="355" data-original-width="731" height="194" src="https://blogger.googleusercontent.com/img/a/AVvXsEj98OaHw6wgPUXBvECiTRkkJqfrjdRVWqR-Wo_04OFVe_7stUP6rP88XRUjvofeycfietJIqEalRBjGtxumQU7XacKUxaYCjbDiF4zz-ZbNTqiBgXFMx2GHvZy06DRZyHHGDXMXL8Hpux_39MGuWiYeD9sINcf3Teg7_lUOxgYkiC85cQCJPGFw7wGq8jI1=w400-h194" width="400" /></a></div><div class="separator" style="clear: both; text-align: center;">구현된 BIM 지식 챗봇 서비스</div></div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;">LLM 에 관련된 깊은 내용은 아래 링크를 참고한다. 이 글은 여러 참고 자료를 이용해 작성된 것이다. 상세 내용은 레퍼런스를 참고바란다.</div><div class="separator" style="clear: both; text-align: left;"><ul style="text-align: left;"><li><a href="https://daddynkidsmakers.blogspot.com/2024/02/llama-2.html">오픈소스 기반 LLM의 민주화, LLAMA-2 논문 분석 및 기술 요약하기</a></li><li><a href="https://daddynkidsmakers.blogspot.com/2023/09/llama2.html">ChatGPT와 같은 생성AI 서비스 개발을 위한 간단한 LLAMA-2 설치와 사용법</a></li><li><a href="https://daddynkidsmakers.blogspot.com/2021/10/blog-post.html" style="font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; font-size: 14.85px;">어텐션 기반 트랜스포머 딥러닝 모델 이해, 활용 사례 및 파치토치를 통한 간단한 사용방법 소개</a></li><li><a href="https://daddynkidsmakers.blogspot.com/2024/02/blog-post.html" style="font-size: 14.85px;">트랜스포머 디코더 핵심 코드 구현을 통한 동작 메커니즘 이해하기</a></li><li><a href="https://daddynkidsmakers.blogspot.com/2024/02/ai-stable-diffusion.html">파이토치로 멀티모달 생성AI 모델, Stable Diffusion 아키텍처를 코딩, 구현</a></li><li><a href="https://github.com/mac999/computer_vision_deeplearning/tree/main" style="color: #992211; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; font-size: 14.85px; text-decoration-line: none;">Computer_vision_deeplearning: computer vision based on deep learning lecture materials, github</a></li></ul><div>LLM은 빅테크 업체 간 경쟁이 심한 분야이다. 관련해, <a href="https://github.com/google/gemma_pytorch">Gemma</a>, <a href="https://www.databricks.com/blog/mpt-7b">MPT-7B</a>과 같은 다른 LLM 모델들도 오픈소스로 공개되고 있어 선택지가 많아지고 있다. 이와 관련해서는 다음을 참고한다. </div><div><ul style="text-align: left;"><li>G<a href="https://github.com/google/gemma_pytorch">oogle, Gemma_pytorch: The official PyTorch implementation of Google's Gemma models (github.com)</a>, <a href="https://console.cloud.google.com/vertex-ai/publishers/google/model-garden/335?pli=1">Gemma – Vertex AI – Google Cloud Console</a></li><li><a href="https://huggingface.co/bigscience/bloom">Bloom · Hugging Face</a></li><li><a href="https://www.databricks.com/blog/mpt-7b">MPT-7B: Open-Source, Commercially Usable LLMs | Databricks Blog</a></li><li><a href="https://falconllm.tii.ae/">Falcon LLM</a></li><li><a href="https://lmsys.org/blog/2023-03-30-vicuna/">Vicuna: An Open-Source Chatbot</a></li></ul></div><div><br /></div></div><div class="separator" style="clear: both; text-align: left;"><b>설치</b></div><div class="separator" style="clear: both; text-align: left;">설치를 위해서는 NVIDIA driver, CUDA, tensorflow, pytorch 등 기본 딥러닝 개발 환경이 설치되어 있어야 한다(최소 구동 GPU RAM 6GB).</div><div class="separator" style="clear: both; text-align: left;"><ul style="text-align: left;"><li><a href="https://www.tensorflow.org/install/pip?hl=ko#windows-native_1">TensorFlow 설치</a></li><li><a href="https://pytorch.org/get-started/locally/">Start Locally | PyTorch</a> 설치</li></ul></div>설치 순서는 다음과 같다. </div><div class="separator" style="clear: both; text-align: left;"> </div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;">1. 기본 패키지를 설치한다. LLM 모델 기반 서비스 개발 지원 라이브러리 <a href="https://www.langchain.com/" style="text-align: center;">LangChain</a>, 앱 App UI 개발 지원 <a href="https://streamlit.io/" style="text-align: center;">streamlit</a>.io, 텍스트 임베딩 벡터 데이터베이스 <a href="https://www.trychroma.com/">Chroma</a> DB 등을 설치한다. </div><div class="separator" style="clear: both;">pip install langchain streamlit streamlit_chat pypdf fastembed chardet</div><div class="separator" style="clear: both;">pip install chromadb==0.4.15</div><div class="separator" style="clear: both;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEiqGBseH1ITtqp0B62t2uIhXI8S6Nqo44WwyPq_wygGiXn4jArfl-xRqMWDNkoCKAn2EJjn0ycvsa2myELs7l31EjJNgtqMDRzYcj92Gb-pZMZKcWu1u6CNnwMUZIy9YzjTUbwseT2b-caiRIW3o7eI4nke6dQq1nJ4DFc-Y0i_FUPZAs6APxH9Jl1hJltb" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="864" data-original-width="906" height="174" src="https://blogger.googleusercontent.com/img/a/AVvXsEiqGBseH1ITtqp0B62t2uIhXI8S6Nqo44WwyPq_wygGiXn4jArfl-xRqMWDNkoCKAn2EJjn0ycvsa2myELs7l31EjJNgtqMDRzYcj92Gb-pZMZKcWu1u6CNnwMUZIy9YzjTUbwseT2b-caiRIW3o7eI4nke6dQq1nJ4DFc-Y0i_FUPZAs6APxH9Jl1hJltb=w183-h174" width="183" /></a></div><div class="separator" style="clear: both; text-align: center;">다양한 LLM 모델을 이용한 서비스 개발을 지원하는 랭체인(<a href="https://www.langchain.com/">LangChain</a>) 패키지</div><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgEWrmJOAeZsBvw4yShrCUTn1PkWARl58yvXExn4G1nRPSCgxSTcarctfeyoeyocgIZ8qRTsVqRdhxoEWK-PugAVP321hEYvEv3YsIEvkKORENOIPAur-wNGjOCBbsAas1MFmn4QFQEFyJc2MKUnoyZpKb4DY9EMPwb2woinzH3P161WQKoomaz3lXXtrdw" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="401" data-original-width="757" height="170" src="https://blogger.googleusercontent.com/img/a/AVvXsEgEWrmJOAeZsBvw4yShrCUTn1PkWARl58yvXExn4G1nRPSCgxSTcarctfeyoeyocgIZ8qRTsVqRdhxoEWK-PugAVP321hEYvEv3YsIEvkKORENOIPAur-wNGjOCBbsAas1MFmn4QFQEFyJc2MKUnoyZpKb4DY9EMPwb2woinzH3P161WQKoomaz3lXXtrdw" width="320" /></a></div><div class="separator" style="clear: both; text-align: center;">간단한 코드로 웹 App 개발 지원 UI 라이브러리 <a href="https://streamlit.io/">streamlit.io</a> 패키지</div></div><br /></div><div class="separator" style="clear: both;">혹은 pip와 유사한 패키지 설치 관리자인 <a href="https://python-poetry.org/docs/">poetry</a> 설치 후, 다음 사용 패키지들을 pyproject.toml 이름으로 저장한 후, 설치한다.</div><div class="separator" style="clear: both;"><div class="separator" style="clear: both;"><span style="font-size: x-small;">[tool.poetry]</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;">name = "Local LLM"</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;">version = "0.1.0"</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;">description = ""</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;">readme = "README.md"</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"><br /></span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;">[tool.poetry.dependencies]</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;">python = ">=3.9,<3.9.7 || >3.9.7,<3.12"</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;">pypdf = "^3.17.1"</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;">streamlit = "^1.29.0"</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;">streamlit-chat = "^0.1.1"</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;">langchain = "^0.0.343"</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;">fastembed = "^0.1.1"</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;">openai = "^1.3.6"</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;">langchainhub = "^0.1.14"</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;">chromadb = "^0.4.18"</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;">watchdog = "^3.0.0"</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"><br /></span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;">[build-system]</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;">requires = ["poetry-core"]</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;">build-backend = "poetry.core.masonry.api"</span></div><div class="separator" style="clear: both;"><br /></div><div class="separator" style="clear: both;"><a href="https://python-poetry.org/docs/">poetry </a>설치는 다음과 같다. </div><div class="separator" style="clear: both;">poetry install</div><div class="separator" style="clear: both;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgGGHXQu1N2mG5melTUt0UHZ3cYcPQ4PjIIWBC0W4M6ahXj7OR-2INCWG5ub_Z95msjWeLWdKwYqnu6m9Ovw52RKQwi-E--sA5IouBtc4A3VgWSgtoKkjF2Zc5QfyDWErM_vp9Bi-iTrpeC9sWkFdVO0BpO0hXP0_GQ-8uxZmsvqUDbWW7v-uVZmCaVHUf9" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="419" data-original-width="1044" height="160" src="https://blogger.googleusercontent.com/img/a/AVvXsEgGGHXQu1N2mG5melTUt0UHZ3cYcPQ4PjIIWBC0W4M6ahXj7OR-2INCWG5ub_Z95msjWeLWdKwYqnu6m9Ovw52RKQwi-E--sA5IouBtc4A3VgWSgtoKkjF2Zc5QfyDWErM_vp9Bi-iTrpeC9sWkFdVO0BpO0hXP0_GQ-8uxZmsvqUDbWW7v-uVZmCaVHUf9=w400-h160" width="400" /></a></div><div class="separator" style="clear: both; text-align: center;">패키지 설치 모습</div><br /></div></div></div></div><div style="text-align: left;">2. <a href="https://ollama.com/">Ollama</a> 웹사이트를 방문해, 로컬 LLM을 지원하는 OLLAMA를 설치한다. <br /></div><div style="text-align: left;">ollama run llama2</div><div style="text-align: left;"><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEiPIGXC75hXn6LLJ17JFfFmeAD4SV7tzq2w_8Jgo8V2Zg_w3Yfp33ejBt88aM4WNkSVBc931Po-8n24r-w0uDbFMOn3XDRdKON3bSRN3w-RSM9f8htjQxJScPk0PmQrWVoVnk56P3nbCYl8_aasqLkZHX8ctcfzw1NaNII_6mF8w7BGUSFnb6G_R2MYjj4F" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="443" data-original-width="425" height="240" src="https://blogger.googleusercontent.com/img/a/AVvXsEiPIGXC75hXn6LLJ17JFfFmeAD4SV7tzq2w_8Jgo8V2Zg_w3Yfp33ejBt88aM4WNkSVBc931Po-8n24r-w0uDbFMOn3XDRdKON3bSRN3w-RSM9f8htjQxJScPk0PmQrWVoVnk56P3nbCYl8_aasqLkZHX8ctcfzw1NaNII_6mF8w7BGUSFnb6G_R2MYjj4F" width="230" /></a></div><div class="separator" style="clear: both; text-align: center;"><a href="https://ollama.com/">Ollama</a></div><br /></div><div class="separator" style="clear: both; text-align: left;">3. 다음 Ollama 명령을 터미널에서 실행하여, 많이 사용되는 몇몇 LLM 학습 모델을 다운로드 한다. 상세한 모델 종류는 <a href="https://github.com/ollama/ollama">여기를 참고</a>한다.</div><div class="separator" style="clear: both; text-align: left;">ollama pull mistral </div><div class="separator" style="clear: both; text-align: left;">ollama pull dolphin-phi</div><div class="separator" style="clear: both; text-align: left;">ollama pull llama2</div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;">다음 명령으로 다운로드된 모델 이미지를 확인한다.</div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;">ollama list</div><div style="text-align: center;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEi5g-xBucYYscPVXPP-r38TtHvybiWBFNRXssYyUa-oVNEIOdkQrpPf2BMekP5ZkIozXid5E4ggD77jI4FoIpGU4Wnuz-eSTKqIxFAn6EAEMOoiOZRypsSyLCgxeNVUqyZckxQBuIVUe5KpxlMN0-gDs7gBYci68MNxnlqfL4p5l4mV5SyVELvM3zF-9sKK" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="89" data-original-width="560" height="56" src="https://blogger.googleusercontent.com/img/a/AVvXsEi5g-xBucYYscPVXPP-r38TtHvybiWBFNRXssYyUa-oVNEIOdkQrpPf2BMekP5ZkIozXid5E4ggD77jI4FoIpGU4Wnuz-eSTKqIxFAn6EAEMOoiOZRypsSyLCgxeNVUqyZckxQBuIVUe5KpxlMN0-gDs7gBYci68MNxnlqfL4p5l4mV5SyVELvM3zF-9sKK=w350-h56" width="350" /></a></div></div></div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><br /></div></div><div class="separator" style="clear: both; text-align: left;"><b>PDF 기반 전문가 서비스 구축을 위한 RAG 파이프라인 구축<br /></b><div class="separator" style="clear: both;">RAG(Retrieval-augmented generation)는 학습된 LLM 모델의 외부 데이터 소스를 활용하여, 검색 모델을 증강하는 기법이다. 이를 통해, 질문이 입력되면, QAChain을 통해, 벡터 스토리지에 해당 PDF 내용 임베딩이 있는 지 확인하고, 이 임베딩 컨텐츠를 LLAMA 서버에 전달하여, 적절한 답변을 얻는다. </div><div class="separator" style="clear: both;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEiDM37NG6KjIzGXvuBxcqyAxJbLl86o_Ns-VxI5sVLZXDiJmYdoXqHAl2zCKk0pSiE8RcsRQhcIaUq3W9MVo2DFHh5t-5E9zroRlK5JczaPyhhwuP21k-fKnfAky0nnEMD3EcMW3C2mGUYYxmVOExC9fp5iT_kz_4-Uz0ol0p6aKlPguP6RIv6bC95gxVY4" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="380" data-original-width="720" height="222" src="https://blogger.googleusercontent.com/img/a/AVvXsEiDM37NG6KjIzGXvuBxcqyAxJbLl86o_Ns-VxI5sVLZXDiJmYdoXqHAl2zCKk0pSiE8RcsRQhcIaUq3W9MVo2DFHh5t-5E9zroRlK5JczaPyhhwuP21k-fKnfAky0nnEMD3EcMW3C2mGUYYxmVOExC9fp5iT_kz_4-Uz0ol0p6aKlPguP6RIv6bC95gxVY4=w421-h222" width="421" /></a></div><div class="separator" style="clear: both; text-align: center;">Chroma DB 기반 RAG 처리 개념</div><br /></div><div class="separator" style="clear: both;">RAG는 LLM의 다음 문제를 해결한다.</div><div class="separator" style="clear: both;"><ul><li>편향성: 훈련 데이터에만 편향된 답변 생성</li><li>잘못된 정보 생성: 잘못되거나 유해한 정보 생성</li><li>할루시네이션(Hallucination) 현상: 사실이 아닌 정보를 생성하는 문제</li><li>비전문성 정보 생성: 학습되지 않은 질문에 대한 답변을 전문가처럼 생성하는 문제</li></ul></div><div class="separator" style="clear: both;">이를 위해, 전문지식이 포함된 PDF파일들을 입력하면, 이를 문서 청크로 분할해, 벡터 저장소에 청크 파일을 임베딩하여 데이터베이스에 저장한다. </div><div class="separator" style="clear: both;"><br /></div></div><div class="separator" style="clear: both; text-align: left;">다음은 그 구조를 보여준다. </div></div><div style="text-align: left;"><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEiRlcbhc02F-IitbLlA_-J7dzXXxmTY2rHHoxwbUxYAhc5TEV0mlk0Yr_nmXCaSvtwiubL5d0UXr-R73fICnJt48dPUiUDLWE-tu8IwMR2UKnOAJ12XdHiEVZAF62TNHvvfhZ2U8kdtpmnEaymIah2bqU7Emtf4EAMzV59PiL__56gCzqCt_Rkt3U3-6TLZ" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="318" data-original-width="600" height="213" src="https://blogger.googleusercontent.com/img/a/AVvXsEiRlcbhc02F-IitbLlA_-J7dzXXxmTY2rHHoxwbUxYAhc5TEV0mlk0Yr_nmXCaSvtwiubL5d0UXr-R73fICnJt48dPUiUDLWE-tu8IwMR2UKnOAJ12XdHiEVZAF62TNHvvfhZ2U8kdtpmnEaymIah2bqU7Emtf4EAMzV59PiL__56gCzqCt_Rkt3U3-6TLZ=w400-h213" width="400" /></a></div><div class="separator" style="clear: both; text-align: center;">PDF 텍스트 임베딩 벡터 데이터베이스 구축 후 LLM 기반 질의 답변 생성 구조</div></div><br />다음 코드를 rag.py란 파일로 저장한다.</div><div><span style="font-size: x-small;">from langchain.vectorstores import Chroma</span></div><div><span style="font-size: x-small;">from langchain.chat_models import ChatOllama</span></div><div><span style="font-size: x-small;">from langchain.embeddings import FastEmbedEmbeddings</span></div><div><span style="font-size: x-small;">from langchain.schema.output_parser import StrOutputParser</span></div><div><span style="font-size: x-small;">from langchain.document_loaders import PyPDFLoader</span></div><div><span style="font-size: x-small;">from langchain.text_splitter import RecursiveCharacterTextSplitter</span></div><div><span style="font-size: x-small;">from langchain.schema.runnable import RunnablePassthrough</span></div><div><span style="font-size: x-small;">from langchain.prompts import PromptTemplate</span></div><div><span style="font-size: x-small;">from langchain.vectorstores.utils import filter_complex_metadata</span></div><div><br /></div><div><span style="font-size: x-small;">class ChatPDF:</span></div><div><span style="font-size: x-small;"> vector_store = None</span></div><div><span style="font-size: x-small;"> retriever = None</span></div><div><span style="font-size: x-small;"> chain = None</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;"> def __init__(self):</span></div><div><span style="font-size: x-small;"> self.model = ChatOllama(model="mistral") # OLLAMA의 mistral 모델 이용</span></div><div><span style="font-size: x-small;"> self.text_splitter = RecursiveCharacterTextSplitter(chunk_size=1024, chunk_overlap=100) # PDF 텍스트 분할</span></div><div><span style="font-size: x-small;"> self.prompt = PromptTemplate.from_template(</span></div><div><span style="font-size: x-small;"> """</span></div><div><span style="font-size: x-small;"> <s> [INST] You are an assistant for question-answering tasks. Use the following pieces of retrieved context </span></div><div><span style="font-size: x-small;"> to answer the question. If you don't know the answer, just say that you don't know. Use three sentences</span></div><div><span style="font-size: x-small;"> maximum and keep the answer concise. [/INST] </s> </span></div><div><span style="font-size: x-small;"> [INST] Question: {question} </span></div><div><span style="font-size: x-small;"> Context: {context} </span></div><div><span style="font-size: x-small;"> Answer: [/INST]</span></div><div><span style="font-size: x-small;"> """</span></div><div><span style="font-size: x-small;"> )</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;"> def ingest(self, pdf_file_path: str):</span></div><div><span style="font-size: x-small;"> docs = PyPDFLoader(file_path=pdf_file_path).load() # 랭체인의 PDF 모듈 이용해 문서 로딩</span></div><div><span style="font-size: x-small;"> chunks = self.text_splitter.split_documents(docs) # 문서를 청크로 분할</span></div><div><span style="font-size: x-small;"> chunks = filter_complex_metadata(chunks) </span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;"> vector_store = Chroma.from_documents(documents=chunks, embedding=FastEmbedEmbeddings()) # 임메딩 벡터 저장소 생성 및 청크 설정</span></div><div><span style="font-size: x-small;"> self.retriever = vector_store.as_retriever(</span><span style="font-size: small;">search_type="similarity_score_threshold",</span></div><div><span style="font-size: x-small;"> search_kwargs={</span></div><div><span style="font-size: x-small;"> "k": 3,</span></div><div><span style="font-size: x-small;"> "score_threshold": 0.5,</span></div><div><span style="font-size: x-small;"> },</span></div><div><span style="font-size: x-small;"> ) # 유사도 스코어 기반 벡터 검색 설정</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;"> self.chain = ({"context": self.retriever, "question": RunnablePassthrough()}</span><span style="font-size: small;"> | self.prompt</span><span style="font-size: small;"> | self.model</span><span style="font-size: small;"> | StrOutputParser()) # 프롬프트 입력에 대한 모델 실행, 출력 파서 방법 설정</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;"> def ask(self, query: str): # 질문 프롬프트 입력 시 호출</span></div><div><span style="font-size: x-small;"> if not self.chain:</span></div><div><span style="font-size: x-small;"> return "Please, add a PDF document first."</span></div><div><span style="font-size: x-small;"> return self.chain.invoke(query) </span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;"> def clear(self): # 초기화</span></div><div><span style="font-size: x-small;"> self.vector_store = None</span></div><div><span style="font-size: x-small;"> self.retriever = None</span></div><div style="text-align: left;"><span style="font-size: x-small;"> self.chain = None</span> </div><div style="text-align: left;"><br /></div><div style="text-align: left;"><b>웹 기반 App 개발</b></div><div style="text-align: left;">다음과 같이 UI를 가진 앱을 app.py 파일명으로 코딩한다.</div><div style="text-align: left;"><div><span style="font-size: x-small;">import os, </span><span style="font-size: small;">tempfile</span></div><div><span style="font-size: x-small;">import streamlit as st</span></div><div><span style="font-size: x-small;">from streamlit_chat import message</span></div><div><span style="font-size: x-small;">from rag import ChatPDF</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;">st.set_page_config(page_title="ChatPDF") # 앞서 정의한 PDF 임베딩 벡터 데이터베이스 RAG 모듈 임포트</span></div><div><br /></div><div><span style="font-size: x-small;">def display_messages(): # 메시지 출력</span></div><div><span style="font-size: x-small;"> st.subheader("Chat") </span></div><div><span style="font-size: x-small;"> for i, (msg, is_user) in enumerate(st.session_state["messages"]):</span></div><div><span style="font-size: x-small;"> message(msg, is_user=is_user, key=str(i))</span></div><div><span style="font-size: x-small;"> st.session_state["thinking_spinner"] = st.empty()</span></div><div><br /></div><div><span style="font-size: x-small;">def process_input(): # 챗 메시지 입력 </span></div><div><span style="font-size: x-small;"> if st.session_state["user_input"] and len(st.session_state["user_input"].strip()) > 0:</span></div><div><span style="font-size: x-small;"> user_text = st.session_state["user_input"].strip()</span></div><div><span style="font-size: x-small;"> with st.session_state["thinking_spinner"], st.spinner(f"Thinking"):</span></div><div><span style="font-size: x-small;"> agent_text = st.session_state["assistant"].ask(user_text) # 사용자 입력에서 답변 획득</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;"> st.session_state["messages"].append((user_text, True))</span></div><div><span style="font-size: x-small;"> st.session_state["messages"].append((agent_text, False))</span></div><div><br /></div><div><span style="font-size: x-small;">def read_and_save_file(): # file_upader UI에서 PDF 선택 시 호출</span></div><div><span style="font-size: x-small;"> st.session_state["assistant"].clear() # LLM 어시스턴스 초기화</span></div><div><span style="font-size: x-small;"> st.session_state["messages"] = []</span></div><div><span style="font-size: x-small;"> st.session_state["user_input"] = ""</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;"> for file in st.session_state["file_uploader"]:</span></div><div><span style="font-size: x-small;"> with tempfile.NamedTemporaryFile(delete=False) as tf:</span></div><div><span style="font-size: x-small;"> tf.write(file.getbuffer())</span></div><div><span style="font-size: x-small;"> file_path = tf.name</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;"> with st.session_state["ingestion_spinner"], st.spinner(f"Ingesting {file.name}"):</span></div><div><span style="font-size: x-small;"> st.session_state["assistant"].ingest(file_path) # PDF 파일을 어시스턴스에 전달</span></div><div><span style="font-size: x-small;"> os.remove(file_path)</span></div><div><br /></div><div><span style="font-size: x-small;">def page():</span></div><div><span style="font-size: x-small;"> if len(st.session_state) == 0:</span></div><div><span style="font-size: x-small;"> st.session_state["messages"] = []</span></div><div><span style="font-size: x-small;"> st.session_state["assistant"] = ChatPDF() # PDF, 벡터 데이터베이스, LLM 모델 호출 역할하는 객체 설정</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;"> # UI 정의</span></div><div><span style="font-size: x-small;"> st.header("Chat BIM") # 타이틀 </span></div><div><span style="font-size: x-small;"> st.subheader("Upload a document") # 서브헤더 </span></div><div><span style="font-size: x-small;"> st.file_uploader(</span><span style="font-size: small;">"Upload document", </span><span style="font-size: small;">type=["pdf"],</span></div><div><span style="font-size: x-small;"> key="file_uploader", </span><span style="font-size: small;">on_change=read_and_save_file,</span></div><div><span style="font-size: x-small;"> label_visibility="collapsed", </span><span style="font-size: small;">accept_multiple_files=True,</span></div><div><span style="font-size: x-small;"> ) # 업로더 </span></div><div><span style="font-size: x-small;"> st.session_state["ingestion_spinner"] = st.empty()</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;"> display_messages() # 메시지 출력</span></div><div><span style="font-size: x-small;"> st.text_input("Message", key="user_input", on_change=process_input) # 채팅 입력 버튼 생성</span></div><div><br /></div><div><span style="font-size: x-small;">if __name__ == "__main__":</span></div><div><span style="font-size: x-small;"> page()</span></div></div><div style="text-align: left;"><br /></div><div style="text-align: left;"><b>LLM 기반 챗봇 실행하기</b></div><div style="text-align: left;">다음 명령을 실행한다. </div><div style="text-align: left;">streamlit run app.py</div><div style="text-align: left;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhCvZBACj8HvDMAn00CiB3QkOFtZ2uEatBdX7UcDnt3El0lSRJ7-u7cSRLSwqNSZ9B8Lz6J1m4itATLiFO-Pgjk-izAox0GvensZmRPf5CSU9zlH4pr-F3Ztfsvbdc8mdqg10RjI_bevTZzOMWcnzgK6yU4sUo-o62dGkFg82JvQUiIVX4UUYh4N20ePbQP" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="444" data-original-width="641" height="222" src="https://blogger.googleusercontent.com/img/a/AVvXsEhCvZBACj8HvDMAn00CiB3QkOFtZ2uEatBdX7UcDnt3El0lSRJ7-u7cSRLSwqNSZ9B8Lz6J1m4itATLiFO-Pgjk-izAox0GvensZmRPf5CSU9zlH4pr-F3Ztfsvbdc8mdqg10RjI_bevTZzOMWcnzgK6yU4sUo-o62dGkFg82JvQUiIVX4UUYh4N20ePbQP" width="320" /></a></div><br /></div><div style="text-align: left;">실행되면, BIM 관련 PDF 문서를 업로드한다. 여기서는 <a href="https://www.mdpi.com/2220-9964/7/5/162">Development of a Conceptual Mapping Standard to Link Building and Geospatial Information</a> 논문 PDF파일을 사용하였다.</div><div style="text-align: left;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhatdsQOdTVJypKgndjSI70nAHijBmF8bRTGLkfcTDp2Kpc4LsP72MLyzp5QWrQSBEy_W65pUF6HK6YxA3u1mwTKTurW0uNFujPPgSABFcUBfT1zLG8DojULvhgJ8FaFOE_az8od4LbV4PvYrSxfCMmZH9bSZo3P8ZzIX2CpksHRQG-Goscj2iEBe0obx1_" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="703" data-original-width="720" height="240" src="https://blogger.googleusercontent.com/img/a/AVvXsEhatdsQOdTVJypKgndjSI70nAHijBmF8bRTGLkfcTDp2Kpc4LsP72MLyzp5QWrQSBEy_W65pUF6HK6YxA3u1mwTKTurW0uNFujPPgSABFcUBfT1zLG8DojULvhgJ8FaFOE_az8od4LbV4PvYrSxfCMmZH9bSZo3P8ZzIX2CpksHRQG-Goscj2iEBe0obx1_" width="246" /></a></div><br /></div><div style="text-align: left;">관련 질문을 하면, 증강 학습된 BIM 내용에 근거해 잘 답변하는 것을 확인할 수 있다. 이로써, BIM 전문가가 대답하는 것 같은 챗봇을 개발해 보았다. </div><div style="text-align: center;"><img alt="" data-original-height="963" data-original-width="766" height="514" src="https://blogger.googleusercontent.com/img/a/AVvXsEg9_XdnKXf5W3R7PCU3D-PyPOxBBRffZwyzZUvgtRwnU4_ikOSSRs_pWaKn3eHcyo_J5F6VhWuW5FphFyjj6QCfbWy8q64y5xW_bD_ts1LkqMTAY0bPqNFBfQQP-5tPmr8snsn-pXXqEvxvDeCY9HKNzNUASKv-eAsL1wXVNaKu8P21jOhET9vQ8V4JscAs=w408-h514" style="color: #0000ee;" width="408" /></div><div style="text-align: center;"><span style="color: #0000ee;"><div class="separator" style="clear: both; text-align: center;"><br /></div></span></div><div style="text-align: left;">참고로, OLLAMA는 로컬에서 실행될 수 있도록, 경량화되어 다음과 같이 적은 GPU 메모리를 사용한다. </div><div style="text-align: left;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEj3gd-Vx309PCPzqrtYltJw_tUcCopzwC8rsaehddpP4LoQdCD7ihmrgYUtPn9gmMlde9nMFPH0mzpvHhMuGkgUcwB8bMAu5Fx32ucgKyN0dt8zHbnOFcCThppuA4OfUhcRVtBuxksU9AsU4FMCQxhB24PwdWF-Vt3HMyBw2m0e5McNMkpEZnIKAfKfvCpg" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="360" data-original-width="823" height="176" src="https://blogger.googleusercontent.com/img/a/AVvXsEj3gd-Vx309PCPzqrtYltJw_tUcCopzwC8rsaehddpP4LoQdCD7ihmrgYUtPn9gmMlde9nMFPH0mzpvHhMuGkgUcwB8bMAu5Fx32ucgKyN0dt8zHbnOFcCThppuA4OfUhcRVtBuxksU9AsU4FMCQxhB24PwdWF-Vt3HMyBw2m0e5McNMkpEZnIKAfKfvCpg=w400-h176" width="400" /></a></div><div style="text-align: left;"><br /></div><div style="text-align: left;"><b>마무리</b></div><div style="text-align: left;"><div class="separator" style="clear: both;">이 글을 통해, 기존 BIM pdf 파일을 검색해 학습하고, LLM에 증강 학습한 후, 이를 간단한 UI로 웹서비스 하는 과정을 간략히 보여주고, 구현해 보았다. 이를 통해, 어느 정도 수준?의 BIM전문가 챗봇 시스템을 구현하였다. </div><div class="separator" style="clear: both;"><br /></div><div class="separator" style="clear: both;">앞서 언급된 라이브러리를 이용하면, 수많은 PDF파일을 미리 임베딩 벡터 데이터베이스로 만들어 놓고, 좀 더 전문적인 질의 답변을 도출할 수 있다. 이외, LangChain등의 다양한 기능을 사용하면, 좀 더 특화된 서비스 개발이 가능하다. </div><div class="separator" style="clear: both;"><br /></div></div>이와 같은 과정을 통해, 전문영역에 특화된 자체 서버에서 실행되는 로컬 LLM 서비스 개발이 가능하다.<br /><br /></div><div style="text-align: left;"><b>참고: OLLAMA기반 Image to Text LMM(Large Multi-modal Model) 사용</b></div><div style="text-align: left;">OLLAMA가 설치되어 있다면, 다양한 멀티모달 LLM 모델을 다운로드하고 실행해 볼 수 있다.</div><div style="text-align: left;"><br /></div><div style="text-align: left;">예를 들어, 고양이 사진을 주어, 이를 설명하게 하는 Image to Text를 간단히 실행해 볼 수 있다. 이는 LMM 모델인 LLaVA를 다운로드해 프롬프트를 입력하면 된다. </div><div style="text-align: left;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgoZZf7GhCYkm5Lpqn9C9kpVA8z1nw6s2CkT4nlVjgRCd4h_sV3glFc8F7Tj5qMcQL5oz-A_UurY9bzQcf6HG_Tg3MOu-m7l2DB4NLc_uQDaoe2k6n-V0EC3t4S0VudNF_BvqnDRPQEHl1yXDG4ovij3wD4XBoyMfcZbQm1eDCK68Zy-42z4iX_fPtbGB9T/s2048/cat.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1536" data-original-width="2048" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgoZZf7GhCYkm5Lpqn9C9kpVA8z1nw6s2CkT4nlVjgRCd4h_sV3glFc8F7Tj5qMcQL5oz-A_UurY9bzQcf6HG_Tg3MOu-m7l2DB4NLc_uQDaoe2k6n-V0EC3t4S0VudNF_BvqnDRPQEHl1yXDG4ovij3wD4XBoyMfcZbQm1eDCK68Zy-42z4iX_fPtbGB9T/s320/cat.jpg" width="320" /></a></div><br /></div><div style="text-align: left;">앞의 사진을 cat.jpg로 저장한 후, 다음 명령을 터미널에서 실행해 본다. </div><div style="text-align: left;">ollama run llava "describe this iamge: ./cat.jpg"</div><div style="text-align: left;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjMAyiz-HP8rkcjFD1WPg_qEARV5dZPlQtmxvf9xGjXEEaFB3x5Hwn6MqkzRWG8SjifIy1u2JKWbsie_W5Iy3lYAa_IstCakgTCVvSe767c5nynTXdRFMU3yfLrBZZRovNarakvWw70wniLKafX-8AVm_2pCXRmU8nFtRlD1LY9FRoFPa2j2yXM7A302tNx" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="50" data-original-width="696" height="35" src="https://blogger.googleusercontent.com/img/a/AVvXsEjMAyiz-HP8rkcjFD1WPg_qEARV5dZPlQtmxvf9xGjXEEaFB3x5Hwn6MqkzRWG8SjifIy1u2JKWbsie_W5Iy3lYAa_IstCakgTCVvSe767c5nynTXdRFMU3yfLrBZZRovNarakvWw70wniLKafX-8AVm_2pCXRmU8nFtRlD1LY9FRoFPa2j2yXM7A302tNx=w480-h35" width="480" /></a></div><br />그럼, 주어진 이미지를 설명하는 Text를 쉽게 얻을 수 있을 것이다.</div><div style="text-align: left;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEio6L5kbdUyJeJU8Yf3Lx3AeogPvFcV-YYZ5JRjuR7MSC6xgVH1YEtjwZDvR6CUGkZtITahOY1uDZCZZx4DKdTpxvnGTAXi1dlkuBrJNSJmjkUmPdv0CcOvIC4DgIf-uV6BXfnpnJV5bs8w5Pwtd5xYHH_RYO_bTkrbyBZa9sGEobJ7lQ-CsaRl52ZfYUSJ" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="103" data-original-width="660" height="85" src="https://blogger.googleusercontent.com/img/a/AVvXsEio6L5kbdUyJeJU8Yf3Lx3AeogPvFcV-YYZ5JRjuR7MSC6xgVH1YEtjwZDvR6CUGkZtITahOY1uDZCZZx4DKdTpxvnGTAXi1dlkuBrJNSJmjkUmPdv0CcOvIC4DgIf-uV6BXfnpnJV5bs8w5Pwtd5xYHH_RYO_bTkrbyBZa9sGEobJ7lQ-CsaRl52ZfYUSJ=w538-h85" width="538" /></a></div><div style="text-align: left;"><br /></div>글 내용은 다음과 같다.</div><div style="text-align: left;"><br /></div><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><div>"테이블 다리로 보이는 것 옆에 똑바로 앉아 있는 얼룩무늬 고양이의 이미지입니다. 고양이는 뚜렷한 어두운 줄무늬가 있는 밝은 주황색 털을 가지고 있는데, 이는 흔히 볼 수 있는 패턴입니다. 얼룩 고양이는 눈을 크게 뜨고 카메라를 정면으로 차분한 태도로 바라보고 있는 모습입니다. </div></div><div style="text-align: left;"><div>배경에는 바닥에 다음과 같은 패턴의 러그가 깔려 있습니다. 베이지색과 기타 중성색이 포함됩니다. 전경에 테이블 다리가 있기 때문에 다이닝 룸과 같은 생활 공간처럼 보입니다."</div></div></blockquote><div style="text-align: left;"><br /></div><div style="text-align: left;">거의 일치한다.</div><div style="text-align: left;"><br /></div><div style="text-align: left;"><div><b>레퍼런스</b></div><div><ul><li><a href="https://ollama.com/">Ollama</a>, Get up and running with large language models, locally.</li><li><a href="https://www.trychroma.com/">Chroma</a>, AI-native open source embedding database</li><li><a href="https://streamlit.io/">Streamlit • A faster way to build and share data apps</a></li><li><a href="https://python.langchain.com/docs/get_started/introduction">Introduction | 🦜️🔗 Langchain</a></li><li><a href="https://www.nvidia.com/en-us/ai-on-rtx/chat-with-rtx-generative-ai/">Build a Custom LLM with Chat With RTX | NVIDIA</a></li><li><a href="https://blog.streamlit.io/how-to-build-a-llama-2-chatbot/">How to build a Llama 2 chatbot (streamlit.io)</a></li><li><a href="https://medium.com/@stefnestor/python-streamlit-local-llm-2aaa75961d03">(Python) Streamlit + Local LLM. Yet-Another-Code-Example for… | by Stef Nestor | Medium</a></li><li><a href="https://medium.com/@vndee.huynh/build-your-own-rag-and-run-it-locally-langchain-ollama-streamlit-181d42805895">Build your own RAG and run it locally: Langchain + Ollama + Streamlit | by Duy Huynh | Medium</a></li><li><a href="https://medium.com/@datadrifters/fully-local-rag-with-qdrant-ollama-langchain-and-langserve-10d4dd1facbc">Fully Local RAG with Qdrant, Ollama, LangChain and LangServe | by Datadrifters | Medium</a></li><li><a href="https://medium.aiplanet.com/implementing-rag-using-langchain-ollama-and-chainlit-on-windows-using-wsl-92d14472f15d">Implementing RAG using Langchain Ollama and Chainlit on Windows using WSL | by Plaban Nayak | AI Planet</a></li><li><a href="https://medium.com/@ingridwickstevens/chat-with-your-audio-locally-a-guide-to-rag-with-whisper-ollama-and-faiss-6656b0b40a68">Chat with Your Audio Locally: A Guide to RAG with Whisper, Ollama, and FAISS | by Ingrid Stevens | Medium</a></li><li><a href="https://ai.gopubby.com/chatollama-ollama-based-100-local-rag-application-4282faaa23b2">ChatOllama | Ollama Based 100% Local RAG Application | by 01coder | Mar, 2024 | AI Advances (gopubby.com)</a></li><li><a href="https://litellm.ai/">LiteLLM</a></li></ul><div></div></div></div>Daddy Makerhttp://www.blogger.com/profile/02275758703365576780noreply@blogger.com0tag:blogger.com,1999:blog-5201956450461596914.post-33876310113094331982024-02-07T22:28:00.000-08:002024-02-24T01:49:41.522-08:00대중화된 멀티모달 생성AI 모델, Stable Diffusion 아키텍처 분석과 동작 원리 이해<div style="text-align: left;">이 글은 Stable Diffusion(스테이블 디퓨전) 아키텍처를 분석하고, 핵심 개념과 모델을 이해한다. 스테이블 디퓨전은 기존에 연구된 멀티모달(Multi Modal) 딥러닝 아키텍처인 CLIP(OpenAI), 노이즈 확산(디퓨전) 및 역확산 시뮬레이션 모델, 컴퓨터 비전에서 사용되던 U-Net, 오토인코더를 통한 데이터 압축과 잠재공간(Latent Space)차원 연산 등을 적극 사용해, Text to Image 생성AI(Gen AI) 기술을 구현한다. </div><div style="text-align: left;"><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><br /></div><div class="separator" style="clear: both;">스테이블 디퓨전은 text-to-image 생성AI 모델로 <a href="https://runwayml.com/">RunwayML</a>, <a href="https://stability.ai/">Stavility AI</a>의 지원을 받은 뮌헨하이델베르그 대학 <a href="https://ommer-lab.com/">CompViz</a> 그룹에서 개발해 동작 원리와 소스 전체를 공개하였다(2022.8. <a href="https://github.com/CompVis">GitHub</a>). 이 모델은 대중의 폭팔적인 관심을 끌며 대중에 생성AI를 각인시켰고, 이 기술을 이용한 멀티모달 생성AI(Gen AI)기술 투자의 기폭제가 되었다. 2023년에는 빅테크 기업 OpenAI, Microsoft, Google, Meta(Facebook), NVIDIA가 생성AI에 큰 투자를 했고, 그 결과 우리는 Microsoft CoPilot(LLM. Text-Image), OpenAI의 ChatGPT4(LLM. Text-Image), SORA(Text-Video), Google Gemini, Facebook LLAMA(LLM), ImageBind(Text-Image-Sound-Video)와 같은 생성AI 기술을 사용할 수 있게 되었다.</div><div class="separator" style="clear: both;"><br /></div><div class="separator" style="clear: both;">스테이블 디퓨전은 다음 그림과 같이 디퓨전 모델, U-Net, 오토인코더(Autoencoder), 트랜스포머(Transformer) 어텐션 모델(Attention)을 사용해, 학습한 모델을 역으로 계산해 주어진 텍스트 조건에서 이미지가 생성될 수 있도록 한다.</div><div class="separator" style="clear: both;"><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both;"><div class="separator" style="clear: both;"><div class="separator" style="clear: both;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEi5oFkCSpt-Id3UZ0UODP_JGxk5gIHdXGeeU0sMZiXsANJ7jJ9-BgdN0_hzbVXv-dxKaoY8GpPN31J8quWabC17Y23aVUAQ8_-H9d8W4K0j2P6SrCLTHR1fY1M4tAx5TYKlj2d6Y7h97jIdVKt9kaBk8IgWiaRbpP-Gp8DpfQnh5MnMaZ2kUE91DZGS94bi" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="430" data-original-width="874" height="314" src="https://blogger.googleusercontent.com/img/a/AVvXsEi5oFkCSpt-Id3UZ0UODP_JGxk5gIHdXGeeU0sMZiXsANJ7jJ9-BgdN0_hzbVXv-dxKaoY8GpPN31J8quWabC17Y23aVUAQ8_-H9d8W4K0j2P6SrCLTHR1fY1M4tAx5TYKlj2d6Y7h97jIdVKt9kaBk8IgWiaRbpP-Gp8DpfQnh5MnMaZ2kUE91DZGS94bi=w640-h314" width="640" /></a></div></div></div></div><div class="separator" style="clear: both; text-align: center;">스테이블 디퓨전 아키텍처 기반 Text To Image 생성(Inference) 및 학습 과정</div><div><br /></div></div><div class="separator" style="clear: both;">이 모델은 기존 자연어 처리 분야에서 개발된 트랜스포머, 컴퓨터 비전 딥러닝 모델 기술을 적극 사용한다. 이와 관련된 내용을 깊게 이해하고 싶다면 다음을 참고한다. </div><div class="separator" style="clear: both;"><ul><li><a href="https://daddynkidsmakers.blogspot.com/2017/02/blog-post_24.html" style="color: #992211; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; text-decoration-line: none;"><span style="font-size: x-small;">머신러닝 딥러닝 신경망 개념, 종류 및 개발</span></a></li><li><a href="https://daddynkidsmakers.blogspot.com/2021/10/blog-post.html" style="font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif;"><span style="font-size: x-small;">어텐션 기반 트랜스포머 딥러닝 모델 이해, 활용 사례 및 파치토치를 통한 간단한 사용방법 소개</span></a></li><li><a href="https://daddynkidsmakers.blogspot.com/2024/02/blog-post.html"><span style="font-size: x-small;">트랜스포머 디코더 핵심 코드 구현을 통한 동작 메커니즘 이해하기</span></a></li><li><a href="https://daddynkidsmakers.blogspot.com/2024/02/llama-2.html"><span style="font-size: x-small;">오픈소스 기반 LLM의 민주화, LLAMA-2 논문 분석 및 기술 요약</span></a></li><li><a href="https://daddynkidsmakers.blogspot.com/2024/02/clip.html"><span style="font-size: x-small;">생성AI 멀티모달 모델 개발의 시작. OpenAI의 CLIP모델 이해, 코드 분석, 개발, 사용하기</span></a></li><li><a href="https://github.com/mac999/computer_vision_deeplearning/tree/main" style="color: #992211; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; text-decoration-line: none;"><span style="font-size: x-small;">Computer vision deep learning: computer vision based on deep learning lecture materials, github</span></a></li><li><a href="https://daddynkidsmakers.blogspot.com/2022/11/3.html" style="color: #992211; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; text-decoration-line: none;"><span style="font-size: x-small;">딥러닝 기반 3차원 스캔 데이터, 포인트 클라우드 학습</span></a></li></ul><div>실제 코드 개발 방법을 알고 싶다면, 다음 링크를 참고한다. </div><div><ul style="text-align: left;"><li><a href="https://daddynkidsmakers.blogspot.com/2024/02/ai-stable-diffusion.html">파이토치로 멀티모달 생성AI 모델, Stable Diffusion 아키텍처를 코딩, 구현해보기</a></li></ul></div><div>스테이블 디퓨전 아키텍처 모델의 이해가 아닌, 설치 및 사용 방법만 알고 싶을 때는 아래 링크를 참고한다.</div><ul><li><a href="https://daddynkidsmakers.blogspot.com/2023/03/generative-ai.html" style="color: #992211; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; text-decoration-line: none;"><span style="font-size: x-small;">생성(Generative) AI 오픈소스 딥러닝 모델 Stable Diffusion, ControlNet 개념 및 ComfyUI 사용법</span></a></li><li><a href="https://daddynkidsmakers.blogspot.com/2023/09/llama2.html" style="font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif;"><span style="font-size: x-small;">ChatGPT와 같은 생성AI 서비스 개발을 위한 간단한 LLAMA-2 설치와 사용법</span></a></li></ul></div></div><div class="separator" style="clear: both; text-align: center;"><div style="text-align: left;">이 글은 멀티모달, 디퓨전과 관련된 다양한 문헌을 참고해 정리된 것이다. 관련 내용은 이 글의 레퍼런스를 참고한다.</div><ul style="text-align: left;"></ul></div><div class="separator" style="clear: both; text-align: left;"><b><br /></b></div><div class="separator" style="clear: both; text-align: left;"><b>아키텍처 구조</b></div><div class="separator" style="clear: both; text-align: left;">스테이블 디퓨젼은 기존에 개발된 디퓨젼(Diffusion), 벡터 임베딩(Vector Embedding), <a href="https://huggingface.co/docs/diffusers/en/tutorials/basic_training">U-Net</a>과 <a href="https://medium.com/@rekalantar/variational-auto-encoder-vae-pytorch-tutorial-dce2d2fe0f5f">VAE</a>(Variational Autoencoders), 오토인코더, 트랜스포머와 CLIP을 활용해 개발된 것이다. 스테이블 디퓨전 2는 기존 CLIP을 그대로 사용하지 않고, 입력된 텍스트를 조건으로 파라메터로 조정할 수 있도록 하였다(컨디셔닝 기법 적용). 이로 인해, 입력 텍스트에 따라 이미지 스타일을 변화시킬 수 있다. 다음 그림은 스테이블 디퓨전의 핵심기술요소만 보여준다. </div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjjZxKRp68PBPb79_hgInMCay7hnzxIlMLjhEOsPV_D1XZhHdoMfv_0UIbr3knd7_mItfp9DMWfOg1khAps7Kj-smt8BSMPGUzyX0_A829Tsz8e0Omw1twumhOGzPXT85luaBgf6b_wpvuwW-IyEktXHaSE9YUagdE3EcwdBjS8oC4_ns4ea6GmrABmI0ZW" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="295" data-original-width="376" height="240" src="https://blogger.googleusercontent.com/img/a/AVvXsEjjZxKRp68PBPb79_hgInMCay7hnzxIlMLjhEOsPV_D1XZhHdoMfv_0UIbr3knd7_mItfp9DMWfOg1khAps7Kj-smt8BSMPGUzyX0_A829Tsz8e0Omw1twumhOGzPXT85luaBgf6b_wpvuwW-IyEktXHaSE9YUagdE3EcwdBjS8oC4_ns4ea6GmrABmI0ZW" width="306" /></a></div></div></div></div><div class="separator" style="clear: both; text-align: center;">스테이블 디퓨전 아키텍처</div></div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;">스테이블 디퓨전에 텍스트가 입력되면, 텍스트 인코더가 특징을 가진 임베딩 벡터로 변환한다. 아울러, 이미지-노이즈 데이터셋으로 학습된 디퓨전 모델이 사용되어, 텍스트 임베딩 벡터와 압축된 이미지 벡터가 잠재공간(Latent Space)에 표현된다. 트랜스포머를 이용해, 유사도가 제일 큰(잠재공간에서 가까운 텍스트-이미지 쌍) 텍스트-이미지 임베딩 벡터 쌍을 계산한다. 이 결과로 이미지 특징 벡터를 얻고, 오토인코더로 특징 벡터를 이미지로 변환한다. </div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;">참고로, 유명한 딥러닝 플랫폼 개발사인 <a href="https://www.mosaicml.com/blog/train-custom-gpt-diffusion-models">MosaicML</a>에서 공개한 스테이블 디퓨전 개발 과정(2023.4.26. 참고-MosaicML의 <a href="https://github.com/mosaicml/diffusion">Stable Diffusion 소스코드</a>)은 의미있는 정보를 제공한다. 이 사례에서 Stable Diffusion 개발 시 GPU 사용 비용은 NVIDIA A100 GPU x 100개 x 1.6일, x 4.9일 동안 550,000 학습량이다. 총 23,835 A100 시간이 필요하다. 100TB 데이터셋이 학습에 사용되었다. 이를 비용으로 환산하면 $47,700 (약 6천만원) 정도가 된다. 즉, 그라운드 제로에서 스테이블 디퓨전 모델을 학습한다는 것은 어느 정도 규모와 자원이 있어야 한다는 것을 의미한다(이 과정을 <a href="https://github.com/AUTOMATIC1111">Automatic1111</a>의 <a href="https://daddynkidsmakers.blogspot.com/2023/03/generative-ai.html">WebUI</a>와 같은 도구를 이용해 스테이블 디퓨전 모델을 커스텀 데이터셋으로 전이학습하는 것과 혼동하지 말자).</div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgbupwovawKVEf4UIo3VZ-TeEuiwL4DkhkdwYNZM9rQESPWhKMWAPpRsX_JcWr9GE18Du31LAMsBSpsN2V_Pc7wjNKEA6Ha5yV__kEVKaAAcxmPanWDFIc5huZnrlF_Qth5TS7T_Bs43RK4X2dRq3WW_d9WFUzT-o69ulvVngRTpvmPc7LIYDWFhXx3U6eg" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="1258" data-original-width="2414" height="168" src="https://blogger.googleusercontent.com/img/a/AVvXsEgbupwovawKVEf4UIo3VZ-TeEuiwL4DkhkdwYNZM9rQESPWhKMWAPpRsX_JcWr9GE18Du31LAMsBSpsN2V_Pc7wjNKEA6Ha5yV__kEVKaAAcxmPanWDFIc5huZnrlF_Qth5TS7T_Bs43RK4X2dRq3WW_d9WFUzT-o69ulvVngRTpvmPc7LIYDWFhXx3U6eg=w321-h168" width="321" /></a></div><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgyJLiNUcd0yCDACq4AMpWozO20cZUT_3qnoNA1g2kIOvCKYl-f1YfptB5BrFw9eBoGwuIdORSHdMZYNkn2Y6naDwARJPgyA-HMz99vfUGYvxkQ1H4raHKP0t5BbjoYrecj3nOX-701ufcl-LMbpnnxHqAzJW-aM41YJ-c49RRh2bqancwCfgMUj40hz1wJ" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="474" data-original-width="1167" height="130" src="https://blogger.googleusercontent.com/img/a/AVvXsEgyJLiNUcd0yCDACq4AMpWozO20cZUT_3qnoNA1g2kIOvCKYl-f1YfptB5BrFw9eBoGwuIdORSHdMZYNkn2Y6naDwARJPgyA-HMz99vfUGYvxkQ1H4raHKP0t5BbjoYrecj3nOX-701ufcl-LMbpnnxHqAzJW-aM41YJ-c49RRh2bqancwCfgMUj40hz1wJ" width="320" /></a></div><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhWYAPyxMofHq6qKypdHOJRy_z3C2F6m8HPSLlaKvdXK2vS-GtJvvFtL0gZhPWQBMC2awqG9htDQYDOlwgWny-p5Q_7Z91e0bjEnmBRId5f1_9W6lMmNwU7zG45qED4KKRyIgN62gjCn0wFFm0cshrn-9PO8Rm46Kj1DPY9CFHGEkxH6H8G5oCnUIBpniu2" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="839" data-original-width="1169" height="230" src="https://blogger.googleusercontent.com/img/a/AVvXsEhWYAPyxMofHq6qKypdHOJRy_z3C2F6m8HPSLlaKvdXK2vS-GtJvvFtL0gZhPWQBMC2awqG9htDQYDOlwgWny-p5Q_7Z91e0bjEnmBRId5f1_9W6lMmNwU7zG45qED4KKRyIgN62gjCn0wFFm0cshrn-9PO8Rm46Kj1DPY9CFHGEkxH6H8G5oCnUIBpniu2" width="320" /></a></div></div></div><div class="separator" style="clear: both; text-align: center;"><span style="text-align: left;">MosaicML의 </span><a href="https://github.com/mosaicml/diffusion" style="text-align: left;">Stable Diffusion Github(소스코드</a>)</div></div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;"><b>디노이즈와 디퓨전</b></div><div class="separator" style="clear: both; text-align: left;">스테이블 디퓨전(Diffusion)에서 디퓨전은 텍스트에서 이미지를 생성하는 변환기 역할을 한다. 디퓨전 모델을 사용하여, 기존 생성AI 모델인 <a href="https://en.wikipedia.org/wiki/Generative_adversarial_network">GAN</a>(<a href="https://en.wikipedia.org/wiki/Generative_adversarial_network">Generative Adversarial Network</a>)과 같이 암시적으로 데이터를 학습하지 않고, 점진적이고 안정적으로 노이즈를 지워 새로운 샘플 이미지를 생성한다. GAN방식 대신 디퓨전을 사용하고자 했던 개발자들의 핵심 질문은 다음과 같다.</div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: center;">어떻게 무작위 이미지(데이터)에서 안정적이고 고품질 이미지를 생성, 학습할 수 있을 까? 어떻게 랜덤하게 분산된 데이터를 분산되기 전의 모습으로 되돌릴 수 있을까?</div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;">이 질문은 어떻게 생성해야할 이미지의 통계적 분포를 효과적으로 학습할 수 있는가를 묻고 있는 것이다. 기존의 GAN은 생성자, 판별자를 통해 암시적으로 이 분포를 학습하였으나, 오버피팅(Overfitting), 모드 붕괴(<a href="Mode Collapse">Mode Collapse</a>. 다양한 입력 데이터 특징이 학습되지 않아, 치우친 결과만 얻는 현상)와 같은 불안정한 부분이 있었다. GAN은 학습을 통해 안정적으로 해를 수렴한다고 보장하기 어렵다. GAN은 입력 데이터의 통계적 분포가 정규분포같이 다양성이 있지 않으면 붕괴(발산)될 수 있다. </div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjE5s-RvgrBZbrm5K8sUZ1DApG2_khRzg1d93jdlMfIWuo8oibiIdrX4wfm7dwXIU-ErcgjQ7Teiom6tjIQ8FrexVDjYBDoGXVlYkpFaUhw7xZ7NrZl9wHkwZffytSMlGaScNOV6dMej8gVerj3gjoL_uNrtk5aG63arqzYxVX8aGYAMKxnrDwjrJ1fLvMY" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="193" data-original-width="828" height="96" src="https://blogger.googleusercontent.com/img/a/AVvXsEjE5s-RvgrBZbrm5K8sUZ1DApG2_khRzg1d93jdlMfIWuo8oibiIdrX4wfm7dwXIU-ErcgjQ7Teiom6tjIQ8FrexVDjYBDoGXVlYkpFaUhw7xZ7NrZl9wHkwZffytSMlGaScNOV6dMej8gVerj3gjoL_uNrtk5aG63arqzYxVX8aGYAMKxnrDwjrJ1fLvMY=w409-h96" width="409" /></a></div><div class="separator" style="clear: both; text-align: center;">GAN의 모드 붕괴 현상 (MNIST 데이터 학습이 계속되어도 입력에 대해 동일한 결과만 얻음)</div></div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;">스테이블 디퓨전은 여기에 다음 질문을 덧붙인다. </div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: center;">어떻게 생성될 이미지의 조건을 텍스트로 조정할 수 있을까? </div><div><br /></div><div>이건 <a href="https://en.wikipedia.org/wiki/Multimodal_learning">멀티모달리티</a>(Multi-modality) 조건을 어떻게 구현할 수 있는가? 즉, 텍스트 입력 조건에 따라 연관된 다른 형태의 데이터를 어떻게 생성할 수 있느냐는 것이다.</div><div><br /></div></div><div class="separator" style="clear: both; text-align: left;">우선, 첫번째 질문은 디퓨전(확산) 현상과 관계된다. 디퓨전은 노이즈의 확산(디퓨전)을 의미한다. 이와 유사한 연구가 오래전 물리학 분야에서 있었다. 노이즈가 이미지 전체에 확산되는 과정은 분자운동에 의해 충돌하며 퍼지는 엔트로피 현상과 유사하다(<a href="https://en.wikipedia.org/wiki/Diffusion_equation">브라운</a> 운동으로 알려져 있음). 물리학자는 이 현상을 확산 물질 밀도가 변화하는 속도를 시간과 위치에 의한 편미분함수로 정의할 수 있음을 증명했다. 이 함수는 미세입자 동역학 해석을 위해 개발된 랑주뱅 동역학 모델(<a href="https://en.wikipedia.org/wiki/Langevin_equation">Langevin equation</a>, 1908)에서 설명되었다(아래 자료 참고. 특정 상황에서는 열 방정식으로 해석). </div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEg_b1FL-OhsxIZ-oA9Yy4Jv1RBjGK59eRpUmHN_J_jthWdGIR2scpo3A44p0nXS5NRe_TTyYs0wUJusBA_Xn2-lHyCtBT8Wb4VycBrcpcAJoYjjaR1bkWuUgGqjdTSB0Rfw4NdFm44howpaLGu_XkZN7_IuBxUG-4wx96-2lf4u5sA8FSM-1BsVfu_dyyXW" style="margin-left: 1em; margin-right: 1em;"></a><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEg_b1FL-OhsxIZ-oA9Yy4Jv1RBjGK59eRpUmHN_J_jthWdGIR2scpo3A44p0nXS5NRe_TTyYs0wUJusBA_Xn2-lHyCtBT8Wb4VycBrcpcAJoYjjaR1bkWuUgGqjdTSB0Rfw4NdFm44howpaLGu_XkZN7_IuBxUG-4wx96-2lf4u5sA8FSM-1BsVfu_dyyXW" style="margin-left: 1em; margin-right: 1em;"></a><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiqCcccyB-miqScLvOMyyy0m26o53nXBYD1zxMO6G4Wy54he9notK5yQ7uAH8PfPYaLsobHTvq8NLZUNgVHsEJV8CkxufRIUE1_sxeVfR5iNqJLYf0w4G1PZzRQfCewWSTb06qZZGOG_Y2MfLPx3h6Zp8kFME1kwAthwQ3Z0lCaxkQTQ1qqQaLqQT8-s9y-/s573/brownianmotion.gif" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="573" data-original-width="570" height="167" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiqCcccyB-miqScLvOMyyy0m26o53nXBYD1zxMO6G4Wy54he9notK5yQ7uAH8PfPYaLsobHTvq8NLZUNgVHsEJV8CkxufRIUE1_sxeVfR5iNqJLYf0w4G1PZzRQfCewWSTb06qZZGOG_Y2MfLPx3h6Zp8kFME1kwAthwQ3Z0lCaxkQTQ1qqQaLqQT8-s9y-/w166-h167/brownianmotion.gif" width="166" /></a><a href="https://blogger.googleusercontent.com/img/a/AVvXsEg_b1FL-OhsxIZ-oA9Yy4Jv1RBjGK59eRpUmHN_J_jthWdGIR2scpo3A44p0nXS5NRe_TTyYs0wUJusBA_Xn2-lHyCtBT8Wb4VycBrcpcAJoYjjaR1bkWuUgGqjdTSB0Rfw4NdFm44howpaLGu_XkZN7_IuBxUG-4wx96-2lf4u5sA8FSM-1BsVfu_dyyXW" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="594" data-original-width="790" height="169" src="https://blogger.googleusercontent.com/img/a/AVvXsEg_b1FL-OhsxIZ-oA9Yy4Jv1RBjGK59eRpUmHN_J_jthWdGIR2scpo3A44p0nXS5NRe_TTyYs0wUJusBA_Xn2-lHyCtBT8Wb4VycBrcpcAJoYjjaR1bkWuUgGqjdTSB0Rfw4NdFm44howpaLGu_XkZN7_IuBxUG-4wx96-2lf4u5sA8FSM-1BsVfu_dyyXW=w224-h169" width="224" /></a></div></div></div><div class="separator" style="clear: both; text-align: center;">브라운 운동(<a href="https://www.miniphysics.com/brownian-motion.html">Brownian Motion - Learn Physics From The Physics Authority</a>)과 확산 방정식(Brownian motion's diffusion equation. <a href="https://www.youtube.com/watch?app=desktop&v=P9qar8mv3Tk" style="text-align: left;">Diffusion Equation - Derivation and Explanation using Brownian</a>)</div><div class="separator" style="clear: both; text-align: center;"><br /></div></div><div class="separator" style="clear: both; text-align: left;">스테이블 디퓨전은 이 개념을 생성AI에 사용한다. 디퓨전 모델은 다음의 순방향 노이즈 확산 방정식으로 시작한다.</div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhSFPdaJn6bPd_SlqANrhO4ltrKHQ0gkz-bxFDgLRHoLQ-s4vxLoX3Bkk6DabJSeYNDkMYvDBPzv4mI52YTiAJrDGGTzkV908B6oQo2M5-nH8GTLCvwdxRwvmsL92RhfpwQM1QIcwO-D747QmPKQFjq4ySiApbcm1zELdKIxZreD6LCvonE-xbTk_UaUXF8" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="108" data-original-width="998" height="31" src="https://blogger.googleusercontent.com/img/a/AVvXsEhSFPdaJn6bPd_SlqANrhO4ltrKHQ0gkz-bxFDgLRHoLQ-s4vxLoX3Bkk6DabJSeYNDkMYvDBPzv4mI52YTiAJrDGGTzkV908B6oQo2M5-nH8GTLCvwdxRwvmsL92RhfpwQM1QIcwO-D747QmPKQFjq4ySiApbcm1zELdKIxZreD6LCvonE-xbTk_UaUXF8=w280-h31" width="280" /></a></div></div></div><div class="separator" style="clear: both; text-align: center;">여기서, </div><div class="separator" style="clear: both; text-align: center;">x(t) = 잡음</div><div class="separator" style="clear: both; text-align: center;">t = 확산시간</div><div class="separator" style="clear: both; text-align: center;">σ(t) = 잡음강도</div><div class="separator" style="clear: both; text-align: center;">Δt = 확산단위시간</div><div class="separator" style="clear: both; text-align: center;">r≈N(0,1) = 표준 정규 확률 변수</div><div class="separator" style="clear: both; text-align: center;"><br /></div>방정식의 의미는 단위시간이 지날수록 이전 잡음 수준에 비례하고, 표준정규확률에 따라 잡음강도가 비례하여, 노이즈가 증가한다는 것이다(당연하다). 만약, 1차원 공간에서 이를 그대로 코딩한다면, 다음과 같을 것이다. </div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;"><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px;"><div><div class="separator" style="clear: both;"><div class="separator" style="clear: both; text-align: center;">x[i + 1] = x[i] + random_normal * noise_strength</div><div class="separator" style="clear: both;"> </div></div></div></blockquote></div><div class="separator" style="clear: both; text-align: left;">이를 역방향으로 되돌릴 수 있다면, 임의의 노이즈된 데이터에서 원본 이미지를 생성할 수 있다. </div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;">이를 위해, 전방향 확산 방정식을 미분방정식으로 변환하여 역확산 방정식을 유도한다(딥러닝 모델의 가중치는 함수의 미분값을 반복적으로 조정하는 과정임을 떠올리자). 이를 수식으로 표현하면 다음과 같다(<a href="https://towardsdatascience.com/diffusion-models-91b75430ec2">유도과정 참고</a>).</div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEj3THQi9iQClH5DfkVLqogq9BGoK6BzaP8Zzmn4QGGkfWADN55awlYG2PfblbGhwJufksk-RiJz8XVh5fUDeRhf0VawunRqGYD5bqkPQbm6v2TtfeJkP59sGnghAZMhl5KjnY-2d5HEeBTbpFx9LE0xrCHp6Tie9aNAsPTgHc1G0JCHPIX8dYTThJ1zNdtq" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="219" data-original-width="994" height="108" src="https://blogger.googleusercontent.com/img/a/AVvXsEj3THQi9iQClH5DfkVLqogq9BGoK6BzaP8Zzmn4QGGkfWADN55awlYG2PfblbGhwJufksk-RiJz8XVh5fUDeRhf0VawunRqGYD5bqkPQbm6v2TtfeJkP59sGnghAZMhl5KjnY-2d5HEeBTbpFx9LE0xrCHp6Tie9aNAsPTgHc1G0JCHPIX8dYTThJ1zNdtq=w491-h108" width="491" /></a></div><div class="separator" style="clear: both; text-align: left;"><br /></div>수식은 전체 고정된 T 시간을 가정해, 유한하게 계산되도록 한다. 수식 의미는 다음과 같다. </div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;">t + Δt 의 잡음 x()는 이전 시간의 잡음 x(t)에 비례한다(첫번째항). 아울러, 잡음강도 <span style="text-align: center;">σ(T - t) 제곱에 비례하는 데, 이는 잡음 확률을 나타내는 함수 log p(x) 함수의 미분값과 </span>Δt에 비례한다(두번째항). 또한, 잡음강도 <span style="text-align: center;">σ(T - t), </span>Δt, 정규확률분포 r에 비례한다. 두번째 항을 간략히 표현하면, s(x, t) 함수로 정리된다. 여기서, s(x, t) 함수를 diffusion score 함수라 한다.</div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;">다음 그림은 x함수 값의 전방향, 역방향 확산 과정을 t시간에 따라 출력한 것이다. </div><div class="separator" style="clear: both;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEg0yVzA7SrKd37R3qHKWiTQHabWVZA_irZdu2M7w6F3Ef9TGrz764RYmVd0M_rjyntt20gCuAJrbjuVF79lj33RaMn8eaFzDnGjvRDWr3-UDK-xmLUm2iYIpxsYTfMZKi3HVSNfAQo4WalZVsbFFuYMeXRSJ4FOXdUkZeMipr74m2V8p9FAMBp8ewrF0aUm" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="437" data-original-width="1313" height="190" src="https://blogger.googleusercontent.com/img/a/AVvXsEg0yVzA7SrKd37R3qHKWiTQHabWVZA_irZdu2M7w6F3Ef9TGrz764RYmVd0M_rjyntt20gCuAJrbjuVF79lj33RaMn8eaFzDnGjvRDWr3-UDK-xmLUm2iYIpxsYTfMZKi3HVSNfAQo4WalZVsbFFuYMeXRSJ4FOXdUkZeMipr74m2V8p9FAMBp8ewrF0aUm=w569-h190" width="569" /></a></div><div class="separator" style="clear: both; text-align: center;">전방향(좌), 역방향(우) 확산 함수 x(t) 결과(<a href="https://arxiv.org/abs/2011.13456" style="text-align: left;">Score-Based Generative Modeling through Stochastic Differential Equations)</a></div><div class="separator" style="clear: both; text-align: center;"><br /></div></div></div><div class="separator" style="clear: both; text-align: left;">전확산, 역확산 시뮬레이션은 다음과 같이 구현 될 수 있다. </div><div class="separator" style="clear: both; text-align: left;"><div><div class="separator" style="clear: both; text-align: center;"><div style="text-align: left;"><span style="font-size: x-small;">def noise_strength_constant(t): # 노이즈 강도 함수 리턴값은 1로 설정</span></div><div style="text-align: left;"><div><span style="font-size: x-small;"> return 1</span></div><div><span style="font-size: x-small;"><br /></span></div></div></div></div><div><div class="separator" style="clear: both; text-align: center;"><div style="text-align: left;"><div><span style="font-size: x-small;">def forward_diffusion_1D(x0, noise_strength_fn, t0, nsteps, dt):</span></div><div><span style="font-size: x-small;"> x = np.zeros(nsteps + 1) # 시간에 대한 노이즈 샘플 흐름을 담기 위한 벡터 생성</span></div><div><span style="font-size: x-small;"> x[0] = x0 # 초기 x0 샘플 설정</span></div><div><span style="font-size: x-small;"> t = t0 + np.arange(nsteps + 1) * dt # 샘플링되는 t시간 계산</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;"> for i in range(nsteps): # 디퓨전 시뮬레이션(Euler-Maruyama 식 사용)</span></div><div><span style="font-size: x-small;"> noise_strength = noise_strength_fn(t[i]) # t시간 잡음 강도 </span></div><div><span style="font-size: x-small;"> random_normal = np.random.randn() # 정규분포 잡음</span></div><div><span style="font-size: x-small;"> x[i + 1] = x[i] + random_normal * noise_strength # 각 시간마다 잡음 계산</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;"> return x, t # 잡음 벡터, 해당 시간 시점 리턴</span></div></div><div style="text-align: left;"><br /></div><div style="text-align: left;"><div><div class="separator" style="clear: both; text-align: center;"><div style="text-align: left;">여기서, </div></div></div><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px;"><div><div class="separator" style="clear: both; text-align: center;"><div style="text-align: left;">x0: 초기 샘플 데이터값</div></div></div><div><div class="separator" style="clear: both; text-align: center;"><div style="text-align: left;">noise_strength_fn: 노이즈 강도 함수. 시간 t입력.</div></div></div><div><div class="separator" style="clear: both; text-align: center;"><div style="text-align: left;">t0: 초기 시간 단계</div></div></div><div><div class="separator" style="clear: both; text-align: center;"><div style="text-align: left;">nsteps: 확산 단계</div></div></div><div><div class="separator" style="clear: both; text-align: center;"><div style="text-align: left;">dt: 시간 미분치</div></div></div><div><div class="separator" style="clear: both; text-align: center;"><div style="text-align: left;">x: nstep * dt 시간의 노이즈 샘플값(벡터) 리턴</div></div></div><div><div class="separator" style="clear: both; text-align: center;"><div style="text-align: left;">t: nstep * dt 시간 리턴</div></div></div></blockquote><div><div class="separator" style="clear: both; text-align: center;"><div style="text-align: left;"><div>이다. </div><div><br /></div><div>순방향 확산을 앞서 정의된 함수로 시뮬레이션해본다. </div><div><div><span style="font-size: x-small;">nsteps = 100 # 확산 최대 단계 N</span></div><div><span style="font-size: x-small;">t0 = 0 # 초기 시간</span></div><div><span style="font-size: x-small;">dt = 0.1 # 단위 시간</span></div><div><span style="font-size: x-small;">noise_strength_fn = noise_strength_constant # 노이즈 강도</span></div><div><span style="font-size: x-small;">x0 = 0 # 초기 샘플링 값</span></div><div><span style="font-size: x-small;">num_tries = 7 # 가시화할 단계수</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;"># 확산 그래프 출력 </span></div><div><span style="font-size: x-small;">plt.figure(figsize=(15, 5))</span></div><div><span style="font-size: x-small;">for i in range(num_tries): # 확산 시뮬레이션</span></div><div><span style="font-size: x-small;"> x, t = forward_diffusion_1D(x0, noise_strength_fn, t0, nsteps, dt)</span></div><div><span style="font-size: x-small;"> plt.plot(t, x, label=f'Trial {i+1}') # 확산값 출력</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;">plt.xlabel('Time', fontsize=20)</span></div><div><span style="font-size: x-small;">plt.ylabel('Sample Value ($x$)', fontsize=20)</span></div><div><span style="font-size: x-small;">plt.title('Forward Diffusion Visualization', fontsize=20)</span></div><div><span style="font-size: x-small;">plt.legend()</span></div><div><span style="font-size: x-small;">plt.show()</span></div></div><div><br /></div><div>결과는 다음과 같다.</div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEiTK2hxvVbJ4xrhaTZr-rK0sUbiarJkfarlWYsmGLIO6LWVzCO53UT0cjya7l531Mdc49BhPVVTkRy4x1CDyocsEGsYe_OReoXrVaTDnymUkIg_JwRRYmWdaggMFq7wX7_W-Mr_RaqVwQsGlot3QnAB7laFzDIF35iTrNo3L-cntnUC7o2zQzyXbINhZ2vb" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="597" data-original-width="1569" height="153" src="https://blogger.googleusercontent.com/img/a/AVvXsEiTK2hxvVbJ4xrhaTZr-rK0sUbiarJkfarlWYsmGLIO6LWVzCO53UT0cjya7l531Mdc49BhPVVTkRy4x1CDyocsEGsYe_OReoXrVaTDnymUkIg_JwRRYmWdaggMFq7wX7_W-Mr_RaqVwQsGlot3QnAB7laFzDIF35iTrNo3L-cntnUC7o2zQzyXbINhZ2vb=w400-h153" width="400" /></a></div><br />이제 역방향 확산 함수를 구현한다. </div><div><br /></div><div>앞서 설명한 바와 같이, 순방향은 정규분포를 이용할 수 있지만, 역방향은 이를 그대로 사용할 수 없어, 다음 편미분 방정식으로 근사화해 구현한다. </div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhSEo8rcEmPWKShdloeNitSAtnrYscaBaPbdHKrJpuCEmYceCqscBxZEb-SkYDjHjLE_BUk8gHaLgs3D3v5fLrVK0z60ec8bKleKNyewqnUkAAdCzvRLZ82RMbX9WpdEFAbJCv9sxNx7TeHt2t51-dpn9h68wcQGxMXsqOmlsYQpGf1TrkT-5XHOj7zR234" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="185" data-original-width="834" height="106" src="https://blogger.googleusercontent.com/img/a/AVvXsEhSEo8rcEmPWKShdloeNitSAtnrYscaBaPbdHKrJpuCEmYceCqscBxZEb-SkYDjHjLE_BUk8gHaLgs3D3v5fLrVK0z60ec8bKleKNyewqnUkAAdCzvRLZ82RMbX9WpdEFAbJCv9sxNx7TeHt2t51-dpn9h68wcQGxMXsqOmlsYQpGf1TrkT-5XHOj7zR234=w476-h106" width="476" /></a></div><div>여기서, s(x, t)는 score 함수로 알려져 있다. 이를 구현할 수 있다면, 역방향 확산이 가능하다. 시작점 x0=0이고, 잡음 강도가 일정하다면, s() 함수는 다음과 같다. </div><div style="text-align: center;"><div class="separator" style="clear: both;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjN40-LPdymWVbq1ic1D0Sw80MnWNTS0mtTklVqqVVV9bKntKi3WKWVtU1aF3GdLcfPMTaxEdsCdOgO5-UZVdU1Jn8pVh1yJ1ssgv9TYQPn9r60zqQHSFgJev0bvXMH61liIWabRfwGlVFg6aguRXVD9FnIDnsF_vePiRggdZu-8JolFT5_Ae-M8_YCIGcq" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="84" data-original-width="812" height="33" src="https://blogger.googleusercontent.com/img/a/AVvXsEjN40-LPdymWVbq1ic1D0Sw80MnWNTS0mtTklVqqVVV9bKntKi3WKWVtU1aF3GdLcfPMTaxEdsCdOgO5-UZVdU1Jn8pVh1yJ1ssgv9TYQPn9r60zqQHSFgJev0bvXMH61liIWabRfwGlVFg6aguRXVD9FnIDnsF_vePiRggdZu-8JolFT5_Ae-M8_YCIGcq" width="320" /></a></div></div><div><br /></div>이를 코드로 다음과 같이 구현한다.</div><div><div><span style="font-size: x-small;">def score_simple(x, x0, noise_strength, t):</span></div><div><span style="font-size: x-small;"> score = - (x - x0) / ((noise_strength**2) * t) # s(x,t) 스코어 함수</span></div><div><span style="font-size: x-small;"> return score</span></div></div><div><span style="font-size: x-small;"><br /></span></div><div><div><span style="font-size: x-small;">def reverse_diffusion_1D(x0, noise_strength_fn, score_fn, T, nsteps, dt):</span></div><div><span style="font-size: x-small;"> x = np.zeros(nsteps + 1) # 잡음 경로 벡터 리스트</span></div><div><span style="font-size: x-small;"> x[0] = x0 # 잡음 초기값</span></div><div><span style="font-size: x-small;"> t = np.arange(nsteps + 1) * dt # 잡음 생성 시점 계산</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;"> # Euler-Maruyama 식 사용해 역확산 시뮬레이션</span></div><div><span style="font-size: x-small;"> for i in range(nsteps): </span></div><div><span style="font-size: x-small;"> noise_strength = noise_strength_fn(T - t[i]) # 노이즈 강도</span></div><div><span style="font-size: x-small;"> score = score_fn(x[i], 0, noise_strength, T - t[i]) # s(x,t) 스코어 함수</span></div><div><span style="font-size: x-small;"> random_normal = np.random.randn() # 랜덤값</span></div><div><span style="font-size: x-small;"> x[i + 1] = x[i] + score * noise_strength**2 * dt + noise_strength * random_normal * np.sqrt(dt) # 잡음 역확산</span></div><div><span style="font-size: x-small;"> return x, t</span></div><br /></div><div>이제, 시뮬레이션하여, 출력해 본다. </div><div><div><span style="font-size: x-small;">nsteps = 100</span></div><div><span style="font-size: x-small;">t0 = 0</span></div><div><span style="font-size: x-small;">dt = 0.1</span></div><div><span style="font-size: x-small;">noise_strength_fn = noise_strength_constant</span></div><div><span style="font-size: x-small;">score_fn = score_simple</span></div><div><span style="font-size: x-small;">x0 = 0</span></div><div><span style="font-size: x-small;">T = 11</span></div><div><span style="font-size: x-small;">num_tries = 7</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;">plt.figure(figsize=(15, 5))</span></div><div><span style="font-size: x-small;">for i in range(num_tries):</span></div><div><span style="font-size: x-small;"> x0 = np.random.normal(loc=0, scale=T) # 타임T, 강도1 랜덤값</span></div><div><span style="font-size: x-small;"> x, t = reverse_diffusion_1D(x0, noise_strength_fn, score_fn, T, nsteps, dt)</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;"> plt.plot(t, x, label=f'Trial {i+1}') </span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;">plt.xlabel('Time', fontsize=20)</span></div><div><span style="font-size: x-small;">plt.ylabel('Sample Value ($x$)', fontsize=20)</span></div><div><span style="font-size: x-small;">plt.title('Reverse Diffusion', fontsize=20)</span></div><div><span style="font-size: x-small;">plt.legend()</span></div><div><span style="font-size: x-small;">plt.show()</span></div></div><div><br /></div><div>역확산 효과가 잘 시뮬레이션되었다.</div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgv3ja6OkICqfOoWmBfH_6DQ4YWLSHfVLNtglYhAHGlInKY3Bq9vYbPjzk086qKlvWnIMPZEFYpnVkM1QTOsssz0e3sp_xCoKjQ3mcx4TZk6CBwFAdQT7nojmf7FwxiPmsVnEZBmrRCGdiLNFuxHv-rWN1towt5GHfbfSeMpPhIdCiYXUCKdvE7SG_5lrIB" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="593" data-original-width="1579" height="150" src="https://blogger.googleusercontent.com/img/a/AVvXsEgv3ja6OkICqfOoWmBfH_6DQ4YWLSHfVLNtglYhAHGlInKY3Bq9vYbPjzk086qKlvWnIMPZEFYpnVkM1QTOsssz0e3sp_xCoKjQ3mcx4TZk6CBwFAdQT7nojmf7FwxiPmsVnEZBmrRCGdiLNFuxHv-rWN1towt5GHfbfSeMpPhIdCiYXUCKdvE7SG_5lrIB=w400-h150" width="400" /></a></div></div></div></div></div></div></div></div></div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;">이런 개념을 이용하면, 순방향 디퓨전, 역방향 디퓨전 방식을 이용해, 다양하고 복잡한 이미지(노이즈)에서 원본 이미지로 변환하거나, 반대로 역변환할 수 있다(참고. <a href="https://arxiv.org/abs/2011.13456" style="text-align: center;">Score-Based Generative Modeling</a><span style="text-align: center;">, 2011)</span>. </div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhbU8pucYWXCJhiDc0V7abyQv_XSqV4Cubv_tnIjuSTbpZooLy16jRsBhG9KKKZ95ccpHPOPw5vJQEC1HzykRWliSESyKg1jd8ha2oOVNpT7HNRtqAPJmWev_JlJ9IJSIEtAy5TJgk9ZYE93wqEbB2mk3hXdghVGlH5fn-jXsvvZqNu6seB9By1gvzNRhxb" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="366" data-original-width="834" height="187" src="https://blogger.googleusercontent.com/img/a/AVvXsEhbU8pucYWXCJhiDc0V7abyQv_XSqV4Cubv_tnIjuSTbpZooLy16jRsBhG9KKKZ95ccpHPOPw5vJQEC1HzykRWliSESyKg1jd8ha2oOVNpT7HNRtqAPJmWev_JlJ9IJSIEtAy5TJgk9ZYE93wqEbB2mk3hXdghVGlH5fn-jXsvvZqNu6seB9By1gvzNRhxb=w428-h187" width="428" /></a></div></div></div><div class="separator" style="clear: both; text-align: center;">확률적 미분 디퓨전 방정식(SDE. Stochastic Differential Equation) 모델(Fareed Khan, 2024. <a href="https://arxiv.org/abs/2011.13456">Score-Based Generative Modeling</a>, 2011)</div><br /></div><div class="separator" style="clear: both; text-align: left;">이 역변환 디퓨전 방정식은 디노이징 디퓨전 모델(Denoising Diffusion Model)이라 불린다. 디노이징 디퓨전 모델은 DDPM(Denoising Diffusion Probabilistic Models)기술로 알려져 있고(<a href="https://arxiv.org/pdf/2006.11239.pdf">Jonathan Ho et al</a>, 2020), 스테이블 디퓨전에서 적극 사용된다(참고 - <a href="https://github.com/hojonathanho/diffusion">소스코드</a>). </div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEiT8kjmA0eLr8RTgogZv9Gia-tg1z6Lb_HcMBCZV-zu-ueOSSIbl198HbhC-YzV5iBywyYSOpbVtKucglM3Ht59aE8T82rQbHZ1xTHA-mjXEiX6euly00bGcspQ_wpuaX8zPB_GKJjGhfK3JXP9gimSo5XReBAhEJSssMusyauRR4XRvVjGze713XHFWnZx" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="281" data-original-width="1170" height="152" src="https://blogger.googleusercontent.com/img/a/AVvXsEiT8kjmA0eLr8RTgogZv9Gia-tg1z6Lb_HcMBCZV-zu-ueOSSIbl198HbhC-YzV5iBywyYSOpbVtKucglM3Ht59aE8T82rQbHZ1xTHA-mjXEiX6euly00bGcspQ_wpuaX8zPB_GKJjGhfK3JXP9gimSo5XReBAhEJSssMusyauRR4XRvVjGze713XHFWnZx=w639-h152" width="639" /></a></div><div class="separator" style="clear: both; text-align: center;"><span style="text-align: left;">DDPM(Denoising Diffusion Probabilistic Models. </span><a href="https://arxiv.org/pdf/2006.11239.pdf" style="text-align: left;">Jonathan Ho et al</a><span style="text-align: left;">, 2020</span><span style="text-align: left;">)</span></div></div><div class="separator" style="clear: both;"><br /></div></div></div><div style="text-align: left;"><div class="separator" style="clear: both; text-align: left;">디퓨전 함수를 이미지 데이터에 적용하면, t 시간에 따른 노이즈 양을 조정할 수 있다. 다음 그림은 그 과정을 보여준다. 디퓨전 함수에 t0를 입력하면 원본 이미지, T를 입력하면, 노이즈 이미지가 계산된다.</div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhxNTIZWZFf2poItNjZVBG_KDzRLd70l9UgV7ShlgmQi8KRwp1oO6yfkw7HDoU7Trd2ZyNXtjtFGVP7EqRFXCADgPJr1h0-v5E8eTTnUUV7-_H4DNKn47BzMnCdNlvLC8rY5Wi3SpaqMKofaemkzY3ZfNN6AZwJWMnw34CONsQD4EiY-Gbf6vQghbWevIjk" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="140" data-original-width="800" height="83" src="https://blogger.googleusercontent.com/img/a/AVvXsEhxNTIZWZFf2poItNjZVBG_KDzRLd70l9UgV7ShlgmQi8KRwp1oO6yfkw7HDoU7Trd2ZyNXtjtFGVP7EqRFXCADgPJr1h0-v5E8eTTnUUV7-_H4DNKn47BzMnCdNlvLC8rY5Wi3SpaqMKofaemkzY3ZfNN6AZwJWMnw34CONsQD4EiY-Gbf6vQghbWevIjk=w477-h83" width="477" /></a></div></div><div class="separator" style="clear: both; text-align: center;">노이징(Noising) 및 디노이징(Denoising) 과정 개념(<span style="text-align: left;">Jay Alammar, 2022)</span></div></div></div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;"><b>U-Net과 디퓨전</b></div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;">전방향 역방향 디퓨전 함수의 대상은 이미지가 되어야 한다. 픽셀 공간을 다른 픽셀 값으로 변환하는 것이기 때문에, 이미지의 입력 픽셀이 라벨링된 출력 픽셀로 계산되어야 한다. 그러므로, 이 기능은 이미지 세그먼테이션에 많이 사용된 U-Net 모델을 사용한다. 디퓨전 함수와 U-Net을 통해, 노이즈-이미지 간 변환 및 역변환 맵핑을 하도록 설계한다.</div><div class="separator" style="clear: both;"><div class="separator" style="clear: both; text-align: center;"><br /></div></div><div class="separator" style="clear: both;">디퓨전은 시간의 함수이므로, 주어진 데이터의 해를 점진적으로 학습(가중치 조정)할 수 있게 되고, 이는 스테이블한(안정적인) 학습 모델을 얻을 수 있다는 것을 의미한다. 디퓨전 함수를 이용해 각 에폭(Epoch) 당 학습할 다양한 잡음 있는 데이터셋을 만든다. s(x, t)에서 t값을 임의로 할당해, 입력 이미지에 다양한 변형을 하여, 미니배치 데이터를 생성한다. 다음 그림은 이를 보여준다. </div><div class="separator" style="clear: both;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEg-N8oZZnsE9B2laelJg8g1eOfvBNQ8iulRxI5uG27RtbgTIlxsM5uYEot296xgkWO8WBJ4L7OJEEoXcVJNsJ9xSMQe4DVfuydfAHONeQtd5S1f2VdS5bsxFQewn7wD39HEy1QTZVMEFLj_7WZeJI-QCRGQdjDyfjeD9mZwYbwLAotEWMAcQTtRG-DVN0Iz" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="983" data-original-width="1100" height="357" src="https://blogger.googleusercontent.com/img/a/AVvXsEg-N8oZZnsE9B2laelJg8g1eOfvBNQ8iulRxI5uG27RtbgTIlxsM5uYEot296xgkWO8WBJ4L7OJEEoXcVJNsJ9xSMQe4DVfuydfAHONeQtd5S1f2VdS5bsxFQewn7wD39HEy1QTZVMEFLj_7WZeJI-QCRGQdjDyfjeD9mZwYbwLAotEWMAcQTtRG-DVN0Iz=w400-h357" width="400" /></a></div><div class="separator" style="clear: both; text-align: center;">UNet에 입력되는 학습 데이터셋(Steins, 2023)</div><div class="separator" style="clear: both;"><br /></div>각 학습 단계는 다음과 같은 순서로 진행된다. </div><div class="separator" style="clear: both;"><ol><li>t 시간 단계 임베딩 벡터 변환</li><li>t 시간에 따른 노이즈 이미지 생성</li><li>1, 2를 입력데이터, 노이즈를 라벨링 데이터로 하여, U-Net 모델 학습</li></ol></div><div class="separator" style="clear: both;">입력 이미지-텍스트 학습 데이터를 노이즈 이미지 라벨으로 학습하고, 역계산하는 방식으로 이미지를 생성한다. 다음 그림은 이를 보여준다.</div></div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjAKOC3JSRPEqccPWpwJ7T3JTrJQG08L4rcADtVFF4eMAPM8FDgDAkQK3ZTi28_JlqpSnL7aE-ZzeNG0op8xeUwkzJ8sGYvFvjuV6ruMyD48gRfRetciBG2SLV8DUdnEokHP818LvkFPR_DC9sTFGBf3KqD14WX-K_GSDmeELq8zGorDlVMAM2JFSEiZ9Sc" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="728" data-original-width="1100" height="287" src="https://blogger.googleusercontent.com/img/a/AVvXsEjAKOC3JSRPEqccPWpwJ7T3JTrJQG08L4rcADtVFF4eMAPM8FDgDAkQK3ZTi28_JlqpSnL7aE-ZzeNG0op8xeUwkzJ8sGYvFvjuV6ruMyD48gRfRetciBG2SLV8DUdnEokHP818LvkFPR_DC9sTFGBf3KqD14WX-K_GSDmeELq8zGorDlVMAM2JFSEiZ9Sc=w433-h287" width="433" /></a></div><div class="separator" style="clear: both; text-align: center;">디퓨전에서 사용하는 U-Net 구조와 역할(<a href="https://medium.com/@steinsfu/stable-diffusion-clearly-explained-ed008044e07e">Steins</a>, 2023)</div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;">참고로, U-Net은 입력 픽셀 값을 압축해 일반화(추상화)하여, 라벨링된 픽셀 값으로 계산하는 학습 모델로 딥러닝 컴퓨터 비전 분야에서 이미지 세그먼테이션에서 사용되었다. 다음은 32x32 이미지를 목표 라벨 이미지로 학습하기 위한 U-Net 모델을 보여준다. 일반화된 이미지 특징 학습으로 인해, 이미지 세부 특징을 놓칠 수 있어, 각 계층마다 ResNet 잔차연결을 사용한 것을 확인할 수 있다.</div></div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhNm1VhRHAJMhgMXbTJFDSzT-4F6cmQPVZwAyrjwR0UheBT6uM__wX9hbooGZwvaB18cLF-ugd8PxTCHok1ikXavYpk8gQiSJwNEiX5asq2yQGzEQT_BsoqiF5WcPbRtSEsOBEwlMcpO-ws6F56I2y89znCYtrAnA9mSo2cteGT4O08w8FgUsl5FeqaukcL" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="834" data-original-width="1100" height="240" src="https://blogger.googleusercontent.com/img/a/AVvXsEhNm1VhRHAJMhgMXbTJFDSzT-4F6cmQPVZwAyrjwR0UheBT6uM__wX9hbooGZwvaB18cLF-ugd8PxTCHok1ikXavYpk8gQiSJwNEiX5asq2yQGzEQT_BsoqiF5WcPbRtSEsOBEwlMcpO-ws6F56I2y89znCYtrAnA9mSo2cteGT4O08w8FgUsl5FeqaukcL" width="317" /></a></div><div class="separator" style="clear: both; text-align: center;">U-Net과 ResNet 모델 구조(<a href="https://tree.rocks/make-diffusion-model-from-scratch-easy-way-to-implement-quick-diffusion-model-e60d18fd0f2e">Seachaos</a>, 2023)</div><br /></div><div class="separator" style="clear: both; text-align: left;"><b>잠재공간과 오토인코더</b></div><div class="separator" style="clear: both; text-align: left;">입력 이미지를 그대로 디퓨전과 U-Net으로 계산한다면, 이미지 크기에 따라 매우 큰 GPU 메모리와 계산 성능이 필요할 것이다. 이를 잠재공간(Latent Space)에서 표현하면, 메모리와 연산량을 크게 줄일 수 있다.</div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;">잠재공간은 데이터 특징이 압축된 벡터를 표현하는 다차원 공간이다. 이 공간은 입력 데이터가 압축된 벡터로 차원을 가진다. <a href="https://medium.com/@rekalantar/variational-auto-encoder-vae-pytorch-tutorial-dce2d2fe0f5f">VAE</a>(Variational Autoencoder. 변분 오토인코더)를 사용하면, 데이터를 압축해 잠재공간에 위치시킬 수 있다. 잠재 공간에서 학습한다면, 원본 이미지보다 훨씬 작은 임베딩 벡터 데이터만 계산되므로, 개선된 연산 속도과 효율적인 GPU 메모리 사용이 가능하다. 이를 위해, VAE는 다음 그림과 같이 인코더-디코더 레이어를 배치하고, 잠재공간으로 압축된 텐서 출력의 평균μ, 분산σ이 정규분포를 가지는 확률분포 N(μ, σ)가 학습되도록 한다.</div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgkWu21ekbtpywYrJUMZSLqGE2qUXRPkio0sxC_zHyMzXJeRR0-BzHrOBSVCVzHmwwc4gwMELxuOMN1rtcveDPH6onjd9f2GQTRau8_N0iGC2P_AXplt9Z8P4_rov0_VLdh8CNw3tfQ8IR49ADuR1UNnyYfVJ5LuVPu1m7qtfv5efn1uS8ijGbVAyoHjVJe" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="275" data-original-width="638" height="173" src="https://blogger.googleusercontent.com/img/a/AVvXsEgkWu21ekbtpywYrJUMZSLqGE2qUXRPkio0sxC_zHyMzXJeRR0-BzHrOBSVCVzHmwwc4gwMELxuOMN1rtcveDPH6onjd9f2GQTRau8_N0iGC2P_AXplt9Z8P4_rov0_VLdh8CNw3tfQ8IR49ADuR1UNnyYfVJ5LuVPu1m7qtfv5efn1uS8ijGbVAyoHjVJe=w400-h173" width="400" /></a></div><div class="separator" style="clear: both; text-align: center;">VAE 오토인코더 모델과 잠재공간(Latent Space)</div></div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;">오토인코더 구현은 단순히 신경망 층으로 인코더와 디코더를 만들고, 인코더의 결과가 확률분포 N(μ, σ)가 되도록 학습하는 것이다. 다음은 파이토치로 구현한 핵심 코드 일부를 보여준다. </div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><span style="font-size: x-small;">class VAE(nn.Module):</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> def __init__(self, input_dim=784, hidden_dim=400, latent_dim=200, device=device):</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> super(VAE, self).__init__()</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"><br /></span></div><div class="separator" style="clear: both;"><span style="font-size: small;"> self.encoder = nn.Sequential(</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> nn.Linear(input_dim, hidden_dim),</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> nn.LeakyReLU(0.2),</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> nn.Linear(hidden_dim, latent_dim),</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> nn.LeakyReLU(0.2)</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> ) # 인코더. 선형 레이어 + ReLU의 조합이다.</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> </span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> # 잠재공간 차원(평균, 편차)로 인코더 출력을 맵핑 </span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> self.mean_layer = nn.Linear(latent_dim, 2)</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> self.logvar_layer = nn.Linear(latent_dim, 2)</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> </span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> self.decoder = nn.Sequential(</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> nn.Linear(2, latent_dim),</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> nn.LeakyReLU(0.2),</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> nn.Linear(latent_dim, hidden_dim),</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> nn.LeakyReLU(0.2),</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> nn.Linear(hidden_dim, input_dim),</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> nn.Sigmoid()</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> ) # 디코더는 인코더 반대로 처리함.</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> </span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> def encode(self, x): # 인코더 계산</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> x = self.encoder(x)</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> mean, logvar = self.mean_layer(x), self.logvar_layer(x)</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> return mean, logvar</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"><br /></span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> def reparameterization(self, mean, var): # 정규분포 계산</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> epsilon = torch.randn_like(var).to(device) </span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> z = mean + var*epsilon</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> return z</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"><br /></span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> def decode(self, x): # 디코더 계산</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> return self.decoder(x)</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"><br /></span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> def forward(self, x): </span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> mean, logvar = self.encode(x) # 인코더 학습</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> z = self.reparameterization(mean, logvar) # 정규분포 계산</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> x_hat = self.decode(z) # 디코더 계산</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> return x_hat, mean, logvar # 계산결과, 평균, 분산</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"><br /></span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"><div class="separator" style="clear: both;">def loss_function(x, x_hat, mean, log_var): # 손실 함수 정의</div><div class="separator" style="clear: both;"> reproduction_loss = nn.functional.binary_cross_entropy(x_hat, x, reduction='sum') # 이진 교차엔트로피 손실</div><div class="separator" style="clear: both;"> KLD = - 0.5 * torch.sum(1+ log_var - mean.pow(2) - log_var.exp()) # <a href="https://medium.com/@rekalantar/variational-auto-encoder-vae-pytorch-tutorial-dce2d2fe0f5f">Kullback-Leibler(KL)</a> 손실 계산</div></span><span style="font-size: x-small;"><div class="separator" style="clear: both;"><br /></div><div class="separator" style="clear: both;"> return reproduction_loss + KLD # 두 확률분포의 거리를 최소화하려는 의도</div></span></div></div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;">오토인코더를 이용해, 다음 그림과 같이 압축된 잠재공간차원의 특정 위치는 입력된 데이터를 맵핑할 수 있다. </div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjDid6sovUaU-dr7elKer2wkMwTyjasZaTf59Zo8PP6GHaLAXnpVcldiHXAe7K1BiuqstMHqgDfj9KZK33aUaz9kVZ6BDHPJJ489j8RuaiqvKsz3CQwtoNbpKhlzP04nSKoj5q1G0WrQIOjC0Pi-64NqcwOCTdF10GrAQVsItpRX6o62hniQMpzjjkvKsFA" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="1152" data-original-width="2373" height="194" src="https://blogger.googleusercontent.com/img/a/AVvXsEjDid6sovUaU-dr7elKer2wkMwTyjasZaTf59Zo8PP6GHaLAXnpVcldiHXAe7K1BiuqstMHqgDfj9KZK33aUaz9kVZ6BDHPJJ489j8RuaiqvKsz3CQwtoNbpKhlzP04nSKoj5q1G0WrQIOjC0Pi-64NqcwOCTdF10GrAQVsItpRX6o62hniQMpzjjkvKsFA=w400-h194" width="400" /></a></div><div class="separator" style="clear: both; text-align: center;">잠재공간에 맵핑된(인코딩된) 데이터(Alexej Klushyn, 2019.12, <a href="https://argmax.ai/blog/vhp-vae/" style="text-align: left;">Learning Hierarchical Priors in VAEs</a>)</div><div class="separator" style="clear: both; text-align: center;"><br /></div></div><div class="separator" style="clear: both; text-align: left;">잠재공간에서 디퓨젼을 수행하는 과정은 앞의 픽셀공간에서 디퓨전을 실행하는 것과 유사하다(디퓨전 방정식은 특정 데이터 유형과 무관하게 동작됨). 노이즈-이미지 간 디퓨전은 오토인코더 모델을 이용한다. 오토인코더는 인코더-디코더 쌍을 가지고 있다. 그러므로, 전방향으로 노이즈 라벨을 예측하고, 역방향으로 노이즈에서 이미지를 생성하는 것이 가능하다. </div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;">텍스트 인코더도 같은 방식으로 잠재공간차원에 맵핑한다. 참고로, 변환된 텍스트 벡터를 임베딩 벡터라 한다(정확히는 벡터가 아닌 텐서값).</div><div class="separator" style="clear: both;"><br /></div></div><div class="separator" style="clear: both; text-align: left;">이제, 오토인코더로 잠재공간에 압축되어 위치된 입력 데이터에 텍스트 조건정보를 추가해 파라메터화하고, 이에 따라 이미지가 생성될 수 있도록 U-Net모델을 학습한다. 이를 컨디셔닝이라 한다. 컨디셔닝은 학습 시 특정 조건을 줄 수 있는 파라메터(예. 텍스트)를 포함한다. </div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhmFUi79IfqnnY7YaN-IdI0kGaWIFkooDmySuWRUC3dHCU0-hyFiIZwMjhpNS6TEKRxw8A7Ni13S-FAo3UzHnx5dMd5ovhFvXqvx68cNlehnhi2WQOY_bwqlJfaOd2W988CcKV_OKYURPzEyCFgPKHbkdq5p9TZSb02zVDP09DYJ3vba2kBFgT9F9C1ZREf" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="321" data-original-width="998" height="162" src="https://blogger.googleusercontent.com/img/a/AVvXsEhmFUi79IfqnnY7YaN-IdI0kGaWIFkooDmySuWRUC3dHCU0-hyFiIZwMjhpNS6TEKRxw8A7Ni13S-FAo3UzHnx5dMd5ovhFvXqvx68cNlehnhi2WQOY_bwqlJfaOd2W988CcKV_OKYURPzEyCFgPKHbkdq5p9TZSb02zVDP09DYJ3vba2kBFgT9F9C1ZREf=w503-h162" width="503" /></a></div><div class="separator" style="clear: both; text-align: center;">잠재공간에서 디퓨전 컨디셔닝 처리(<a href="https://medium.com/@steinsfu/stable-diffusion-clearly-explained-ed008044e07e">Steins</a>, 2023)</div><br /></div><div class="separator" style="clear: both; text-align: left;">입력 데이터(이미지)와 텍스트를 잠재 공간차원에서 거리가 가깝도록 유사도를 조정해야(학습해야) 하므로, 앞에 글에서 언급한 트랜스포머의 어텐션 모델을 사용한다.</div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;">컨디셔닝은 트랜스포머 모델을 통해, 각 U-Net의 레이어 모듈에 출력과 연결되도록 한다. 이를 통해, 신경망은 입력 이미지와 텍스트에 대해 다양한 x(t)에 대한 노이즈 배치 데이터를 예측하도록 학습될 것이다. </div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;">학습이 끝나면, 텍스트와 노이즈를 입력해 잠재공간 차원에서 노이즈 역확산 방정식(DDPM. Denoising Diffusion Probabilistic Models. 디노이징 확률 모델)을 계산한다. 이를 통해, 텍스트 입력 조건에 따른 이미지를 생성하도록 한다. 각 U-Net 층에 트랜스포머의 어텐션 모델이 연결하여, 일반적 수준에서 세부적 수준까지 생성되는 이미지를 조건에 따라 생성하도록 한다.</div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both;"><b>트랜스포머와 CLIP</b></div><div class="separator" style="clear: both;">스테이블 디퓨전은 트랜스포머를 이용해, 멀티모달인 텍스트-이미지 데이터쌍의 유사도를 계산하는 CLIP(Contrasive Language-Image Pre-Traning. <a href="https://daddynkidsmakers.blogspot.com/2024/02/clip.html">참고</a>)을 사용한다. CLIP을 이용하면, 'lovely cat'과 고양이 이미지 임베딩 벡터를 잠재 공간 차원에서 거리가 가까워지도록 조정할 수 있다. 즉, 다음과 같은 W 가중치를 학습할 수 있다. </div><div class="separator" style="clear: both; text-align: center;">i ≈ t·W</div><div class="separator" style="clear: both; text-align: center;">여기서, </div><div class="separator" style="clear: both; text-align: center;">i=image embedding vector</div><div class="separator" style="clear: both; text-align: center;">t=text embedding vector</div><div class="separator" style="clear: both; text-align: center;">W=weight vector</div><div class="separator" style="clear: both;"><br /></div><div class="separator" style="clear: both;">이때, 목적 함수는 cosine similarity(유사도)가 된다(트랜스포머 모델에서 사용된 어텐션 스코어 행렬값). CLIP을 이용해, 멀티모달 데이터셋인 텍스트-이미지를 잠재공간에 표현해, 멀티모달(Multi Modal. 쉽게 말해 이종 데이터셋) 조건에서 학습할 수 있도록 한다. 텍스트 임베딩 벡터를 이미지 생성 과정에 포함하려면, 노이즈 예측하는 U-Net 부분을 텍스트 조건에 따라 조정되도록 수정해야 한다. 다음 그림은 이 과정을 보여준다. </div></div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEiQVF8FJpJC5gXILUyQqsMTGxNYf0njVJi1pfnHndVs88czkDy4dV2WCGYRthK9AWewVMwxegQ_E3LLW-x8RyLceMfOH-BDf4XZKnYTeCeP6N6rxrv8Kn0ZJKbzB5xRd8LMHcZEflm9wgiuOPaUwzrxBq1GgS8KzJhp8piGx4IknZ72bxbzgO2laTBEoQV1" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="419" data-original-width="929" height="221" src="https://blogger.googleusercontent.com/img/a/AVvXsEiQVF8FJpJC5gXILUyQqsMTGxNYf0njVJi1pfnHndVs88czkDy4dV2WCGYRthK9AWewVMwxegQ_E3LLW-x8RyLceMfOH-BDf4XZKnYTeCeP6N6rxrv8Kn0ZJKbzB5xRd8LMHcZEflm9wgiuOPaUwzrxBq1GgS8KzJhp8piGx4IknZ72bxbzgO2laTBEoQV1=w492-h221" width="492" /></a></div><div class="separator" style="clear: both; text-align: center;">스테이블 디퓨전에서 트랜스포머 어텐션을 이용한 U-Net 컨디셔닝(<a href="https://arxiv.org/abs/2112.10752" style="text-align: left;">Robin Rombach</a> et al, 2022)</div><div><br /></div></div></div><div class="separator" style="clear: both; text-align: left;">앞의 그림의 QKV는 트랜스포머 어텐션 모델의 Query, Key, Value 입력을 의미한다. 여기서, Query는 조건화를 위해 텍스트 임베딩이 입력되어야 한다. K, V는 학습된 이미지 컨텍스트가 입력되어야 한다. 그럼, 어텐션 모델이 입력되는 이미지, 텍스트의 유사도를 가깝게 만들기 위해 학습 가중치를 조정할 것이다. U-Net 각 층마다 어텐션 모델을 적용하여, 개요에서 상세 수준까지 스타일을 조정할 수 있도록 컨디셔닝을 적용한다. </div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;">이제 컨디셔닝 된 U-Net 모델을 설계했으므로, 잠재공간에 표현된 압축 이미지 임베딩 벡터와 노이즈 양을 입력받아, ResNet (잔차연결 모델)을 이용해, 예측된 노이즈 이미지를 생성하도록 학습한다. </div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;">이제, 학습될 배치 데이터(입력 이미지, 이미지를 설명하는 텍스트)가 스테이블 디퓨전 모델에입력될 때 마다, 각 ResNet에서 출력되는 이미지 특징 벡터와 입력된 텍스트 벡터 간의 유사도가 가까워지도록 W값이 조정될 것이다. 각 ResNet의 레이어 층별로 어텐션 스코어가 계산되므로, 텍스트-이미지 생성 시 텍스트의 묘사에 따라서, 이미지 전체 및 디테일 생성에 조건을 줄 수 있다.</div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><div class="separator" style="clear: both; text-align: center;"><br /></div></div></div><div class="separator" style="clear: both; text-align: left;">디퓨전(확산) 모델에 따라, 입력 데이터에 대한 다양한 노이즈 이미지가 계산되도록 학습되므로, 학습 결과를 역방향 디퓨전 모델로 역계산하여, 디노이징하면 텍스트 입력에 따른 이미지를 생성할 수 있다. </div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEieVtBqZR2FJhjc9QedJ9DuWiLTm_GMo8z5BqpbhXUpa6G9XNNo1d1xl7rUyXLy0a1FXN6uO_pqEfa2qkh6KaRLtEZIKAcW5CMwYncuo-XH1bpIIM8tQfjRWfFlZE6e0w22AM5di4ujBYvK35TDahYCq4P93i9_saPQ4iYnm1bSyndCF3sT_IXIdW3mWUsu" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="946" data-original-width="1124" height="337" src="https://blogger.googleusercontent.com/img/a/AVvXsEieVtBqZR2FJhjc9QedJ9DuWiLTm_GMo8z5BqpbhXUpa6G9XNNo1d1xl7rUyXLy0a1FXN6uO_pqEfa2qkh6KaRLtEZIKAcW5CMwYncuo-XH1bpIIM8tQfjRWfFlZE6e0w22AM5di4ujBYvK35TDahYCq4P93i9_saPQ4iYnm1bSyndCF3sT_IXIdW3mWUsu=w400-h337" width="400" /></a></div><div class="separator" style="clear: both; text-align: center;">역확산 모델에 따른 단계별 디노이징 예(Lgnacio Aristimuno, 2023.11, <a href="https://blog.marvik.ai/2023/11/28/an-introduction-to-diffusion-models-and-stable-diffusion/" style="text-align: left;">An Introduction to Diffusion Models and Stable Diffusion</a>)</div></div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;">다음 그림은 앞의 과정을 거쳐 개발된 스테이블 디퓨전 아키텍처를 보여준다. 그림에서 디노이징 단계(denoising step)은 스위치(switch)에 의해 역계산되는 것을 이해할 수 있다.</div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEioLty8INTLpMrFGGS1tV_owfZg4vlHOjkedKo4iW6oQ2M_UTYytLk4UTIeQzyrhSZjYOC2UO_9xBWnyzeqTXb2ZyLohfnGjepd3K0HyWBdFw6dZ5dxwHTppwCCOP9GGFrh_qBdoxDJqF6aq9ha7Vg4ooWL9mWR1Wt06h0EE7lXHx6o3jBaCQqlX223ntGE" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="315" data-original-width="624" height="237" src="https://blogger.googleusercontent.com/img/a/AVvXsEioLty8INTLpMrFGGS1tV_owfZg4vlHOjkedKo4iW6oQ2M_UTYytLk4UTIeQzyrhSZjYOC2UO_9xBWnyzeqTXb2ZyLohfnGjepd3K0HyWBdFw6dZ5dxwHTppwCCOP9GGFrh_qBdoxDJqF6aq9ha7Vg4ooWL9mWR1Wt06h0EE7lXHx6o3jBaCQqlX223ntGE=w466-h237" width="466" /></a></div><div class="separator" style="clear: both; text-align: center;">스테이블 디퓨전 아키텍처(<a href="https://arxiv.org/abs/2112.10752" style="text-align: left;">Robin Rombach</a> et al, 2022)</div></div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;"><b>마무리</b></div><div class="separator" style="clear: both; text-align: left;">이 글은 멀티모달 생성AI를 대중에 널리 알리게 된 딥러닝 모델인 스테이블 디퓨전의 동작 원리, 모델을 가급적 상세히 분석, 정리해 보았다. 이 과정을 통해, 멀티모달 데이터 생성 과정, 메커니즘, 활용 방법 및 한계를 확인할 수 있다. </div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;">앞서 보았듯이 스테이블 디퓨전 개발자들은 오토인코더를 통해 계산된 잠재공간차원에서 U-Net을 조건화하여 모델을 학습하도록 하였다. 입력 텍스트에 대한 조건화된 U-Net학습은 트랜스포머의 어텐션 모델을 적용하여 처리했다. 이런 멀티 모달리티 기능은 OpenAI의 CLIP 아키텍처를 재활용한 것이다. 잠재공간에서 조건화되고 학습될 수 있는 U-Net을 구현한다는 것은 다른 발전 가능성을 내포하고 있다. 잠재공간에 입력될 임베딩 벡터에 비디오 영상 프레임과 시간을 함께 입력해 학습할 수 있을 것이다. 조건화된 파라메터에 CLIP에서 자동생성된 비디오 캡션을 사용하거나, 생성 데이터의 형태와 스타일을 제어할 수 있는 파라메터를 텍스트처럼 입력하고 학습할 수도 있다. </div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;">CLIP으로 촉발된 멀티 모달리티와 스테이블 디퓨전 아키텍처의 확장 가능성은 생성AI의 기술 발전을 증폭시키고 있다.</div><div><div class="separator" style="clear: both; text-align: center;"><div><div class="separator" style="clear: both;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhQqlvXes9-SCPej5T7FWXCznFGbjpjO0LxzfQuXaz3tZLElreMb35fSrk9PsGN3lafuUrZ-EBMhN35RpegFPIb2VCzGHwKo-oapS-pU-r6EGllixM6XadgsUAd-8Lbm61zsjcZ6GhGJp6wnBzeCFLR4NfzX1VViHZyx_5QKE9r3au8cqomWOa8ohWx6a_f" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="466" data-original-width="828" height="250" src="https://blogger.googleusercontent.com/img/a/AVvXsEhQqlvXes9-SCPej5T7FWXCznFGbjpjO0LxzfQuXaz3tZLElreMb35fSrk9PsGN3lafuUrZ-EBMhN35RpegFPIb2VCzGHwKo-oapS-pU-r6EGllixM6XadgsUAd-8Lbm61zsjcZ6GhGJp6wnBzeCFLR4NfzX1VViHZyx_5QKE9r3au8cqomWOa8ohWx6a_f=w445-h250" width="445" /></a></div><div class="separator" style="clear: both;">스테이블 디퓨전까지 생성 AI 발전 과정(<a href="https://medium.com/@vasco-dev/history-and-literature-on-latent-stable-diffusion-dbca69fd54d5" style="text-align: left;">History and literature on Latent (Stable) Diffusion</a>)</div></div></div><div class="separator" style="clear: both; text-align: center;"><span style="margin-left: 1em; margin-right: 1em;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhx0k8PMZPE-LuufwMavdsOpcW64V6ijNZHP9UB1chz1Z4pHJu_CD65-bl5eWN40gM4CEHSRXb6J-7SrY-apEJggMNF9XPp6mLaSqG6luxpbapBZN4lwh7iteqdO2uTUMatNxmx6Z9V9Ztw1UpyaHzAA5eZO9hMROzdYt-J7v8iPhc7i0gknS9NKS3MRiB5" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="765" data-original-width="556" height="230" src="https://blogger.googleusercontent.com/img/a/AVvXsEhx0k8PMZPE-LuufwMavdsOpcW64V6ijNZHP9UB1chz1Z4pHJu_CD65-bl5eWN40gM4CEHSRXb6J-7SrY-apEJggMNF9XPp6mLaSqG6luxpbapBZN4lwh7iteqdO2uTUMatNxmx6Z9V9Ztw1UpyaHzAA5eZO9hMROzdYt-J7v8iPhc7i0gknS9NKS3MRiB5=w167-h230" width="167" /></a></span><a href="https://blogger.googleusercontent.com/img/a/AVvXsEiGgKKtSX9yQTpwxsHxM6-QTxmeGQ3tVILF9s4lamNIpCdE6SCMs1DAMc7UsZf1C6gFNZ3GFyaMzbyhDb9csTMJ3WrMf1k54XDS8-tuWEi1C3SxqE0Nxje3MOO_aXJmfeIHxKdQMLkVqktdtfOnBPTPP-CqEX86NOM2cWWrJ-V9vyriwqE9Evd0pSZcPkfy" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="1176" data-original-width="1521" height="220" src="https://blogger.googleusercontent.com/img/a/AVvXsEiGgKKtSX9yQTpwxsHxM6-QTxmeGQ3tVILF9s4lamNIpCdE6SCMs1DAMc7UsZf1C6gFNZ3GFyaMzbyhDb9csTMJ3WrMf1k54XDS8-tuWEi1C3SxqE0Nxje3MOO_aXJmfeIHxKdQMLkVqktdtfOnBPTPP-CqEX86NOM2cWWrJ-V9vyriwqE9Evd0pSZcPkfy=w284-h220" width="284" /></a></div></div><div class="separator" style="clear: both; text-align: center;">스테이블 디퓨전 모델 출력을 제어하는 컨트롤넷(<a href="https://arxiv.org/pdf/2302.05543.pdf">ControlNet</a>, <a href="https://github.com/lllyasviel/ControlNet">GitHub</a>. 2023.11)</div><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhactkCDkGTTiA3ojpQoZp1cfE0jOHqjXC3wJCo_U23ZHy4XQihmLKvwm2x6bUwLt5up5XhAXIqPuXBxj5LOy_PJWVArmeLayQWZEOSt5ha1GeROnLVsMH4yVaZ5zbMxvgdLT3q3Yno2V3doAwWfjzlEI0l-zu88Knqyv4oSEnnTSGk4-8lwMhgavDPcsms" style="margin-left: 1em; margin-right: 1em;"></a><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhactkCDkGTTiA3ojpQoZp1cfE0jOHqjXC3wJCo_U23ZHy4XQihmLKvwm2x6bUwLt5up5XhAXIqPuXBxj5LOy_PJWVArmeLayQWZEOSt5ha1GeROnLVsMH4yVaZ5zbMxvgdLT3q3Yno2V3doAwWfjzlEI0l-zu88Knqyv4oSEnnTSGk4-8lwMhgavDPcsms" style="margin-left: 1em; margin-right: 1em;"></a><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhactkCDkGTTiA3ojpQoZp1cfE0jOHqjXC3wJCo_U23ZHy4XQihmLKvwm2x6bUwLt5up5XhAXIqPuXBxj5LOy_PJWVArmeLayQWZEOSt5ha1GeROnLVsMH4yVaZ5zbMxvgdLT3q3Yno2V3doAwWfjzlEI0l-zu88Knqyv4oSEnnTSGk4-8lwMhgavDPcsms" style="margin-left: 1em; margin-right: 1em;"></a><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhactkCDkGTTiA3ojpQoZp1cfE0jOHqjXC3wJCo_U23ZHy4XQihmLKvwm2x6bUwLt5up5XhAXIqPuXBxj5LOy_PJWVArmeLayQWZEOSt5ha1GeROnLVsMH4yVaZ5zbMxvgdLT3q3Yno2V3doAwWfjzlEI0l-zu88Knqyv4oSEnnTSGk4-8lwMhgavDPcsms" style="margin-left: 1em; margin-right: 1em;"></a><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjK5eTthraZO-ClIlgRUZtJ2HJsHMGsOl-mmdNjUhkyJVtpygBPeMAosRqXYZ5na-jla7cYMtXaJBNFbpb5sNYzHBdoaygG29-tsW4vLlZY9VkuhxjSm8_JguxcyzXYl3_DY96xsiVZXRn-4JEcF88HcpuDbxzM3tZ1bDBs4PH9dItU-fO4o9Q9e_xd0WqZ" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="643" data-original-width="1600" height="161" src="https://blogger.googleusercontent.com/img/a/AVvXsEjK5eTthraZO-ClIlgRUZtJ2HJsHMGsOl-mmdNjUhkyJVtpygBPeMAosRqXYZ5na-jla7cYMtXaJBNFbpb5sNYzHBdoaygG29-tsW4vLlZY9VkuhxjSm8_JguxcyzXYl3_DY96xsiVZXRn-4JEcF88HcpuDbxzM3tZ1bDBs4PH9dItU-fO4o9Q9e_xd0WqZ=w400-h161" width="400" /></a></div><img alt="" data-original-height="450" data-original-width="800" height="180" src="https://blogger.googleusercontent.com/img/a/AVvXsEhactkCDkGTTiA3ojpQoZp1cfE0jOHqjXC3wJCo_U23ZHy4XQihmLKvwm2x6bUwLt5up5XhAXIqPuXBxj5LOy_PJWVArmeLayQWZEOSt5ha1GeROnLVsMH4yVaZ5zbMxvgdLT3q3Yno2V3doAwWfjzlEI0l-zu88Knqyv4oSEnnTSGk4-8lwMhgavDPcsms=w321-h180" width="321" /></div><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEg6wh04YQOGrtGHJwbV586bdhZ0_1RruACC-jeLt4DoAlPh5sHMIHcOtm3h55D85vv_mKmZABfmKMqgFnk2awTI6wsuZO8oMQg3vFWwOcrB3AtOJq_h8ZVZz10voSKCsWufquMV2tfZSi-2yBFcHVFJJ_aNLQqunrxveKxDa_QnVrsF2_1DigQ2BOeXG3aO" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="372" data-original-width="2000" height="60" src="https://blogger.googleusercontent.com/img/a/AVvXsEg6wh04YQOGrtGHJwbV586bdhZ0_1RruACC-jeLt4DoAlPh5sHMIHcOtm3h55D85vv_mKmZABfmKMqgFnk2awTI6wsuZO8oMQg3vFWwOcrB3AtOJq_h8ZVZz10voSKCsWufquMV2tfZSi-2yBFcHVFJJ_aNLQqunrxveKxDa_QnVrsF2_1DigQ2BOeXG3aO" width="320" /></a></div></div></div></div><div class="separator" style="clear: both; text-align: center;">Text to Video 모델 아키텍처(<a href="https://www.marktechpost.com/2022/12/07/bytedance-ai-researchers-introduce-magicvideo-an-efficient-text-to-video-generation-framework-based-on-latent-diffusion-models/">MagicVideo</a>, 2022.12)와 OpenAI <a href="https://openai.com/sora">SORA</a>(2024.2)</div><div class="separator" style="clear: both; text-align: center;"><br /></div><div><div class="separator" style="clear: both;">다음 글은 여기서 정리된 내용을 바탕으로 스테이블 디퓨전을 실행 가능한 코드 수준으로 구현하고, 입력 데이터가 변환 및 역변환 확산되는 과정을 직접 확인해 보도록 한다.</div><div class="separator" style="clear: both; text-align: center;"><br /></div></div><div class="separator" style="clear: both; text-align: center;"><div><div class="separator" style="clear: both;"><img alt="" data-original-height="618" data-original-width="1183" height="167" src="https://blogger.googleusercontent.com/img/a/AVvXsEhctxT2UtI7a6xmy2U5UB-0JlOyRvCngogtE0v8KocHqaQgyNWHsI8bkI8BacYuVJO-fXG0EHFyOdMFDnjkzcT7yR4mk7IGptkWaKffMWlwCVXDO8H9P209R2i0xMWPMq-YHbONEa59I-JgjXqKp3PLAI5mGhJlNRiVYQoeQYyPv7cS6F82N_rAE_6Kx7_x" style="color: #0000ee;" width="320" /><a href="https://blogger.googleusercontent.com/img/a/AVvXsEj7w0c8-vEvHx8e2aRp1vmn_gm7aaWzkyehdstkcNASFrfHlRRB3NTkXEE-PpLcd1596XVJykuUcMxORNIQchNJnw3KqLuBNsi3MqyHKJBTk8GWtbtq3DWN-_ZEkGxSyZr_ogoWUqXzm03qYGq7wek1bjKohtjz00mxrOW37o4aKcTaz3wPWls-7EuqYO-D" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="837" data-original-width="633" height="166" src="https://blogger.googleusercontent.com/img/a/AVvXsEj7w0c8-vEvHx8e2aRp1vmn_gm7aaWzkyehdstkcNASFrfHlRRB3NTkXEE-PpLcd1596XVJykuUcMxORNIQchNJnw3KqLuBNsi3MqyHKJBTk8GWtbtq3DWN-_ZEkGxSyZr_ogoWUqXzm03qYGq7wek1bjKohtjz00mxrOW37o4aKcTaz3wPWls-7EuqYO-D=w126-h166" width="126" /></a></div><div class="separator" style="clear: both;">스테이블 디퓨전 기술 개발 주역 <a href="https://ommer-lab.com/">Computer Vision & Learning Group (ommer-lab.com)</a></div><div><br /></div></div></div><b>레퍼런스</b><br /><ul style="text-align: left;"><li><a href="https://arxiv.org/abs/2112.10752">Robin Rombach, Andreas Blattmann, Dominik Lorenz, Patrick Esser, Björn Ommer, 2022.4, High-Resolution Image Synthesis with Latent Diffusion Models</a></li><li>MosaicML, 2023, <a href="https://www.databricks.com/blog/stable-diffusion-2">Training Stable Diffusion from Scratch for $50k with MosaicML (Part 2) | Databricks Blog</a></li><li><span style="text-align: center;">Alexej Klushyn, 2019.12, </span><a href="https://argmax.ai/blog/vhp-vae/">Learning Hierarchical Priors in VAEs</a></li><li><a href="https://en.wikipedia.org/wiki/Langevin_equation">Langevin equation, Wikipedia</a></li><li id="cite_note-1" style="animation-delay: -0.01ms; animation-duration: 0.01ms; animation-iteration-count: 1; break-inside: avoid-column; counter-increment: mw-ref-extends-parent 1 mw-references 1; counter-reset: mw-ref-extends-child 0; margin-bottom: 0.1em; scroll-behavior: auto; transition-duration: 0ms;"><span class="reference-text" style="animation-delay: -0.01ms; animation-duration: 0.01ms; animation-iteration-count: 1; scroll-behavior: auto; transition-duration: 0ms;"><cite class="citation journal cs1" id="CITEREFLangevin1908" style="animation-delay: -0.01ms; animation-duration: 0.01ms; animation-iteration-count: 1; font-style: inherit; overflow-wrap: break-word; scroll-behavior: auto; transition-duration: 0ms;">Langevin, P. (1908). <a href="http://www.fisica.unlp.edu.ar/Members/sanchez/Ej2%20viejo/lemons1997_langevin.pdf">Sur la théorie du mouvement brownien [On the Theory of Brownian Motion]</a>. <i style="animation-delay: -0.01ms; animation-duration: 0.01ms; animation-iteration-count: 1; scroll-behavior: auto; transition-duration: 0ms;">C. R. Acad. Sci. Paris</i>. <b style="animation-delay: -0.01ms; animation-duration: 0.01ms; animation-iteration-count: 1; scroll-behavior: auto; transition-duration: 0ms;">146</b>: 530–533.</cite></span></li><li>M<a href="https://www.miniphysics.com/brownian-motion.html" style="text-align: center;">iniphysics, Brownian Motion - Learn Physics From The Physics Authority</a></li><li>Q<a href="https://www.youtube.com/watch?app=desktop&v=P9qar8mv3Tk">uantpie, 2019, Diffusion Equation - Derivation and Explanation using Brownian</a></li><li>Yang Song et al, 2011.2, <a href="https://arxiv.org/abs/2011.13456">Score-Based Generative Modeling through Stochastic Differential Equations</a></li><li><span style="text-align: center;">Lgnacio Aristimuno, 2023.11,</span><span style="text-align: center;"> </span><a href="https://blog.marvik.ai/2023/11/28/an-introduction-to-diffusion-models-and-stable-diffusion/">An Introduction to Diffusion Models and Stable Diffusion</a></li><li>Ainur Gainetdinov, 2023, <a href="https://pub.towardsai.net/gan-mode-collapse-explanation-fa5f9124ee73">GAN Mode Collapse Explanation</a></li><li><a href="https://medium.com/@steinsfu/stable-diffusion-clearly-explained-ed008044e07e">Steins</a>, 2023, <a href="https://medium.com/@steinsfu/stable-diffusion-clearly-explained-ed008044e07e">Stable Diffusion Clearly Explained</a></li><li>Jay Alammar, 2022, <a href="https://jalammar.github.io/illustrated-stable-diffusion/">The Illustrated Stable Diffusion</a></li><li>Thomas, 2023.2, <a href="https://wandb.ai/capecape/train_sd/reports/How-To-Train-a-Conditional-Diffusion-Model-From-Scratch--VmlldzoyNzIzNTQ1">How To Train a Conditional Diffusion Model From Scratch | train_sd – Weights & Biases</a></li><li>Hugging face, <a href="https://huggingface.co/docs/diffusers/en/tutorials/basic_training">Train a diffusion model</a></li><li><a href="https://theaisummer.com/diffusion-models/">Sergios Karagiannakos, 2022.9, How diffusion models work: the math from scratch | AI Summer</a></li><li><a href="https://tree.rocks/make-diffusion-model-from-scratch-easy-way-to-implement-quick-diffusion-model-e60d18fd0f2e">Seachaos</a>, 2023, <a href="https://tree.rocks/make-diffusion-model-from-scratch-easy-way-to-implement-quick-diffusion-model-e60d18fd0f2e">Make Diffusion model from scratch</a> and <a href="https://github.com/gmongaras/Diffusion_models_from_scratch">Diffusion_models_from_scratch(</a>github)</li><li>Olaf Ronneberger et al, 2015, <a href="https://arxiv.org/abs/1505.04597">U-Net: Convolutional Networks for Biomedical Image Segmentation (arxiv.org)</a></li><li><a href="https://arxiv.org/pdf/2006.11239.pdf">Jonathan Ho et al</a>, 2020.12, Denoising Diffusion Probabilistic Models</li><li><a href="https://levelup.gitconnected.com/building-stable-diffusion-from-scratch-using-python-f3ebc8c42da3">Fareed Khan, 2024</a>, <a href="https://levelup.gitconnected.com/building-stable-diffusion-from-scratch-using-python-f3ebc8c42da3">Coding Stable Diffusion from Scratch</a> (<a href="https://americanoisice.tistory.com/109">#1</a>)</li><li><a href="https://medium.com/@rekalantar/variational-auto-encoder-vae-pytorch-tutorial-dce2d2fe0f5f">Reza Kalantar, 2022</a>.11, <a href="https://medium.com/@rekalantar/variational-auto-encoder-vae-pytorch-tutorial-dce2d2fe0f5f">Variational Autoencoder</a></li><li><a href="https://medium.com/@vasco-dev/history-and-literature-on-latent-stable-diffusion-dbca69fd54d5">Vasco Meerman, 2023, History and literature on Latent (Stable) Diffusion</a></li><li><a href="https://huggingface.co/blog/text-to-video">A Dive into Text-to-Video Models (huggingface.co)</a></li><li><a href="https://medium.com/@AIBites/stable-video-diffusion-convert-text-and-images-to-videos-e0f41b7f6986">Shrinivasan Sankar</a>, 2023.12, <a href="https://medium.com/@AIBites/stable-video-diffusion-convert-text-and-images-to-videos-e0f41b7f6986">Stable Video Diffusion — Convert Text and Images to Videos</a></li><li><a href="https://huggingface.co/blog/text-to-video">Adirik Alara Dirik, 2023.5, A Dive into Text-to-Video Models</a></li><li><a href="https://byby.dev/ai-text-to-video-models">Top 7 text-to-video generative AI models</a>, 2023.11</li><li><a href="https://arxiv.org/abs/2302.05543">Lvmin Zhang, Anyi Rao, Maneesh Agrawala, 2023.11, Adding Conditional Control to Text-to-Image Diffusion Models</a></li><li>Ekrem Çetinkaya, 2022.12, <a href="https://www.marktechpost.com/2022/12/07/bytedance-ai-researchers-introduce-magicvideo-an-efficient-text-to-video-generation-framework-based-on-latent-diffusion-models/">ByteDance AI Researchers Introduce 'MagicVideo,' an Efficient Text-to-Video Generation Framework based on Latent Diffusion Models - MarkTechPost</a></li><li><a href="https://openai.com/sora">Sora, Openai.com</a></li><li><a href="https://openai.com/research/video-generation-models-as-world-simulators?fbclid=IwAR3F1oNQZ0GHKf8C6zQiTmvWCJN5QLoVKi9T6RY5jgg9n29nid5ic9DuBkE">Video generation models as world simulators (openai.com)</a></li></ul></div>Daddy Makerhttp://www.blogger.com/profile/02275758703365576780noreply@blogger.com0tag:blogger.com,1999:blog-5201956450461596914.post-61558350989072435072024-02-07T02:53:00.000-08:002024-02-07T02:59:49.233-08:00IMU만으로 Indoor navigation 구현해보기<div style="text-align: left;">이 글은 Indoor navigation을 간단히 구현하는 과정을 보여준다.<br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEioDBQZ9WHGbl2BM0_Y6cLehwCMj96tM2OM4OLxKgbuknf-7RqgqRYPit_3OSkqasU2RoWiV8dg_d2UQgRXACP3gSvHcPK17MXLFaEx-SFL6yUvuN-XqLlE5oss8BrxSN7_AgKSQ6CfrTTYPKh87sUnqluV67LzbYpA0jQfZ2YmPtDoAYEgXdFYcfX4w2g1" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="831" data-original-width="1000" height="240" src="https://blogger.googleusercontent.com/img/a/AVvXsEioDBQZ9WHGbl2BM0_Y6cLehwCMj96tM2OM4OLxKgbuknf-7RqgqRYPit_3OSkqasU2RoWiV8dg_d2UQgRXACP3gSvHcPK17MXLFaEx-SFL6yUvuN-XqLlE5oss8BrxSN7_AgKSQ6CfrTTYPKh87sUnqluV67LzbYpA0jQfZ2YmPtDoAYEgXdFYcfX4w2g1" width="289" /></a></div><br /></div><div style="text-align: left;"><b>레퍼런스</b></div><div style="text-align: left;"><ul style="text-align: left;"><li><a href="https://medium.com/@talha.ej10/navigating-the-uncharted-exploring-indoor-localization-with-imu-based-systems-using-kalman-filters-52df1bf22d14">Navigating the Uncharted: Exploring Indoor Localization with IMU-based Systems using Kalman Filters (KF, EKF, UKF) with Code | by Talha Ejaz | Medium</a></li><li><a href="https://github.com/rfbr/PDR_with_Deep_Learning">rfbr/PDR_with_Deep_Learning (github.com)</a></li><li><a href="https://github.com/yyccR/Location">yyccR/Location: Smartphone navigation positionning, fusion GPS and IMU sensors. (github.com)</a></li><li><a href="https://github.com/mohammed-elkomy/vanilla-dead-reckoning">mohammed-elkomy/vanilla-dead-reckoning: outdoor deadreckoning mobile computing task (github.com)</a></li><li><a href="https://medium.com/metaor-artificial-intelligence/pedistnet-ai-based-indoor-navigation-for-pedestrians-b19ef6221e8d">PeDistNet: AI-based Indoor Navigation for Pedestrians | by Dr Barak Or | MetaOr Artificial Intelligence | Dec, 2023 | Medium</a></li><li><a href="https://www.mdpi.com/1424-8220/17/6/1268">Sensors | Free Full-Text | A LiDAR and IMU Integrated Indoor Navigation System for UAVs and Its Application in Real-Time Pipeline Classification (mdpi.com)</a></li><li><a href="https://bryanung18.medium.com/arkit-indoor-navigation-beacon-marker-based-tutorial-2afad5075228">ARKit Indoor Navigation (Beacon/Marker Based) Tutorial | by Bryan Ung | Medium</a></li><li><a href="https://github.com/neXenio/BLE-Indoor-Positioning">neXenio/BLE-Indoor-Positioning: Multilateration using bluetooth beacons (github.com)</a></li><li><a href="https://github.com/sorianog/indoor-nav-arcore">sorianog/indoor-nav-arcore: Indoor Navigation with ARCore (From: 2018) (github.com)</a></li><li><a href="https://github.com/hungyunliao/Indoor-accelerometer-localization">hungyunliao/Indoor-accelerometer-localization: Localization app using accelero and gyro meters in iPhones. This is a project at Focus Universal- iOS APP, swift (Intern, May - July 2016) (github.com)</a></li><li><a href="https://github.com/ws2131/indoor-location">ws2131/indoor-location: iOS app that can determine the indoor vertical location using sensors in the device (github.com)</a></li><li><a href="https://github.com/priyankark/SensorStreamServer">priyankark/SensorStreamServer: Companion servers for Sensor Stream App. Stream sensor data, audio and images from your phone to an open source server running on your PC/Raspberry Pi in real-time over your local network. (github.com)</a></li><li><a href="https://github.com/Pi4IoT/Node-RED">Pi4IoT/Node-RED: Node-RED (github.com)</a></li></ul></div>Daddy Makerhttp://www.blogger.com/profile/02275758703365576780noreply@blogger.com0tag:blogger.com,1999:blog-5201956450461596914.post-71950693369781749562024-02-06T22:20:00.000-08:002024-02-24T01:23:11.072-08:00생성AI 멀티모달 모델 개발의 시작. OpenAI의 CLIP모델 이해, 코드 분석, 개발, 사용하기<p>이 글은 생성AI의 멀티모달 딥러닝 기술 확산의 계기가 된 Open AI의 <a href="https://openai.com/research/clip">CLIP</a>(Contrastive Language-Image Pre-Training. 2021) 코드 개발 과정을 분석하고, 사용하는 방법을 정리한다. </p><p>CLIP은 구글이 개발한 자연어 번역 목적의 트랜스포머 모델, 비전 데이터 변환에 사용되는 <a href="https://en.wikipedia.org/wiki/Variational_autoencoder">VAE</a> 개념을 사용하여, 멀티모달 학습 방식을 구현하였다. 이 글은 그 과정을 설명하고, 파이토치로 직접 구현하는 과정을 보여준다. CLIP을 이용하면, 유튜브, 넷플릭스와 같은 영상에서 자연어로 질의해 해당 장면을 효과적으로 검색할 수 있다.</p><p>참고로, CLIP에서는 트랜스포머가 핵심 컴포넌트로 사용되었다. CLIP과 같이 트랜스포머가 자연어 번역 이외에 멀티모달의 핵심 기술이 된 이유는 비정형 데이터를 연산 가능한 차원으로 수치화할 수 있는 임베딩 기술의 발전과 트랜스포머의 Key, Query, Value 입력을 통한 여러 학습 데이터 조합이 가능한 특징이 크게 작용했다. </p><div><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgSsuXRCDfRR69boBgYGE0J3cyYde7dhdlY-9mGlcj5pUOvTew0U4J9GPr8oGl4QOVImHX88xKq8qXNA7R925Djeq6St_BuOP-JztSo6dt3lTEWxAJIq-rJ-6nE7U5zQAs155dC5eNeHEgbVdaFnCBdOWwNs6jLuJhRxmxbHxzFv-y5uQDZgTznjv-9FI62" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="701" data-original-width="1777" height="126" src="https://blogger.googleusercontent.com/img/a/AVvXsEgSsuXRCDfRR69boBgYGE0J3cyYde7dhdlY-9mGlcj5pUOvTew0U4J9GPr8oGl4QOVImHX88xKq8qXNA7R925Djeq6St_BuOP-JztSo6dt3lTEWxAJIq-rJ-6nE7U5zQAs155dC5eNeHEgbVdaFnCBdOWwNs6jLuJhRxmxbHxzFv-y5uQDZgTznjv-9FI62" width="320" /></a></div><div class="separator" style="clear: both;">멀티모달 시작을 알린 OpenAI의 CLIP 모델(<a href="https://arxiv.org/abs/2103.00020">Learning Transferable Visual Models From Natural Language Supervision</a>, 2021)</div><div class="separator" style="clear: both;"><br /></div></div></div><div>트랜스포머와 <a href="https://en.wikipedia.org/wiki/Variational_autoencoder">VAE</a>를 이용한 멀티모달 CLIP 네트웍을 좀 더 깊게 파헤쳐 보도록 한다. 앞서 설명된 트랜스포머, 임베딩과 관련된 개념에 익숙하다면, CLIP을 이해하고 구현하는 것이 그리 어렵지는 않을 것이다. 이 글에서 나오는 예시, 개념, 응용에 대한 상세 내용은 다음 링크와 글 마지막에 표시된 레퍼런스를 참고한다.</div><div><ul style="line-height: 1.4; list-style-image: initial; list-style-position: initial; margin: 0.5em 0px; padding: 0px 2.5em;"><li style="border: none; color: #333333; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; font-size: 14.85px; margin: 0px 0px 0.25em; padding: 0px;"><a href="https://daddynkidsmakers.blogspot.com/2017/02/blog-post_24.html" style="color: #992211; text-decoration-line: none;">머신러닝 딥러닝 신경망 개념, 종류 및 개발</a></li><li style="border: none; color: #333333; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; font-size: 14.85px; margin: 0px 0px 0.25em; padding: 0px;"><a href="https://daddynkidsmakers.blogspot.com/2022/11/3.html" style="color: #992211; text-decoration-line: none;">딥러닝 기반 3차원 스캔 데이터, 포인트 클라우드 학습</a></li><li style="border: none; color: #333333; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; font-size: 14.85px; margin: 0px 0px 0.25em; padding: 0px;"><a href="https://daddynkidsmakers.blogspot.com/2023/03/generative-ai.html" style="color: #992211; text-decoration-line: none;">생성(Generative) AI 오픈소스 딥러닝 모델 Stable Diffusion, ControlNet 개념 및 ComfyUI 사용법</a></li><li style="border: none; color: #333333; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; font-size: 14.85px; margin: 0px 0px 0.25em; padding: 0px;"><a href="https://daddynkidsmakers.blogspot.com/2021/10/blog-post.html">어텐션 기반 트랜스포머 딥러닝 모델 이해, 활용 사례 및 파치토치를 통한 간단한 사용방법 소개</a></li><li style="border: none; margin: 0px 0px 0.25em; padding: 0px;"><span face="Arial, Tahoma, Helvetica, FreeSans, sans-serif" style="color: #333333;"><span style="font-size: 14.85px;"><a href="https://daddynkidsmakers.blogspot.com/2024/02/blog-post.html">트랜스포머 디코더 핵심 코드 구현을 통한 동작 메커니즘 이해하기</a></span></span></li><li style="border: none; color: #333333; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; font-size: 14.85px; margin: 0px 0px 0.25em; padding: 0px;"><a href="https://daddynkidsmakers.blogspot.com/2023/09/llama2.html">ChatGPT와 같은 생성AI 서비스 개발을 위한 간단한 LLAMA-2 설치와 사용법</a></li><li style="border: none; color: #333333; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; font-size: 14.85px; margin: 0px 0px 0.25em; padding: 0px;"><a href="https://github.com/mac999/computer_vision_deeplearning/tree/main" style="color: #992211; text-decoration-line: none;">Computer_vision_deeplearning: computer vision based on deep learning lecture materials, github</a></li></ul><div><span face="Arial, Tahoma, Helvetica, FreeSans, sans-serif" style="color: #333333;"><span style="font-size: 14.85px;"><br /></span></span></div></div><div><b>머리말</b></div><div><div>OpenAI에서 개발한 CLIP 모델은 공유 임베딩 공간 내에서 이미지 및 텍스트 형식을 통합하는 것을 목표로 했다. 이 개념은 기술과 함께 이미지와 텍스트를 넘어 다른 양식을 수용한다(멀티모달). 예를 들어, 유튜브 등 비디오 애플리케이션 내에서 텍스트 검색 성능을 개선하기 위해 공통 임베딩 공간에서 비디오 및 텍스트 형식을 결합하여 모델을 학습시켰다.</div><div><br /></div><div>사실, 임베딩 텐서를 잠재 공간(<a href="https://en.wikipedia.org/wiki/Latent_space">Latent Space</a>)으로 이기종 데이터를 변환, 계산, 역변환할 수 있다는 아이디어는 <a href="https://en.wikipedia.org/wiki/Variational_autoencoder">VAE</a> 기술, 구글의 트랜스포머 논문(2017)을 통해 개발자들 사이에 암시되어 있었다. 이를 실제로 시도해본 연구가 CLIP이다. </div><div><br /></div><div>참고로, CLAP(Contrastive Language-Audio Pretraining)는 동일한 임베딩 공간 내에서 텍스트와 오디오 형식을 통합하는 또 다른 모델로, 오디오 애플리케이션 내에서 검색 기능을 개선하는 데 유용하다.</div><div><br /></div><div>CLIP은 다음 응용에 유용하다.</div><div><ul style="text-align: left;"><li>이미지 분류 및 검색: CLIP은 이미지를 자연어 설명과 연결하여 이미지 분류 작업에 사용할 수 있다. 사용자가 텍스트 쿼리를 사용하여 이미지를 검색할 수 있는 보다 다양하고 유연한 이미지 검색 시스템을 허용한다.</li><li>콘텐츠 조정: CLIP은 부적절하거나 유해한 콘텐츠를 식별하고 필터링하기 위해 이미지와 함께 제공되는 텍스트를 분석하여 온라인 플랫폼의 콘텐츠를 조정하는 데 사용할 수 있다.</li></ul></div><div>참고로, Meta AI는 최근 이미지, 텍스트, 오디오, 깊이, 열, IMU 데이터 등 6가지 양식에 걸쳐 공동 임베딩을 학습하는 ImageBind를 출시했다. 두 가지 모달리티를 수용하는 최초의 대규모 AI 모델인 CLIP은 ImageBind 및 기타 다중 모달리티 AI 시스템을 이해하기 위한 전제 조건이다.</div><div><ul style="text-align: left;"><li><a href="https://daddynkidsmakers.blogspot.com/2023/05/imagebind.html">오디오, 영상, 텍스트, 센서, 3D깊이맵 멀티모달 딥러닝 모델 페이스북 imagebind 설치 및 사용기</a></li></ul></div><div>CLIP은 배치 내에서 어떤 N × N (이미지, 텍스트) 쌍이 실제 일치하는지, 예측하도록 설계되었다. CLIP은 이미지 인코더와 텍스트 인코더의 공동 학습을 통해, 멀티모달 임베딩 공간을 만든다. CLIP 손실은 트랜스포머의 어텐션 모델을 사용하여, 학습 데이터 배치에서 N개 쌍에 대한 이미지와 텍스트 임베딩 간의 코사인 유사성을 최대화하는 것을 목표로 한다. </div><div><br /></div><div>다음은 이를 설명하는 의사코드이다.</div></div><ol style="text-align: left;"><li>img_en = image_encoder(I) # [n, d_i] 이미지 임베딩 인코딩를 통한 특징 추출 </li><li>txtxt_emdn = textxt_emdncoder(T) # [n, d_t] 텍스트 임베딩 인코딩을 통한 특징 추출</li><li>img_emd = l2_normalize(np.dot(img_en, W_i), axis=1) # I x W 결합(조인트) 멀티모달 임베딩 텐서 계산</li><li>txt_emd = l2_normalize(np.dot(txtxt_emdn, W_t), axis=1) # T x W 결합(조인트) 멀티모달 임베딩 텐서 계산</li><li>logits = np.dot(img_emd, txt_emd.T) * np.exp(t) # I x T * E^t 함수 이용한 [n, n]코사인 유사도 계산</li><li>labels = np.arange(n)</li><li>loss_i = cross_entropy_loss(logits, labels, axis=0) # 이미지 참값 logits과 예측된 label간 손실</li><li>loss_t = cross_entropy_loss(logits, labels, axis=1) # 텍스트 참값 logits과 예측된 label간 손실</li><li>loss = (loss_i + loss_t)/2 # 이미지, 텍스트 손실 평균값</li></ol><div><div>실제 OpenAI 논문에는 다음과 같이 기술되어 있다(동일하다).</div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEiSfakACozaO4J8tVKZ1B1pUhtCCQ_R5e9FVNyhWgMlL073KcFtEAe6K6Xz_ZAm0SybT201Wu4tGKV_D2LsJuyuhU0gdlOnJnvs6sblV3OzZ_sYbSeqMSdHB4tOtrFIG-cksjrflSLs3FAUeKwKpUum7cudw4V-L9GwSvT67j7blQISQ4AGV-piEp5LcKg2" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="442" data-original-width="413" height="400" src="https://blogger.googleusercontent.com/img/a/AVvXsEiSfakACozaO4J8tVKZ1B1pUhtCCQ_R5e9FVNyhWgMlL073KcFtEAe6K6Xz_ZAm0SybT201Wu4tGKV_D2LsJuyuhU0gdlOnJnvs6sblV3OzZ_sYbSeqMSdHB4tOtrFIG-cksjrflSLs3FAUeKwKpUum7cudw4V-L9GwSvT67j7blQISQ4AGV-piEp5LcKg2=w373-h400" width="373" /></a></div></div><div>이제 각 단계를 개발하는 과정을 확인해보자.</div><div><br /></div><div><b>모델 아키텍처 및 학습</b></div><div>ClIP는 비전 및 텍스트 데이터 세트를 인코딩하기 위한 백본으로 두 개의 개별 아키텍처를 사용하다.</div><div><ul style="text-align: left;"><li>image_encoder: 이미지 인코딩을 담당하는 신경망 아키텍처(예: ResNet 또는 Vision Transformer).</li><li>text_encoder: 텍스트 정보 인코딩을 담당하는 신경망 아키텍처(예: CBOW, BERT 또는 Text Transformer).</li></ul></div><div>원래 CLIP 모델은 CLIP 모델을 학습하는 데 사용한 데이터 세트(4억 개의 이미지-텍스트 쌍)의 양이 많기 때문에 사전 학습된 가중치를 사용해 학습된다. 여기서는 컴퓨터 비전 분야에서 유명한 ResNet 딥러닝 모델 및 자연어 처리에 사용되는 DistilBert 딥러닝 모델에서 사전 학습된 가중치 파일을 사용한다.</div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjx8OCwTpgV6uX4MGBfQnmp3VV8IbQZAZboVMILOoE79RddS6y2fyR3KRU4iYwM56BWBpqUDPbjjcJnw7SjWcBgoUi2NM-7774uA0B2Qd3doUxbeLAndRrDf2Vn8OSFEWJar8VFA_MwtUvAC2_AI2blxMNvhunOIfrTUJQ6ghdeXuSnjVlEGC0I5IXftBtk" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="424" data-original-width="655" height="207" src="https://blogger.googleusercontent.com/img/a/AVvXsEjx8OCwTpgV6uX4MGBfQnmp3VV8IbQZAZboVMILOoE79RddS6y2fyR3KRU4iYwM56BWBpqUDPbjjcJnw7SjWcBgoUi2NM-7774uA0B2Qd3doUxbeLAndRrDf2Vn8OSFEWJar8VFA_MwtUvAC2_AI2blxMNvhunOIfrTUJQ6ghdeXuSnjVlEGC0I5IXftBtk" width="320" /></a></div><div class="separator" style="clear: both; text-align: center;">CLIP 텍스트 인코더, 이미지 인코더 어텐션 스코어 계산 아키텍처 (CLIP, Open AI, 2021)</div></div><div><div class="separator" style="clear: both; text-align: center;"><br /></div></div><div>CLIP 모델은 flickr30k 데이터 세트를 사용하여 훈련된다. 이 데이터 세트는 31,000개 이상의 이미지로 구성되며, 각 이미지에는 최소 5개의 캡션이 라벨링되어 있다. 이 예제에서는 각 이미지에 대해 두 개의 캡션을 사용하여, 총 62,000개의 이미지 및 텍스트 쌍을 학습에 사용하다. 이미지-캡션 텍스트 쌍을 유사도 계산해, 이미지 검색 목적으로 인코더 모델을 학습시킨다. 참고로, GitHub에는 164,000개의 이미지 및 텍스트 쌍이 있는 MS-COCO 데이터 세트에서 모델을 학습시키는 코드도 포함되어 있다.</div></div><div><br /></div><div><b>학습 데이터 준비, 특징 인코딩 및 잠재 공간 투영하기</b></div><div>이미지-텍스트 N쌍의 학습 데이터는 다음과 같은 구조로 사용된다. </div><ul style="text-align: left;"><li>I[n, h, w, c]: n은 미니배치 크기, h와 c는 이미지 해상도, c는 이미지 채널 수. 예) [128,224,224,3]</li><li>T[n, l]: n은 미니배치 크기, l은 텍스트 시퀀스 길이. 예) [128, 32]</li></ul><div>학습할 배치 데이터를 준비하고, 다음과 같이 이미지, 텍스트 특징 추출을 위해 인코딩 모델을 준비한다. </div><div><div>I_f = models.resnet34(pretrained=True) </div><div>T_f= AutoModel.from_pretrained("distilbert-base-multilingual-cased") </div></div><div><br /></div><div>인코딩된 이미지-텍스트 특징 텐서를 신경망 W 텐서에 의해 투영한다. 투영된 결과는 차원이 축소된 임베딩 텐서가 된다. 이 임베딩 텐서는 잠재 공간 차원의 특정 위치에 표현될 것이다.</div><div><ul style="text-align: left;"><li>W_i[d_i, d_e]: 인코딩된 이미지 특징 텐서를 잠재 공간에 맵핑하기 위한 투영 텐서 W_i를 준비한다. W 텐서 행렬은 d_i 차원을 d_e 차원으로 맵핑한다.</li><li>W_t[d_t, d_e]: W_i와 동일한 개념으로, 텍스트 특징 텐서를 잠재 공간으로 맵핑하는 W_t 텐서 행렬을 준비한다.</li></ul><div style="text-align: center;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhqc4SqOXrsCTCi1fi7QngNa1_w1hqWgCwUslpLaRo069WoC1kReO20YryX1Sqt0yrB4c-tWEssNimNbjcQfLrZVPWyQpACkYcYzWPcJKVo0v77YqE2sT7PTBF-HnfS-ZBN4KBReunPNV5BzgiFYExHEGEfeQgNTOsoquHfb1FRxHeI6BrMZxt_m2x_WZFE" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="620" data-original-width="1511" height="153" src="https://blogger.googleusercontent.com/img/a/AVvXsEhqc4SqOXrsCTCi1fi7QngNa1_w1hqWgCwUslpLaRo069WoC1kReO20YryX1Sqt0yrB4c-tWEssNimNbjcQfLrZVPWyQpACkYcYzWPcJKVo0v77YqE2sT7PTBF-HnfS-ZBN4KBReunPNV5BzgiFYExHEGEfeQgNTOsoquHfb1FRxHeI6BrMZxt_m2x_WZFE=w374-h153" width="374" /></a></div></div><div style="text-align: center;">잠재 공간 차원 상의 Text-Image Pair 표현(Nature.com, 2022)</div><div style="text-align: center;"><br /></div><div>투영 연산은 가중치가 학습되는 W_t 투영 텐서를 사용해야 하므로, 두 개의 선형 레이어를 설계해야 한다. 그러므로, 투영 가중치는 Activate Gradient 를 가진다.</div><div><br /></div></div><div>이는 다음과 같이 구현될 수 있다.</div><div><div>class Projection(nn.Module): # W_t 투영 텐서 학습 모듈 정의</div><div> def __init__(self, d_in: int, d_out: int, p: float=0.5) -> None:</div><div> super().__init__()</div><div> self.linear1 = nn.Linear(d_in, d_out, bias=False) # 단순히 두개의 선형 레이어 가짐</div><div> self.linear2 = nn.Linear(d_out, d_out, bias=False)</div><div> self.layer_norm = nn.LayerNorm(d_out) # 레이어 가중치 정규화</div><div> self.drop = nn.Dropout(p) # 드롭 아웃 처리</div><div><br /></div><div> def forward(self, x: torch.Tensor) -> torch.Tensor:</div><div> embed1 = self.linear1(x)</div><div> embed2 = self.drop(self.linear2(F.gelu(embed1)))</div><div> embeds = self.layer_norm(embed1 + embed2)</div><div> return embeds</div></div><div><br /></div><div>이 전체 과정은 다음 다이어그램과 같이 표현될 수 있다.</div><div style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjtdzTrcdRpwfK4cZC-DYFkFUKh0IHXeZTxl502Xs8CU89GMj0ppcE7dbmcJc5PWXJuZ7l0F9Pwf-ClbuFrXDMbiN7gEUCK215uQ75ckkevaBBp7EmkCJkusN0F9nKMBGDtIJmjYg0_y0sMNZQcfeYr46TyBkSoCK4PToVhCbEZ0M3qnvx1jnIW0Zl-GEl2" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="585" data-original-width="1597" height="174" src="https://blogger.googleusercontent.com/img/a/AVvXsEjtdzTrcdRpwfK4cZC-DYFkFUKh0IHXeZTxl502Xs8CU89GMj0ppcE7dbmcJc5PWXJuZ7l0F9Pwf-ClbuFrXDMbiN7gEUCK215uQ75ckkevaBBp7EmkCJkusN0F9nKMBGDtIJmjYg0_y0sMNZQcfeYr46TyBkSoCK4PToVhCbEZ0M3qnvx1jnIW0Zl-GEl2=w477-h174" width="477" /></a></div><div style="text-align: center;">CLIP 아키텍처 전체 구조(<a href="https://openai.com/research/clip" style="text-align: left;">CLIP</a>, <span style="text-align: left;">Contrastive Language-Image Pre-Training. 2021)</span></div><div style="text-align: center;"><br /></div><div>이를 코드로 구현하면 다음과 같다.</div><div><div><span style="font-size: x-small;">class VisionEncoder(nn.Module): # 이미지 인코더 모듈 정의</span></div><div><span style="font-size: x-small;"> def __init__(self, d_out: int) -> None:</span></div><div><span style="font-size: x-small;"> super().__init__()</span></div><div><span style="font-size: x-small;"> base = models.resnet34(pretrained=True)</span></div><div><span style="font-size: x-small;"> d_in = base.fc.in_features</span></div><div><span style="font-size: x-small;"> base.fc = nn.Identity()</span></div><div><span style="font-size: x-small;"> self.base = base</span></div><div><span style="font-size: x-small;"> self.projection = Projection(d_in, d_out) # ResNet에서 얻은 이미지 특징을 잠재 공간으로 프로젝션.</span></div><div><span style="font-size: x-small;"> for p in self.base.parameters(): # 사전 학습 모델 사용하므로, 학습이 필요 없음 설정</span></div><div><span style="font-size: x-small;"> p.requires_grad = False</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;"> def forward(self, x):</span></div><div><span style="font-size: x-small;"> projected_vec = self.projection(self.base(x))</span></div><div><span style="font-size: x-small;"> projection_len = torch.norm(projected_vec, dim=-1, keepdim=True)</span></div><div><span style="font-size: x-small;"> return projected_vec / projection_len # 텐서값 정규화</span></div><div><br /></div><div><span style="font-size: x-small;">class TextEncoder(nn.Module): # 이미지 인코더와 동일한 방식으로 텍스트 인코더 모듈 정의</span></div><div><span style="font-size: x-small;"> def __init__(self, d_out: int) -> None:</span></div><div><span style="font-size: x-small;"> super().__init__()</span></div><div><span style="font-size: x-small;"> self.base = AutoModel.from_pretrained(Config.text_model)</span></div><div><span style="font-size: x-small;"> self.projection = Projection(Config.transformer_embed_dim, d_out)</span></div><div><span style="font-size: x-small;"> for p in self.base.parameters():</span></div><div><span style="font-size: x-small;"> p.requires_grad = False</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;"> def forward(self, x):</span></div><div><span style="font-size: x-small;"> out = self.base(x)[0] # BERT 인코더 이용해 텍스트 특징 텐서 획득</span></div><div><span style="font-size: x-small;"> out = out[:, 0, :] # 필요한 특징 부분만 추출. get CLS token output</span></div><div><span style="font-size: x-small;"> projected_vec = self.projection(out) # 잠재 공간으로 프로젝션 </span></div><div><span style="font-size: x-small;"> projection_len = torch.norm(projected_vec, dim=-1, keepdim=True) </span></div><div><span style="font-size: x-small;"> return projected_vec / projection_len # 텐서값 정규화</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;">vision_encoder = VisionEncoder(Config.embed_dim)</span></div><div><span style="font-size: x-small;">I_e = vision_encoder(images)</span></div><div><span style="font-size: x-small;">caption_encoder = TextEncoder(Config.embed_dim) </span></div><div><span style="font-size: x-small;">T_e = caption_encoder(text["input_ids"])</span></div></div><div><br /></div><div><b>유사도 계산을 위한 어텐션 모델 적용과 CLIP 손실 함수 설계</b></div><div>트랜스포머 논문에 적용된 토큰화된 텐서 간 cosine 유사도 계산은 CLIP에서 다음과 같이 적용된다. </div><div><br /></div><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px;"><div style="text-align: left;">logits = np.dot(I_e, T_e.T) * np.exp(t)</div></blockquote><p>이는 다음과 같이 행렬곱셈 연산자 '@' 구현된다(np.dot과 동일).</p><p>logits = T_e @ T_e.T</p><p>CLIP의 학습 목표는 잠재 공간 차원에서 표현된 이미지와 텍스트 중 관련된 쌍의 거리를 가깝게 조정하는 것이다. 그러므로, 손실 함수는 다음과 같이 설계된다. </p><p></p><ul style="text-align: left;"><li>labels = np.arange(n): 배치 인덱스를 표현하는 라벨을 생성</li><li>loss_i = cross_entropy_loss(logits, labels, axis=0): 이미지 축에 따라 교차 엔트로피 손실 계산</li><li>loss_t = cross_entropy_loss(logits, labels, axis=1): 텍스트 축에 따라 교차 엔트로피 손실 계산</li><li>loss = (loss_i + loss_t) / 2: 이미지, 텍스트 손실 평균 계산</li></ul><div style="text-align: center;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhHaBOG7yvL2WQldOGEINIxkqb8K_bX0PuK_raY12bgWKlAWI6Lp1DD01ZEQP85Hai9DVE5qXAm3fJiTdfmxIMHQrldy4NHrVIlXhuMWRncaxIrVvrWJtbyDXMpHRlYuPmJFud8YctawiTNK89vuJi9nQhbcjjSftN9nd-e9YFuG8iWadkpgbi4eV1u1_MS" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="675" data-original-width="1024" height="242" src="https://blogger.googleusercontent.com/img/a/AVvXsEhHaBOG7yvL2WQldOGEINIxkqb8K_bX0PuK_raY12bgWKlAWI6Lp1DD01ZEQP85Hai9DVE5qXAm3fJiTdfmxIMHQrldy4NHrVIlXhuMWRncaxIrVvrWJtbyDXMpHRlYuPmJFud8YctawiTNK89vuJi9nQhbcjjSftN9nd-e9YFuG8iWadkpgbi4eV1u1_MS=w367-h242" width="367" /></a></div></div><div style="text-align: center;">잠재 공간 차원에서 Text-Image Pair 간 거리(Synthesis.AI, 2023)</div><div><br /></div><div>손실함수 구현은 다음과 같다.</div><div>def CLIP_loss(logits: torch.Tensor) -> torch.Tensor: # 로짓은 텐서 행렬로 입력됨</div><div style="text-align: left;"> n = logits.shape[1] # 샘플 수<br /> labels = torch.arange(n) # 라벨 텐서 획득<br /> loss_i = F.cross_entropy(logits.transpose(0,1), labels, reduction="mean") # 이미지 행 손실<br /> loss_t = F.cross_entropy(logits, labels, reduction="mean") # 텍스트 행 손실<br /> loss = (loss_i + loss_t) / 2 # 평균 손실값 계산<br /> return loss</div><div style="text-align: left;"><br /></div><div style="text-align: left;"><b>최종 CLIP 모델 구현</b></div><div>앞서 정의한 모든 부분을 합하면, 다음과 같이 CLIP 모델이 구현된다. </div><div><div>class CustomModel(nn.Module):</div><div> def __init__(self, lr: float = 1e-3) -> None:</div><div> super().__init__()</div><div> self.vision_encoder = VisionEncoder(Config.embed_dim)</div><div> self.caption_encoder = TextEncoder(Config.embed_dim)</div><div> self.tokenizer = Tokenizer(AutoTokenizer.from_pretrained(Config.text_model))</div><div> self.lr = lr</div><div> self.device = "cuda" if torch.cuda.is_available() else "cpu"</div><div><br /></div><div> def forward(self, images, text):</div><div> text = self.tokenizer(text).to(self.device)</div><div><br /></div><div> image_embed = self.vision_encoder(images)</div><div> caption_embed = self.caption_encoder(text["input_ids"])</div><div> similarity = caption_embed @ image_embed.T</div><div><br /></div><div> loss = CLIP_loss(similarity)</div><div> img_acc, cap_acc = metrics(similarity)</div><div> return loss, img_acc, cap_acc</div></div><div><br /></div><div><b>구현된 CLIP 모델로 학습해 보기</b></div><div>제대로 학습되는 지 확인해 본다. 학습 데이터는 <a href="https://huggingface.co/datasets/nlphuji/flickr30k">flickr30k 데이터셋</a>을 사용한다. 학습 데이터 구조는 다음과 같다.</div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj3Vetk23WwayGNgFY3SFQfCQOXY9ni3rkNW6D6YtJlAHjjlEfaSqKEya7L7cQTqp6DA7sTo93SoR6hDGpZEWwH-AOEMLXg8lNXtCbl33kMeZsUNE_SEk9zdB4fswwQGgIh59e4CoYoiy8xzqbvTmXd6s_JnNfnwNootMes96wZdBueLAz33txvcYdeqRIG/s1965/t1.PNG" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1006" data-original-width="1965" height="205" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj3Vetk23WwayGNgFY3SFQfCQOXY9ni3rkNW6D6YtJlAHjjlEfaSqKEya7L7cQTqp6DA7sTo93SoR6hDGpZEWwH-AOEMLXg8lNXtCbl33kMeZsUNE_SEk9zdB4fswwQGgIh59e4CoYoiy8xzqbvTmXd6s_JnNfnwNootMes96wZdBueLAz33txvcYdeqRIG/w400-h205/t1.PNG" width="400" /></a></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjuzrDBmnMNOfBXgTxQzcWyeO2zM4dX_53IhTGgWMvcpfQdfp4-oWnIP6DEzeCOWCNOQ4qDIf06YtGHV70nLlgorVbPLcFxiK5yM9skMIBLd6BqKsbY3_j-yd9a_YMu0WAbtea0noQ30jcs-zJLw7dyDGVSgXirtFTySOuud6yz4f-g451IlRxAAxXB7HqD/s665/t3.PNG" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="314" data-original-width="665" height="189" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjuzrDBmnMNOfBXgTxQzcWyeO2zM4dX_53IhTGgWMvcpfQdfp4-oWnIP6DEzeCOWCNOQ4qDIf06YtGHV70nLlgorVbPLcFxiK5yM9skMIBLd6BqKsbY3_j-yd9a_YMu0WAbtea0noQ30jcs-zJLw7dyDGVSgXirtFTySOuud6yz4f-g451IlRxAAxXB7HqD/w400-h189/t3.PNG" width="400" /></a></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj5gJI5kvlIcqxAcUiI6UeTZNEWVjnf1U1Fu7daL9_7l0VhkIyeNqfEiwQ5ZoSEDXLb-p73Q-84yoOzI35OY9thkZpDyIjMU_T-kPJmngC8Mxv9C9_L8nXJCROa08oe4iVL7ajiqlvi2YalANqGHiCwVS4G7RA-mpVYPmD841nBK7GpK3stoOFn4CEc5RMn/s1914/t2.PNG" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="622" data-original-width="1914" height="130" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj5gJI5kvlIcqxAcUiI6UeTZNEWVjnf1U1Fu7daL9_7l0VhkIyeNqfEiwQ5ZoSEDXLb-p73Q-84yoOzI35OY9thkZpDyIjMU_T-kPJmngC8Mxv9C9_L8nXJCROa08oe4iVL7ajiqlvi2YalANqGHiCwVS4G7RA-mpVYPmD841nBK7GpK3stoOFn4CEc5RMn/w400-h130/t2.PNG" width="400" /></a></div><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjbps49ZMJ-xx2LvW_rb4bwbF9b2fMGu8Bwdm1VMRUjAgPlp1Kot03Ylo9O5KH8eN_PhkMTD4Ock7RSUcQmZ8lvPTpYBfQgYT70mmdCVvzBxbb6cN_cPj0E3yfpK_F8hml-VbaComwTKzPWPagoe3Tya2ZAZrK2L6FXPZ0EjOt7E-BMJCTpydnwvMXz1BN3" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="349" data-original-width="844" height="165" src="https://blogger.googleusercontent.com/img/a/AVvXsEjbps49ZMJ-xx2LvW_rb4bwbF9b2fMGu8Bwdm1VMRUjAgPlp1Kot03Ylo9O5KH8eN_PhkMTD4Ock7RSUcQmZ8lvPTpYBfQgYT70mmdCVvzBxbb6cN_cPj0E3yfpK_F8hml-VbaComwTKzPWPagoe3Tya2ZAZrK2L6FXPZ0EjOt7E-BMJCTpydnwvMXz1BN3=w400-h165" width="400" /></a></div></div><div class="separator" style="clear: both; text-align: center;"><a href="https://huggingface.co/datasets/nlphuji/flickr30k/tree/main">flickr30k(huggingface)</a></div><br /></div><div>학습 데이터 배치 생성을 위해, 토치의 데이터셋을 사용자 정의한다.</div><div><div><span style="font-size: x-small;">from torch.utils.data import DataLoader</span></div><div><span style="font-size: x-small;">from datasets import load_dataset</span></div><div><span style="font-size: x-small;">from torchvision import transforms</span></div><div><span style="font-size: x-small;">from PIL import Image</span></div><div><span style="font-size: x-small;">import torch</span></div><div><span style="font-size: x-small;">from torchvision import transforms</span></div><div><span style="font-size: x-small;">from PIL import Image</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;">class Flickr30kDataset(torch.utils.data.Dataset): # <a href="https://huggingface.co/datasets/nlphuji/flickr30k">flickr30k 데이터셋</a></span></div><div><span style="font-size: x-small;"> def __init__(self):</span></div><div><span style="font-size: x-small;"> self.dataset = load_dataset("nlphuji/flickr30k", cache_dir="./huggingface_data") # 허깅페이스의 데이터획득</span></div><div><span style="font-size: x-small;"> self.transform = transforms.Compose([</span></div><div><span style="font-size: x-small;"> transforms.Resize((224, 224)),</span></div><div><span style="font-size: x-small;"> transforms.ToTensor(),</span></div><div><span style="font-size: x-small;"> ]) # 학습을 위해 이미지 224,224 해상도로 정규화</span></div><div><span style="font-size: x-small;"> self.cap_per_image = 2 # 인코딩될 텍스트 캡션은 2개만 사용</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;"> def __len__(self):</span></div><div><span style="font-size: x-small;"> return self.dataset.num_rows["test"] * self.cap_per_image # 학습 데이터수=test 이미지들 갯수 x 해당 이미지의 캡션 수</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;"> def __getitem__(self, idx):</span></div><div><span style="font-size: x-small;"> original_idx = idx // self.cap_per_image # 입력 데이터 인덱스에 대한 이미지 당 캡션수의 몫 획득</span></div><div><span style="font-size: x-small;"> image = self.dataset["test"][original_idx]["image"].convert("RGB") </span></div><div><span style="font-size: x-small;"> image = self.transform(image) # 224x224 텐서로 이미지 변환</span></div><div><span style="font-size: small;"> caption = self.dataset["test"][original_idx]["caption"][idx % self.cap_per_image] # 캡션 획득</span></div><div><span style="font-size: small;"><br /></span></div><div><span style="font-size: x-small;"> return {"image": image, "caption": caption} # 캡션에 대한 해당 이미지, 텍스트 리턴</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: small;">flickr30k_custom_dataset = Flickr30kDataset()</span></div></div><div><br /></div><div>앞서 모델 파라메터 설정에 사용된 Config 클래스는 다음과 같이 설계된다. </div><div><div>from dataclasses import dataclass</div><div><br /></div><div>@dataclass</div><div>class Config:</div><div> embed_dim: int = 512 # 임베딩 차원 </div><div> transformer_embed_dim: int = 768 # 트랜스포머 임베딩 차원</div><div> max_len: int = 32 # 텍스트 최대 길이 </div><div> text_model: str = "distilbert-base-multilingual-cased" # 텍스트 특징 추출 인코더 </div><div> epochs: int = 3 # 학습 에포크</div><div> batch_size: int = 128 # 배치 크기 </div></div><div><br /></div><div>이제, 학습 데이터를 로딩해 확인해 본다. </div><div><div>clip_dataloader = DataLoader(flickr30k_custom_dataset, batch_size=BATCH_SIZE, shuffle=True, num_workers=4) # 데이터 로더 객체 생성</div></div><div><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhWAJceA01yMLgIBbBrMPs2Ks4vzAz3tN4dbosNPz4jQ0mt49KASere-WMi0A6tsaOs7blqpnlfktch3025sBKVU66S7wGO86Zon2q7nxkTuHeh4k-iWKZx4jcY4GPuz4yh_DegPHHxPCggM6C94EZv3PFX6NQmHOkJqlHchC-Z2jFjYCBd27b3tPiPkGhf" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="482" data-original-width="453" height="240" src="https://blogger.googleusercontent.com/img/a/AVvXsEhWAJceA01yMLgIBbBrMPs2Ks4vzAz3tN4dbosNPz4jQ0mt49KASere-WMi0A6tsaOs7blqpnlfktch3025sBKVU66S7wGO86Zon2q7nxkTuHeh4k-iWKZx4jcY4GPuz4yh_DegPHHxPCggM6C94EZv3PFX6NQmHOkJqlHchC-Z2jFjYCBd27b3tPiPkGhf" width="226" /></a></div></div></div><div class="separator" style="clear: both; text-align: center;">학습 데이터 일부 예시</div><div><br /></div>병렬 학습을 위해, GPU로 정의된 CLIP모델을 전송하고, 최적 해 탐색을 위한 ADAM 솔류션을 적용한다.<br /><div>model = CustomModel().to(device)</div><div>optimizer = torch.optim.Adam([</div><div> {'params': model.vision_encoder.parameters()},</div><div> {'params': model.caption_encoder.parameters()}</div><div>], lr=model.lr)</div></div><div><br /></div><div>이제, 다음과 같이 학습한다.</div><div><div><span style="font-size: x-small;">batch_zero = True</span></div><div><span style="font-size: x-small;">for epoch in range(start_epoch, num_epochs): # 에포크 횟수만큼 학습</span></div><div><span style="font-size: x-small;"> model.train()</span></div><div><span style="font-size: x-small;"> for batch in clip_dataloader: # 미니 배치 학습</span></div><div><span style="font-size: x-small;"> image = batch["image"].to(device) </span></div><div><span style="font-size: x-small;"> text = batch["caption"]</span></div><div><span style="font-size: x-small;"> loss, img_acc, cap_acc = model.common_step((image, text)) # 이미지-텍스트 쌍 유사도 계산</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;"> optimizer.zero_grad() # 역전파 학습</span></div><div><span style="font-size: x-small;"> loss.backward()</span></div><div><span style="font-size: x-small;"> optimizer.step()</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;"> if batch_zero: # 손실값 출력</span></div><div><span style="font-size: x-small;"> print(f"Epoch [{0}/{num_epochs}], Batch Loss: {loss.item()}")</span></div><div><span style="font-size: x-small;"> batch_zero = False</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;"> print(f"Epoch [{epoch+1}/{num_epochs}], Batch Loss: {loss.item()}")</span></div><div><span style="font-size: x-small;">print("Training complete.")</span></div></div><div><br /></div><div>앞서 설명을 확인해 보면, 기존에 연구 개발된 기술과 개념을 사용하였다는 것을 잘 알 수 있다. CLIP의 우수한 점은 멀티모달 데이터셋을 조인트하여 유사도 어텐션 스코어를 계산하는 방식을 잠재 공간에서 수행한다는 아이디어를 구현한 점이다. </div><div><br /></div><div><b>CLIP 실제 활용 예제 - 텍스트 기반 이미지 검색 </b></div><div>앞서 코드 분석을 통해, CLIP의 동작 메커니즘을 이해하였다. 이를 이용해, 두 멀티모달 데이터 간 유사도를 얻어본다. 예제에 사용할 멀티모달 데이터는 무작위 이미지, 텍스트 캡션을 사용한다.</div><div><br /></div><div>이 예제에서는 OpenAI에서 공개한 CLIP 코드를 직접 사용해 본다. </div><div><ul style="text-align: left;"><li><a href="https://github.com/openai/CLIP">openai/CLIP: CLIP (Contrastive Language-Image Pretraining), Predict the most relevant text snippet given an image</a></li></ul></div><div>다음과 같이 터미널에 명령을 실행한다. </div><div><div>conda install --yes -c pytorch pytorch=1.7.1 torchvision cudatoolkit=11.0</div><div>pip install ftfy regex tqdm</div><div>pip install git+https://github.com/openai/CLIP.git</div></div><div><br /></div><div>다음과 같이 파이썬 소스를 만든 후 실행해 본다. 이 예제는 텍스트와 함께 주어진 이미지에 대한 유사도를 계산하여 리턴한다. 이를 이용하면 입력 텍스트에 대한 영상 이미지를 검색할 수 있다. 예를 들어, 넷플릭스와 같은 비디오, 이미지 검색 엔진 등에 사용될 수 있다.</div><div><div><span style="font-size: x-small;">import os</span></div><div><span style="font-size: x-small;">import clip</span></div><div><span style="font-size: x-small;">import torch</span></div><div><span style="font-size: x-small;">from torchvision.datasets import CIFAR100</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;"># Load the model</span></div><div><span style="font-size: x-small;">device = "cuda" if torch.cuda.is_available() else "cpu"</span></div><div><span style="font-size: x-small;">model, preprocess = clip.load('ViT-B/32', device)</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;"># Download the dataset</span></div><div><span style="font-size: x-small;">cifar100 = CIFAR100(root=os.path.expanduser("~/.cache"), download=True, train=False)</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;"># Prepare the inputs</span></div><div><span style="font-size: x-small;">image, class_id = cifar100[3637]</span></div><div><span style="font-size: x-small;">image_input = preprocess(image).unsqueeze(0).to(device)</span></div><div><span style="font-size: x-small;">text_inputs = torch.cat([clip.tokenize(f"a photo of a {c}") for c in cifar100.classes]).to(device)</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;"># Calculate features</span></div><div><span style="font-size: x-small;">with torch.no_grad():</span></div><div><span style="font-size: x-small;"> image_features = model.encode_image(image_input)</span></div><div><span style="font-size: x-small;"> text_features = model.encode_text(text_inputs)</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;"># Pick the top 5 most similar labels for the image</span></div><div><span style="font-size: x-small;">image_features /= image_features.norm(dim=-1, keepdim=True)</span></div><div><span style="font-size: x-small;">text_features /= text_features.norm(dim=-1, keepdim=True)</span></div><div><span style="font-size: x-small;">similarity = (100.0 * image_features @ text_features.T).softmax(dim=-1)</span></div><div><span style="font-size: x-small;">values, indices = similarity[0].topk(5)</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;"># Print the result</span></div><div><span style="font-size: x-small;">print("\nTop predictions:\n")</span></div><div><span style="font-size: x-small;">for value, index in zip(values, indices):</span></div><div><span style="font-size: x-small;"> print(f"{cifar100.classes[index]:>16s}: {100 * value.item():.2f}%")</span></div></div><div><div class="separator" style="clear: both; text-align: center;"><br /></div></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjOTv5ooCmLJWeKGR8uB21dqtDp-OOmc9wPnegSgqj-MU0JhMlcLl49lIwbbXf-FtZ-x6GvFiJr5H3vWdRkhKHV8aKeFIfvqCg6afyoXs-xo6lkOS0DK29a0LI22co6u1qB6Ug7GVPaHAB0cwVDw4dKl-XTNludOvVAnX7BKcYP9mKGZCsgt_6ps2TgR2vH" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="304" data-original-width="441" height="160" src="https://blogger.googleusercontent.com/img/a/AVvXsEjOTv5ooCmLJWeKGR8uB21dqtDp-OOmc9wPnegSgqj-MU0JhMlcLl49lIwbbXf-FtZ-x6GvFiJr5H3vWdRkhKHV8aKeFIfvqCg6afyoXs-xo6lkOS0DK29a0LI22co6u1qB6Ug7GVPaHAB0cwVDw4dKl-XTNludOvVAnX7BKcYP9mKGZCsgt_6ps2TgR2vH=w233-h160" width="233" /></a></div><div class="separator" style="clear: both; text-align: center;">유사도 계산 결과</div><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: left;">다음은 "diagram", "dog", "cat" 와 다이어그램 특징을 입력하고 유사도를 예측한 것이다. </div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><span style="font-size: x-small;">import torch</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;">import clip</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;">from PIL import Image</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"><br /></span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;">device = "cuda" if torch.cuda.is_available() else "cpu"</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;">model, preprocess = clip.load("ViT-B/32", device=device)</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"><br /></span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;">image = preprocess(Image.open("f:/projects/multimodal/clip/CLIP.png")).unsqueeze(0).to(device)</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;">text = clip.tokenize(["a diagram", "a dog", "a cat"]).to(device)</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"><br /></span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;">with torch.no_grad():</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> image_features = model.encode_image(image)</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> text_features = model.encode_text(text)</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> </span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> logits_per_image, logits_per_text = model(image, text)</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> probs = logits_per_image.softmax(dim=-1).cpu().numpy()</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"><br /></span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;">print("Label probs:", probs) # prints: [[0.9927937 0.00421068 0.00299572]]</span></div></div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;">그 결과는 다음과 같이 예상한 다이어그램 유형 그림이 99.27% 유사하다고 예측되었다. </div><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgXHy2OrUDhdkAbV-Uk54u88xq_bpMSnM6cbe-__0iaKAz7gru_d8UWZH45z0HAk5L8P46c0bWrclyAJybDAs4AtK9Yd4SfDWo2mAGNvnTQojFG-IvSCRlhAXBWgVwAbF-mhvsv_nIpNOvHRYKhYInqj1h35-6sloJlbZTcx9ITjj_mdb558j5WBe9KUt9P" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="762" data-original-width="2162" height="113" src="https://blogger.googleusercontent.com/img/a/AVvXsEgXHy2OrUDhdkAbV-Uk54u88xq_bpMSnM6cbe-__0iaKAz7gru_d8UWZH45z0HAk5L8P46c0bWrclyAJybDAs4AtK9Yd4SfDWo2mAGNvnTQojFG-IvSCRlhAXBWgVwAbF-mhvsv_nIpNOvHRYKhYInqj1h35-6sloJlbZTcx9ITjj_mdb558j5WBe9KUt9P" width="320" /></a></div><div class="separator" style="clear: both;">입력 이미지(다이어그램)</div><div class="separator" style="clear: both;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEig5i6uc87WBcaR621kIUzYCJzU5MvYz99KsRmlmsKesn_Ju-nk6YHwl-HQmlsp83SBHcrG3CZ8eKmZgi5wSOHbeTAzBSEuB0BQaP8emm7KS76gOQzAoN7nhJtG4DNvjUux5Y7ePBdB5y2bW5JgswMu2hA1egJiKvVti1wKp9OToyIC6YZJ8zBRmOn78TwA" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="76" data-original-width="399" height="61" src="https://blogger.googleusercontent.com/img/a/AVvXsEig5i6uc87WBcaR621kIUzYCJzU5MvYz99KsRmlmsKesn_Ju-nk6YHwl-HQmlsp83SBHcrG3CZ8eKmZgi5wSOHbeTAzBSEuB0BQaP8emm7KS76gOQzAoN7nhJtG4DNvjUux5Y7ePBdB5y2bW5JgswMu2hA1egJiKvVti1wKp9OToyIC6YZJ8zBRmOn78TwA" width="320" /></a></div>출력 결과(캡션)</div><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgqk5CRVFqu-Eg9pujWbFoBiYHdp_Vu2S51F_rPdvH5ySg05h985lolc-l16hFSNLdeQ7BFDxuSYT392gATO_tSGXif7F6HI_KIXTD4hLnakCSIgZFXgGzKZUrcg4WykbqkKSRFACVaDw15RBWPCvjAv-8ubPLzCyv0uU17hiF3V751Etvqem0L3vlLsZyk" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="809" data-original-width="1863" height="207" src="https://blogger.googleusercontent.com/img/a/AVvXsEgqk5CRVFqu-Eg9pujWbFoBiYHdp_Vu2S51F_rPdvH5ySg05h985lolc-l16hFSNLdeQ7BFDxuSYT392gATO_tSGXif7F6HI_KIXTD4hLnakCSIgZFXgGzKZUrcg4WykbqkKSRFACVaDw15RBWPCvjAv-8ubPLzCyv0uU17hiF3V751Etvqem0L3vlLsZyk=w476-h207" width="476" /></a></div><div class="separator" style="clear: both; text-align: center;">텍스트-이미지 간 유사도 결과 예시(OpenAI)</div><br /></div><div><b>마무리</b></div><div>CLIP은 기존 개발된 딥러닝 기술(임베딩, 인코딩, 트랜스포머, ResNet 등)을 이용해 이종 데이터셋을 잠재 공간 차원에 표현하고 계산하는 방법을 제시한 훌륭한 멀티모달 학습 방법을 제시한다. 공개된 CLIP 기술은 많은 딥러닝 연구 개발자들이 멀티모달 생성AI 기술 개발을 촉진하는 계기가 되었다. 이제 <a href="https://daddynkidsmakers.blogspot.com/2017/02/blog-post_24.html">U-Net</a>, VAE 등을 응용하면, 멀티모달 생성AI를 구현할 수 있다.</div><div><br /></div><div><div><b>레퍼런스</b></div><div><ul><li>OpenAI, 2021, <a href="https://arxiv.org/abs/2103.00020" style="text-align: center;">Learning Transferable Visual Models From Natural Language Supervision</a></li><li><u>O</u><a href="https://github.com/openai/CLIP">penAI/CLIP: CLIP (Contrastive Language-Image Pretraining), Predict the most relevant text snippet given an image</a></li><li><a href="https://huggingface.co/docs/transformers/en/model_doc/clip">CLIP (huggingface.co)</a></li><li><a href="https://towardsdatascience.com/clip-model-and-the-importance-of-multimodal-embeddings-1c8f6b13bf72">CLIP Model and The Importance of Multimodal Embeddings | by Fahim Rustamy, PhD | Dec, 2023 | Towards Data Science</a></li><li><a href="https://huggingface.co/docs/transformers/tasks/question_answering">Question answering (huggingface.co)</a></li><li><a href="https://huggingface.co/docs/transformers/model_doc/codegen">CodeGen (huggingface.co)</a></li><li><a href="https://towardsdatascience.com/simple-implementation-of-openai-clip-model-a-tutorial-ace6ff01d9f2">Simple Implementation of OpenAI CLIP model: A Tutorial | Towards Data Science</a></li><li><a href="https://github.com/explosion/curated-transformers?tab=readme-ov-file">explosion/curated-transformers: 🤖 A PyTorch library of curated Transformer models and their composable components (github.com)</a></li><li><a href="https://jeas.springeropen.com/articles/10.1186/s44147-022-00159-4">MarianCG: a code generation transformer model inspired by machine translation | Journal of Engineering and Applied Science | Full Text (springeropen.com)</a></li><li><a href="https://huggingface.co/transformers/v3.2.0/custom_datasets.html">Fine-tuning with custom datasets — transformers 3.2.0 documentation (huggingface.co)</a></li><li><a href="https://towardsdatascience.com/fine-tune-transformer-models-for-question-answering-on-custom-data-513eaac37a80">Fine-Tune Transformer Models For Question Answering On Custom Data | by Skanda Vivek | Towards Data Science</a></li><li>Nature.com, 2022, <a href="https://www.nature.com/articles/s41598-022-23052-9">Contrastive language and vision learning of general fashion concepts</a></li><li>Synthesis AI, 2023, <a href="https://synthesis.ai/2023/05/16/generative-ai-iv-clip-and-multimodal-retrieval/">CLIP and multimodal retrieval: Generative AI IV</a></li></ul><b><div><b><br /></b></div>추신</b></div></div><div><div style="text-align: left;">요즘 너도 나도 생성AI 전문가다. 업계 있다 보면, 항상 무슨 아이디어를 누가 먼저 말했는 지 중요하게 여기는 분들이 있다. 하지만, 직접 연구 개발하지 않고 전문가라 스스로 칭하는 것은 본인과 주변의 발전에 도움이 되지 않는다(언젠가는 바닥이 들어난다). </div><div style="text-align: left;">연구자, 개발자, 엔지니어의 실제 파급력은 그 아이디어를 현실에 적용한 기술로 구현할 때 발생한다(아이디어만 제시하고 말하는 사람은 비지니스 컨설턴트, 아이디어 제안자라 한다. 본인 스스로 전문가라 말해도 남이 인정해 주지 않으면 무슨 의미가 있나). 전문가로써 인정받고 싶다면, 시간을 아껴서 깊게 공부하고, 직접 해봐야 한다.</div></div>Daddy Makerhttp://www.blogger.com/profile/02275758703365576780noreply@blogger.com0tag:blogger.com,1999:blog-5201956450461596914.post-56184675390485719182024-02-06T02:11:00.000-08:002024-02-08T20:34:12.623-08:00파이토치 트랜스포머 모델 사용자화를 통한 간단한 자연어 번역기 개발 방법 소개<div style="text-align: left;">이 글은 앞서 소개한 트랜스포머 모델을 사용자화하여, 간단한 자연어 번역기를 개발하는 방법을 보여준다. 자연어 번역기 개발을 위한 개념과 사용방법을 간략히 설명한다. 사용된 코드는 Github를 통해 제공된다. 참고로, 이 글에 있는 예제를 실행하기 위해서는 파이토치 라이브러리, GPU 드라이버가 컴퓨터에 설치되어 있어야 한다.</div><div style="text-align: left;"><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjJdF7LyfSwKdEIkfztMeBuqIni9JJtQSbTZ9nWw7Ooqny84upU3b-RLnKOn7NuauSMtTxA9M2al3h_TOWGXKYsMj1esJuxv1IsuKCdlywObKMmLCYpcXLdZIrha3IMEmG5mi9Z6QCaA9PKuQStPTiztd6F9AbUANnV0FvVfF7epEqehz9dA7qYTG85Q9x_" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="1011" data-original-width="2301" height="141" src="https://blogger.googleusercontent.com/img/a/AVvXsEjJdF7LyfSwKdEIkfztMeBuqIni9JJtQSbTZ9nWw7Ooqny84upU3b-RLnKOn7NuauSMtTxA9M2al3h_TOWGXKYsMj1esJuxv1IsuKCdlywObKMmLCYpcXLdZIrha3IMEmG5mi9Z6QCaA9PKuQStPTiztd6F9AbUANnV0FvVfF7epEqehz9dA7qYTG85Q9x_" width="320" /></a></div><div class="separator" style="clear: both; text-align: center;">트랜스포머 기반 자연어 번역 개념도</div><br /></div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><b>머리말</b></div><div class="separator" style="clear: both;">앞의 글에서 보았듯이 트랜스포머는 자연어 번역을 위해 개발된 것이다. 이 글은 자연어 번역을 위해, 미리 잘 만들어진 파이토치 트랜스포머를 이용하기로 한다. 이 글은 파이토치 관련 레퍼런스와 <a href="https://pytorch.org/tutorials/beginner/translation_transformer.html">예제</a>를 참고하였다. 이 글에서 구현되는 코드는 다음 github를 확인한다.</div><div class="separator" style="clear: both;"><ul style="text-align: left;"><li><a href="https://github.com/mac999/transformer/tree/main/de_to_eng_torch">transformer/de_to_eng_torch at main · mac999/transformer (github.com)</a></li></ul><div>트랜스포머에 대한 상세 동작 메커니즘은 다음 링크를 참고한다.</div><div><ul style="text-align: left;"><li><a href="https://daddynkidsmakers.blogspot.com/2023/12/blog-post.html">딥러닝 모델 트랜스포머 인코더 핵심 코드 구현을 통한 동작 메커니즘 이해하기</a></li><li><a href="https://daddynkidsmakers.blogspot.com/2024/02/blog-post.html">트랜스포머 디코더 핵심 코드 구현을 통한 동작 메커니즘 이해하기</a></li></ul><div><br /></div></div></div></div><div class="separator" style="clear: both; text-align: left;"><b>본문</b></div><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><div class="separator" style="clear: both;">TorchText 라이브러리는 자연어 데이터 세트를 만들기 위한 유틸리티가 있다. 이 예는 torchtext의 내장 데이터 세트를 사용하는 방법을 보여준다. 원시 텍스트 문장을 토큰화하고, 어휘를 구축하고, 토큰을 임베딩하여 텐서로 변환한다. 학습을 위해 Multi30k 데이터셋를 이용해 한 쌍의 원본-대상 문장을 준비한다. </div><div class="separator" style="clear: both;"><br /></div><div class="separator" style="clear: both;">torchtext 데이터 세트를 다운로드하려면 https://github.com/pytorch/data 에 따라 torchdata를 설치한다. 코드는 다음과 같다.</div><div class="separator" style="clear: both;"><div class="separator" style="clear: both;"><span style="font-size: x-small;">multi30k.URL["train"] = "https://raw.githubusercontent.com/neychev/small_DL_repo/master/datasets/Multi30k/training.tar.gz"</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;">multi30k.URL["valid"] = "https://raw.githubusercontent.com/neychev/small_DL_repo/master/datasets/Multi30k/validation.tar.gz"</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"><br /></span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;">SRC_LANGUAGE = 'de'</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;">TGT_LANGUAGE = 'en'</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"><br /></span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"># Place-holders</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;">token_transform = {}</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;">vocab_transform = {}</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"><br /></span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;">token_transform[SRC_LANGUAGE] = get_tokenizer('spacy', language='de_core_news_sm')</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;">token_transform[TGT_LANGUAGE] = get_tokenizer('spacy', language='en_core_web_sm')</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"><br /></span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;">print(token_transform[SRC_LANGUAGE]("Eine Gruppe von Menschen steht vor einem Iglu ."))</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;">print(token_transform[TGT_LANGUAGE]("A group of people are standing in front of an igloo .")) </span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"><br /></span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgwPz0JtPD5RwTvsEQSXa27-hqaXfB7qN2RaiPVnfl3dvAmzC5WjMn5u6k6YYgQ25KBu0tOY_Juzz6e9LZC_9zubSuGvkHey913Flq91mPQioduEIJxAUy0KQmenyjeOfj_B4JMOnMoPp6T0xskbwKTRntlboTwZWyEF035C7x4-5QBc1zxKeVlrPTBwNPk" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="111" data-original-width="1014" height="52" src="https://blogger.googleusercontent.com/img/a/AVvXsEgwPz0JtPD5RwTvsEQSXa27-hqaXfB7qN2RaiPVnfl3dvAmzC5WjMn5u6k6YYgQ25KBu0tOY_Juzz6e9LZC_9zubSuGvkHey913Flq91mPQioduEIJxAUy0KQmenyjeOfj_B4JMOnMoPp6T0xskbwKTRntlboTwZWyEF035C7x4-5QBc1zxKeVlrPTBwNPk=w474-h52" width="474" /></a></div><div class="separator" style="clear: both; text-align: center;">토큰화 결과</div><br /></span></div><div class="separator" style="clear: both;">소스, 대상 언어 토크나이저를 위해, 관련된 라이브러리 종속을 설치한다.</div></div><div class="separator" style="clear: both;"><div class="separator" style="clear: both;"><span style="font-size: x-small;">pip install -U torchdata</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;">pip install -U spacy</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;">python -m spacy download en_core_web_sm</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;">python -m spacy download de_core_news_sm</span></div></div><div class="separator" style="clear: both;"><br /></div><div class="separator" style="clear: both;"><div class="separator" style="clear: both;">학습 데이터를 다음과 같이 준비한다.</div><div class="separator" style="clear: both;"><span style="font-size: x-small;">token_transform[SRC_LANGUAGE] = get_tokenizer('spacy', language='de_core_news_sm')</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;">token_transform[TGT_LANGUAGE] = get_tokenizer('spacy', language='en_core_web_sm')</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"><br /></span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"># helper function to yield list of tokens</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;">def yield_tokens(data_iter: Iterable, language: str) -> List[str]:</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> language_index = {SRC_LANGUAGE: 0, TGT_LANGUAGE: 1}</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"><br /></span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> for data_sample in data_iter:</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> yield token_transform[language](data_sample[language_index[language]])</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"><br /></span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"># Define special symbols and indices</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;">UNK_IDX, PAD_IDX, BOS_IDX, EOS_IDX = 0, 1, 2, 3</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"># Make sure the tokens are in order of their indices to properly insert them in vocab</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;">special_symbols = ['<unk>', '<pad>', '<bos>', '<eos>']</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"><br /></span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;">for ln in [SRC_LANGUAGE, TGT_LANGUAGE]:</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> # Training data Iterator</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> train_iter = Multi30k(split='train', language_pair=(SRC_LANGUAGE, TGT_LANGUAGE))</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> # Create torchtext's Vocab object</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> vocab_transform[ln] = build_vocab_from_iterator(yield_tokens(train_iter, ln),</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> min_freq=1,</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> specials=special_symbols,</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> special_first=True)</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"><br /></span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"># Set ``UNK_IDX`` as the default index. This index is returned when the token is not found.</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"># If not set, it throws ``RuntimeError`` when the queried token is not found in the Vocabulary.</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;">for ln in [SRC_LANGUAGE, TGT_LANGUAGE]:</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> vocab_transform[ln].set_default_index(UNK_IDX)</span></div></div><div class="separator" style="clear: both;"><br /></div><div class="separator" style="clear: both;"><b>트랜스포머 기반 Seq2Seq 신경망 구현</b></div><div class="separator" style="clear: both;">이제, Transformer를 사용하는 Seq2Seq 네트워크를 생성한다. 네트워크 세 부분으로 구성되어 있다. </div><div class="separator" style="clear: both;"><br /></div></div></div></div></div><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><div class="separator" style="clear: both;">첫 번째, 임베딩 레이어이다. 이 레이어는 입력 문장들을 텐서로 변환한다. 이 임베딩은 위치 인코딩을 사용하여 입력 토큰의 위치 정보를 모델에 제공한다. </div></div></div></div></div><div style="text-align: left;"><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><div class="separator" style="clear: both;">두 번째, 트랜스포머를 구현한다.</div></div></div></div></div><div style="text-align: left;"><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><div class="separator" style="clear: both;">세 번째, 트랜스포머 모델의 출력은 선형 레이어를 통과해, 대상 언어의 각 토큰에 대한 생성 확률을 제공한다.</div></div></div></div></div></blockquote><div style="text-align: left;"><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><div class="separator" style="clear: both;"><br /></div><div class="separator" style="clear: both;">코드 구현은 다음과 같다. </div><div class="separator" style="clear: both;"><div class="separator" style="clear: both;"><span style="font-size: x-small;">from torch import Tensor</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;">import torch</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;">import torch.nn as nn</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;">from torch.nn import Transformer</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;">import math</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;">DEVICE = torch.device('cuda' if torch.cuda.is_available() else 'cpu')</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"><br /></span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"># 앞의 글에서 설명한 포지션 인코딩과 동일하다.</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;">class PositionalEncoding(nn.Module):</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> def __init__(self,</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> emb_size: int,</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> dropout: float,</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> maxlen: int = 5000):</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> super(PositionalEncoding, self).__init__()</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> den = torch.exp(- torch.arange(0, emb_size, 2)* math.log(10000) / emb_size)</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> pos = torch.arange(0, maxlen).reshape(maxlen, 1)</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> pos_embedding = torch.zeros((maxlen, emb_size))</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> pos_embedding[:, 0::2] = torch.sin(pos * den)</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> pos_embedding[:, 1::2] = torch.cos(pos * den)</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> pos_embedding = pos_embedding.unsqueeze(-2)</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"><br /></span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> self.dropout = nn.Dropout(dropout)</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> self.register_buffer('pos_embedding', pos_embedding)</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"><br /></span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> def forward(self, token_embedding: Tensor):</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> return self.dropout(token_embedding + self.pos_embedding[:token_embedding.size(0), :])</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"><br /></span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"># 토큰 임베딩 텐서 변환 모듈</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;">class TokenEmbedding(nn.Module):</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> def __init__(self, vocab_size: int, emb_size):</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> super(TokenEmbedding, self).__init__()</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> self.embedding = nn.Embedding(vocab_size, emb_size)</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> self.emb_size = emb_size</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"><br /></span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> def forward(self, tokens: Tensor):</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> return self.embedding(tokens.long()) * math.sqrt(self.emb_size)</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"><br /></span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"># Seq2Seq 모듈</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;">class Seq2SeqTransformer(nn.Module):</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> def __init__(self,</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> num_encoder_layers: int,</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> num_decoder_layers: int,</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> emb_size: int,</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> nhead: int,</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> src_vocab_size: int,</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> tgt_vocab_size: int,</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> dim_feedforward: int = 512,</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> dropout: float = 0.1):</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> super(Seq2SeqTransformer, self).__init__()</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> self.transformer = Transformer(d_model=emb_size,</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> nhead=nhead,</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> num_encoder_layers=num_encoder_layers,</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> num_decoder_layers=num_decoder_layers,</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> dim_feedforward=dim_feedforward,</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> dropout=dropout) # 토치에 내장된 트랜스포머 사용</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> self.generator = nn.Linear(emb_size, tgt_vocab_size)</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> self.src_tok_emb = TokenEmbedding(src_vocab_size, emb_size)</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> self.tgt_tok_emb = TokenEmbedding(tgt_vocab_size, emb_size)</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> self.positional_encoding = PositionalEncoding(</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> emb_size, dropout=dropout)</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"><br /></span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> def forward(self,</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> src: Tensor,</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> trg: Tensor,</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> src_mask: Tensor,</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> tgt_mask: Tensor,</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> src_padding_mask: Tensor,</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> tgt_padding_mask: Tensor,</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> memory_key_padding_mask: Tensor):</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> src_emb = self.positional_encoding(self.src_tok_emb(src)) # 소스 토큰 임베딩</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> tgt_emb = self.positional_encoding(self.tgt_tok_emb(trg)) # 대상 토큰 임베딩</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> outs = self.transformer(src_emb, tgt_emb, src_mask, tgt_mask, None,</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> src_padding_mask, tgt_padding_mask, memory_key_padding_mask) # 트랜스포머 호출</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> return self.generator(outs) # 선형 레이어 입력 처리</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"><br /></span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> def encode(self, src: Tensor, src_mask: Tensor):</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> return self.transformer.encoder(self.positional_encoding(</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> self.src_tok_emb(src)), src_mask)</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"><br /></span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> def decode(self, tgt: Tensor, memory: Tensor, tgt_mask: Tensor):</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> return self.transformer.decoder(self.positional_encoding(</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> self.tgt_tok_emb(tgt)), memory,</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> tgt_mask)</span></div></div><div class="separator" style="clear: both;"><br /></div><div class="separator" style="clear: both;">훈련하는 동안 모델이 다음을 학습하지 못하도록, 후속 단어 마스크가 필요하다. 예측을 할 때 미래 단어를 숨길 마스크를 준비한다. 소스 및 대상 패딩 토큰 등도 준비한다. </div><div class="separator" style="clear: both;"><div class="separator" style="clear: both;"><span style="font-size: x-small;">def generate_square_subsequent_mask(sz): </span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> mask = (torch.triu(torch.ones((sz, sz), device=DEVICE)) == 1).transpose(0, 1)</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> mask = mask.float().masked_fill(mask == 0, float('-inf')).masked_fill(mask == 1, float(0.0))</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> return mask</span></div><div class="separator" style="clear: both;"><br /></div><div class="separator" style="clear: both;"><span style="font-size: x-small;">def create_mask(src, tgt):</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> src_seq_len = src.shape[0]</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> tgt_seq_len = tgt.shape[0]</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"><br /></span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> tgt_mask = generate_square_subsequent_mask(tgt_seq_len)</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> src_mask = torch.zeros((src_seq_len, src_seq_len),device=DEVICE).type(torch.bool)</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"><br /></span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> src_padding_mask = (src == PAD_IDX).transpose(0, 1)</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> tgt_padding_mask = (tgt == PAD_IDX).transpose(0, 1)</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> return src_mask, tgt_mask, src_padding_mask, tgt_padding_mask</span></div><div class="separator" style="clear: both;"><br /></div></div><div class="separator" style="clear: both;">이제 모델의 매개 변수를 정의하고 동일하게 인스턴스화한다. 여기서는 교차 엔트로피 손실 함수와 옵티마이저를 정의한다.</div><div class="separator" style="clear: both;"><div class="separator" style="clear: both;"><span style="font-size: x-small;">torch.manual_seed(0)</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"><br /></span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;">SRC_VOCAB_SIZE = len(vocab_transform[SRC_LANGUAGE])</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;">TGT_VOCAB_SIZE = len(vocab_transform[TGT_LANGUAGE])</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;">EMB_SIZE = 512</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;">NHEAD = 8</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;">FFN_HID_DIM = 512</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;">BATCH_SIZE = 128</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;">NUM_ENCODER_LAYERS = 3</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;">NUM_DECODER_LAYERS = 3</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"><br /></span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;">transformer = Seq2SeqTransformer(NUM_ENCODER_LAYERS, NUM_DECODER_LAYERS, EMB_SIZE,</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> NHEAD, SRC_VOCAB_SIZE, TGT_VOCAB_SIZE, FFN_HID_DIM)</span></div><div class="separator" style="clear: both;"><span style="font-size: small;">for p in transformer.parameters():</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> if p.dim() > 1:</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> nn.init.xavier_uniform_(p) # 각 레이어의 신경망 가중치를 적절히 초기화함</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"><br /></span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;">transformer = transformer.to(DEVICE)</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"><br /></span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;">loss_fn = torch.nn.CrossEntropyLoss(ignore_index=PAD_IDX)</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;">optimizer = torch.optim.Adam(transformer.parameters(), lr=0.0001, betas=(0.9, 0.98), eps=1e-9)</span></div></div><div class="separator" style="clear: both;"><br /></div><div class="separator" style="clear: both;"><b>데이터 학습</b></div><div class="separator" style="clear: both;">데이터 학습은 한 쌍의 소스-대상 문자열을 이용한다. 이러한 문자열 쌍을 신경망에서 처리할 수 있는 배치 데이터 텐서로 변환한다(collate 함수). </div><div class="separator" style="clear: both;"><div class="separator" style="clear: both;"><span style="font-size: x-small;">from torch.nn.utils.rnn import pad_sequence</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"><br /></span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"># 문장을 텐서로 변환하기 위한 헬퍼 함수</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;">def sequential_transforms(*transforms):</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> def func(txt_input):</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> for transform in transforms:</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> txt_input = transform(txt_input)</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> return txt_input</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> return func</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"><br /></span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"># BOS, EOS 추가. 텐서 변환</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;">def tensor_transform(token_ids: List[int]):</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> return torch.cat((torch.tensor([BOS_IDX]),</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> torch.tensor(token_ids),</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> torch.tensor([EOS_IDX])))</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"><br /></span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"># 소스, 타겟 언어 문장을 텐서로 변환</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;">text_transform = {}</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;">for ln in [SRC_LANGUAGE, TGT_LANGUAGE]:</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> text_transform[ln] = sequential_transforms(token_transform[ln], # 문장 토큰화</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> vocab_transform[ln], # 토큰 수치화</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> tensor_transform) # BOS/EOS 추가. 텐서 생성</span></div><div class="separator" style="clear: both;"><br /></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"># 학습용 배치 데이터셋 생성. 소스-대상 문장 토큰 대상.</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;">def collate_fn(batch):</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> src_batch, tgt_batch = [], []</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> for src_sample, tgt_sample in batch:</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> src_batch.append(text_transform[SRC_LANGUAGE](src_sample.rstrip("\n")))</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> tgt_batch.append(text_transform[TGT_LANGUAGE](tgt_sample.rstrip("\n")))</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"><br /></span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> src_batch = pad_sequence(src_batch, padding_value=PAD_IDX)</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> tgt_batch = pad_sequence(tgt_batch, padding_value=PAD_IDX)</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> return src_batch, tgt_batch</span></div></div><div class="separator" style="clear: both;"><br /></div></div></div><div class="separator" style="clear: both; text-align: left;">다음과 같이 학습 함수를 정의한다.</div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><span style="font-size: x-small;">from torch.utils.data import DataLoader</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"><br /></span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;">def train_epoch(model, optimizer): # 학습 함수</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> model.train()</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> losses = 0</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> train_iter = Multi30k(split='train', language_pair=(SRC_LANGUAGE, TGT_LANGUAGE))</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> train_dataloader = DataLoader(train_iter, batch_size=BATCH_SIZE, collate_fn=collate_fn)</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"><br /></span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> for src, tgt in train_dataloader:</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> src = src.to(DEVICE)</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> tgt = tgt.to(DEVICE)</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> tgt_input = tgt[:-1, :]</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"><br /></span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> src_mask, tgt_mask, src_padding_mask, tgt_padding_mask = create_mask(src, tgt_input) # 마스크 </span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> logits = model(src, tgt_input, src_mask, tgt_mask,src_padding_mask, tgt_padding_mask, src_padding_mask)</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"><br /></span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> optimizer.zero_grad() </span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> tgt_out = tgt[1:, :]</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> loss = loss_fn(logits.reshape(-1, logits.shape[-1]), tgt_out.reshape(-1)) # 손실 계산</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> loss.backward() # 역전파</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"><br /></span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> optimizer.step()</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> losses += loss.item()</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"><br /></span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> return losses / len(list(train_dataloader))</span></div><div class="separator" style="clear: both;"><br /></div><div class="separator" style="clear: both;"><span style="font-size: x-small;">def evaluate(model): # 평가 함수</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> model.eval()</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> losses = 0</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"><br /></span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> val_iter = Multi30k(split='valid', language_pair=(SRC_LANGUAGE, TGT_LANGUAGE))</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> val_dataloader = DataLoader(val_iter, batch_size=BATCH_SIZE, collate_fn=collate_fn)</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"><br /></span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> for src, tgt in val_dataloader:</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> src = src.to(DEVICE)</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> tgt = tgt.to(DEVICE)</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> tgt_input = tgt[:-1, :]</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> src_mask, tgt_mask, src_padding_mask, tgt_padding_mask = create_mask(src, tgt_input)</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"><br /></span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> logits = model(src, tgt_input, src_mask, tgt_mask,src_padding_mask, tgt_padding_mask, src_padding_mask)</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"><br /></span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> tgt_out = tgt[1:, :]</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> loss = loss_fn(logits.reshape(-1, logits.shape[-1]), tgt_out.reshape(-1))</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> losses += loss.item()</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"><br /></span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> return losses / len(list(val_dataloader))</span></div></div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;">이제 준비된 데이터로 학습한다.</div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"><span style="font-size: x-small;">from timeit import default_timer as timer</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;">NUM_EPOCHS = 18</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"><br /></span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;">for epoch in range(1, NUM_EPOCHS+1):</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> start_time = timer()</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> train_loss = train_epoch(transformer, optimizer) # 에포크 만큼 학습</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> end_time = timer()</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> val_loss = evaluate(transformer)</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> print((f"Epoch: {epoch}, Train loss: {train_loss:.3f}, Val loss: {val_loss:.3f}, "f"Epoch time = {(end_time - start_time):.3f}s"))</span></div><div class="separator" style="clear: both;"><br /></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"># function to generate output sequence using greedy algorithm</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;">def greedy_decode(model, src, src_mask, max_len, start_symbol):</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> src = src.to(DEVICE)</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> src_mask = src_mask.to(DEVICE)</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"><br /></span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> memory = model.encode(src, src_mask)</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> ys = torch.ones(1, 1).fill_(start_symbol).type(torch.long).to(DEVICE)</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> for i in range(max_len-1):</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> memory = memory.to(DEVICE)</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> tgt_mask = (generate_square_subsequent_mask(ys.size(0))</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> .type(torch.bool)).to(DEVICE)</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> out = model.decode(ys, memory, tgt_mask)</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> out = out.transpose(0, 1)</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> prob = model.generator(out[:, -1])</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> _, next_word = torch.max(prob, dim=1)</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> next_word = next_word.item()</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"><br /></span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> ys = torch.cat([ys,</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> torch.ones(1, 1).type_as(src.data).fill_(next_word)], dim=0)</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> if next_word == EOS_IDX:</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> break</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> return ys</span></div><div class="separator" style="clear: both;"><br /></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"># 실제 입력에 대한 대상으로 번역(변환)</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;">def translate(model: torch.nn.Module, src_sentence: str):</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> model.eval()</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> src = text_transform[SRC_LANGUAGE](src_sentence).view(-1, 1)</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> num_tokens = src.shape[0]</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> src_mask = (torch.zeros(num_tokens, num_tokens)).type(torch.bool)</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> tgt_tokens = greedy_decode(</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> model, src, src_mask, max_len=num_tokens + 5, start_symbol=BOS_IDX).flatten()</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> return " ".join(vocab_transform[TGT_LANGUAGE].lookup_tokens(list(tgt_tokens.cpu().numpy()))).replace("<bos>", "").replace("<eos>", "")</span></div></div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;">학습 결과 다음과 같이 자연어 번역 기능이 제대로 동작하는 것을 확인할 수 있다. 참고로, 학습에는 NVIDIA GPU RAM 7G 정도가 사용되었으며, 학습 시간은 한 시간 내에 처리된다. </div><div class="separator" style="clear: both; text-align: left;"><span style="font-size: x-small;">print(translate(transformer, "Eine Gruppe von Menschen steht vor einem Iglu ."))</span></div><div class="separator" style="clear: both; text-align: left;"><span style="font-size: x-small;">> A group of people stand in front of an igloo .</span></div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgygeYyt4eWncGan5oCAfi0KiVgf61QhiaowZXssZupCFIj5dZN9iJ9MW-A4GQf8sGFKBsEeFCE2MyYU2pu1sb_Jmc9tjheXzTGZXAP9UY0hlYYzNvyj_km_6GBXelxkOq-o5oodAOCyIJiN4Vgv9oWM-YiOaoKX2eUvwE6ZdLofOfzo6z_zo7JLkMw6bcQ" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="598" data-original-width="1011" height="286" src="https://blogger.googleusercontent.com/img/a/AVvXsEgygeYyt4eWncGan5oCAfi0KiVgf61QhiaowZXssZupCFIj5dZN9iJ9MW-A4GQf8sGFKBsEeFCE2MyYU2pu1sb_Jmc9tjheXzTGZXAP9UY0hlYYzNvyj_km_6GBXelxkOq-o5oodAOCyIJiN4Vgv9oWM-YiOaoKX2eUvwE6ZdLofOfzo6z_zo7JLkMw6bcQ=w484-h286" width="484" /></a></div></div><div class="separator" style="clear: both; text-align: center;">실행 결과</div></div><div class="separator" style="clear: both; text-align: left;"><br /></div><div style="text-align: left;"><b>마무리</b></div><div style="text-align: left;">토치에서 제공되는 트랜스포머 코드를 확인해 보면, 앞서 설명된 트랜스포머 내부 코드 구현과 거의 유사한 것을 알 수 있다. 실행해보면, 계산 속도가 좀 더 빠르고, 좀 더 많은 양의 데이터를 학습해서 결과다 더 나은 것을 알 수 있다.</div><div style="text-align: left;"><br /></div><div style="text-align: left;">트랜스포머가 자연어 번역 이외에 멀티모달의 핵심 기술이 된 이유는 비정형 데이터를 연산 가능한 차원으로 수치화할 수 있는 임베딩 기술의 발전과 트랜스포머의 Key, Query, Value 입력을 통한 여러 학습 데이터 조합이 가능한 특징이 크게 작용했다. </div><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhxZkKdK5N3HEujl3zvvebvuB5d66JWzaHqeGMI9dR6Dfxpk8KvCbIPAz_amxSktKW1W65cRlJWIo4zROqyAEM5yJRQS64LGOs9oyEg_Neh2AjT1DNTCmY1qYJtgW53Cd-xsGS-2lLwYGwuS5Txq5Oi0dZS2eJOmKMA9JQSlFSNo159Hd0elPCx8L6wPIgQ" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="840" data-original-width="1179" height="285" src="https://blogger.googleusercontent.com/img/a/AVvXsEhxZkKdK5N3HEujl3zvvebvuB5d66JWzaHqeGMI9dR6Dfxpk8KvCbIPAz_amxSktKW1W65cRlJWIo4zROqyAEM5yJRQS64LGOs9oyEg_Neh2AjT1DNTCmY1qYJtgW53Cd-xsGS-2lLwYGwuS5Txq5Oi0dZS2eJOmKMA9JQSlFSNo159Hd0elPCx8L6wPIgQ=w400-h285" width="400" /></a></div><div class="separator" style="clear: both; text-align: center;">트랜스포머 기반 멀티모달 구현 전략 개념도</div></div><div style="text-align: left;"><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjtdzTrcdRpwfK4cZC-DYFkFUKh0IHXeZTxl502Xs8CU89GMj0ppcE7dbmcJc5PWXJuZ7l0F9Pwf-ClbuFrXDMbiN7gEUCK215uQ75ckkevaBBp7EmkCJkusN0F9nKMBGDtIJmjYg0_y0sMNZQcfeYr46TyBkSoCK4PToVhCbEZ0M3qnvx1jnIW0Zl-GEl2" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="585" data-original-width="1597" height="146" src="https://blogger.googleusercontent.com/img/a/AVvXsEjtdzTrcdRpwfK4cZC-DYFkFUKh0IHXeZTxl502Xs8CU89GMj0ppcE7dbmcJc5PWXJuZ7l0F9Pwf-ClbuFrXDMbiN7gEUCK215uQ75ckkevaBBp7EmkCJkusN0F9nKMBGDtIJmjYg0_y0sMNZQcfeYr46TyBkSoCK4PToVhCbEZ0M3qnvx1jnIW0Zl-GEl2=w400-h146" width="400" /></a></div><div class="separator" style="clear: both;">멀티모달 시작을 알린 OpenAI의 CLIP 모델(<a href="https://arxiv.org/abs/2103.00020">Learning Transferable Visual Models From Natural Language Supervision</a>, 2021)</div><div class="separator" style="clear: both;"><br /></div></div></div><div style="text-align: left;">다음은 트랜스포머와 <a href="https://en.wikipedia.org/wiki/Variational_autoencoder">VAE</a>를 이용한 CLIP 멀티모달 네트웍을 좀 더 깊게 파헤쳐 보도록 한다. </div><div style="text-align: left;"><br /></div><div style="text-align: left;"><b>레퍼런스</b></div><div style="text-align: left;"><ul style="text-align: left;"><li><a href="https://pytorch.org/tutorials/beginner/translation_transformer.html">Language Translation with nn.Transformer and torchtext — PyTorch Tutorials 2.2.0+cu121 documentation</a></li><li><a href="https://towardsdatascience.com/how-to-code-the-transformer-in-pytorch-24db27c8f9ec">How to code The Transformer in Pytorch | by Samuel Lynn-Evans | Towards Data Science</a></li><li><a href="https://huggingface.co/docs/transformers/tasks/question_answering">Question answering (huggingface.co)</a></li><li><a href="https://huggingface.co/docs/transformers/model_doc/codegen">CodeGen (huggingface.co)</a></li><li><a href="https://huggingface.co/docs/transformers/en/model_doc/clip">CLIP (huggingface.co)</a></li><li><a href="https://towardsdatascience.com/clip-model-and-the-importance-of-multimodal-embeddings-1c8f6b13bf72">CLIP Model and The Importance of Multimodal Embeddings | by Fahim Rustamy, PhD | Dec, 2023 | Towards Data Science</a></li><li><a href="https://towardsdatascience.com/simple-implementation-of-openai-clip-model-a-tutorial-ace6ff01d9f2">Simple Implementation of OpenAI CLIP model: A Tutorial | Towards Data Science</a></li><li><a href="https://github.com/explosion/curated-transformers?tab=readme-ov-file">explosion/curated-transformers: 🤖 A PyTorch library of curated Transformer models and their composable components (github.com)</a></li><li><a href="https://jeas.springeropen.com/articles/10.1186/s44147-022-00159-4">MarianCG: a code generation transformer model inspired by machine translation | Journal of Engineering and Applied Science | Full Text (springeropen.com)</a></li><li><a href="https://huggingface.co/transformers/v3.2.0/custom_datasets.html">Fine-tuning with custom datasets — transformers 3.2.0 documentation (huggingface.co)</a></li><li><a href="https://www.kaggle.com/code/tejasurya/transformers-language-translator-eng-to-spanish/notebook">Transformers : Language Translator Eng to Spanish | Kaggle</a></li><li><a href="https://towardsdatascience.com/fine-tune-transformer-models-for-question-answering-on-custom-data-513eaac37a80">Fine-Tune Transformer Models For Question Answering On Custom Data | by Skanda Vivek | Towards Data Science</a></li><li><a href="https://www.mdpi.com/2076-3417/12/14/7195">Applied Sciences | Free Full-Text | Boosting the Transformer with the BERT Supervision in Low-Resource Machine Translation (mdpi.com)</a></li></ul></div>Daddy Makerhttp://www.blogger.com/profile/02275758703365576780noreply@blogger.com0tag:blogger.com,1999:blog-5201956450461596914.post-36560304778570867062024-02-04T00:06:00.000-08:002024-02-15T01:58:38.789-08:00트랜스포머 디코더 핵심 코드 구현을 통한 동작 메커니즘 이해하기<div>이 글은 생성AI의 핵심인 딥러닝 모델 트랜스포머(Transformer) 구현 메커니즘을 코드 수준에서 이해하기 위해, 트랜스포머의 작동 과정을 상세히 설명한다. 이 글은 앞서 설명한 <a href="https://daddynkidsmakers.blogspot.com/2023/12/blog-post.html">인코더</a>에 이어, 디코더를 구현하는 방법을 나눔한다.</div><div><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEiHDWtufVWWSfVDd3xutxQSXDhWX-dUflFU3cDTxnQqwdJV2Mh8LxHDkN0d-zfSeKUgjjXaexc4cBfnCn66eCBx19cw-SSWq12ID2N1EE5G63t62W1IzxWcvQ13_YANrvzRVmP9iNUomlIDXgUD48dkaxuR3-zKUivhWSuqmU-hjv_KdVtHerhIBa67bRUl" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="978" data-original-width="1458" height="269" src="https://blogger.googleusercontent.com/img/a/AVvXsEiHDWtufVWWSfVDd3xutxQSXDhWX-dUflFU3cDTxnQqwdJV2Mh8LxHDkN0d-zfSeKUgjjXaexc4cBfnCn66eCBx19cw-SSWq12ID2N1EE5G63t62W1IzxWcvQ13_YANrvzRVmP9iNUomlIDXgUD48dkaxuR3-zKUivhWSuqmU-hjv_KdVtHerhIBa67bRUl=w400-h269" width="400" /></a></div></div><div class="separator" style="clear: both; text-align: center;">트랜스포머 인코더-디코더 모델 적용된 생성 AI 멀티모달 Stable Diffusion 모델(<a href="https://synthesis.ai/2023/01/05/modern-generative-ai-an-overview/">Generative AI Models in Image Generation: Overview - Synthesis AI</a>)</div></div><div><br /></div><div>이 과정을 통해, 텍스트 번역기 등 앞뒤 문맥 관계가 있는 딥러닝 모델을 직접 개발하거나, 이와 유사한 멀티모델 데이터 스트림을 다른 형식으로 변환(트랜스폼)시킬 수 있는 스테이블 디퓨전(Stable Diffusion)같은 생성AI 모델을 개발할 수 있다. 실제, 트랜스포머는 텍스트에서 비전, 음성, 비디오 데이터로 맵핑하는 주요 컴포넌트 중 하나로 사용된다. <br /></div><div><br /></div><div><div>관련 내용이 좀 많아, 글을 <a href="https://daddynkidsmakers.blogspot.com/2023/12/blog-post.html">인코더 부분</a>(<a href="(참고) ">참고</a>)과 디코더 부분크게 두 개로 나누어 진행하도록 한다. 이 글은 트랜스포머 디코더 구현 방법에 대한 글이다. </div><div><br /></div><div>이 글에서 표시된 트랜스포머 내부 실행 소스 코드는 다음 링크에서 다운로드할 수 있다. </div><div><ul><li>Github - <a href="https://github.com/mac999/transformer">transformer (github.com)</a></li></ul></div><div>딥러닝 및 컴퓨터 비전에 대한 개념은 다음 링크를 참고한다. </div><div><ul><li><a href="https://daddynkidsmakers.blogspot.com/2017/02/blog-post_24.html">머신러닝 딥러닝 신경망 개념, 종류 및 개발</a></li><li><a href="https://daddynkidsmakers.blogspot.com/2022/11/3.html">딥러닝 기반 3차원 스캔 데이터, 포인트 클라우드 학습</a></li><li><a href="https://daddynkidsmakers.blogspot.com/2023/03/generative-ai.html">생성(Generative) AI 오픈소스 딥러닝 모델 Stable Diffusion, ControlNet 개념 및 ComfyUI 사용법</a></li><li><a href="https://github.com/mac999/computer_vision_deeplearning/tree/main">Computer_vision_deeplearning: computer vision based on deep learning lecture materials, github</a></li></ul><div>이 글은 많은 레퍼런스들을 참고해, 가능한 트랜스포머 동작방식을 이해하기 쉽도록 정리한 것이다. 관련해 궁금하다면, 이 글 마지막에 있는 레퍼런스들을 살펴보길 바란다. </div></div></div><div><br /></div><div><b>디코딩 처리 순서</b></div><div>디코더는 앞서 인코더에서 설명하였듯이, 텍스트 입력 토큰 각각 임베딩 벡터로 처리되어 디코더에 입력된다. </div><div><div class="separator" style="clear: both; color: #333333; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; font-size: 14.85px; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEivaZUSqlUqCnUlA9jr5VsIiUDz0-Kl8fG_X4F8DTHeH7zbZ64HOqr3ZRPRSka0wi8zf7ixpdN3YXiw1PAk85H5xBKDxqJZE1vhOX4mEYo5LQuuqVqTG2cALwQziGfk7fGADFN8r-K6Q0SYFMmfU400sV8X52yenRsiYMdL5KyDgMWMxiZyKDkWVRQxJMR4" style="color: #992211; margin-left: 1em; margin-right: 1em; text-decoration-line: none;"><img alt="" data-original-height="702" data-original-width="1240" height="309" src="https://blogger.googleusercontent.com/img/a/AVvXsEivaZUSqlUqCnUlA9jr5VsIiUDz0-Kl8fG_X4F8DTHeH7zbZ64HOqr3ZRPRSka0wi8zf7ixpdN3YXiw1PAk85H5xBKDxqJZE1vhOX4mEYo5LQuuqVqTG2cALwQziGfk7fGADFN8r-K6Q0SYFMmfU400sV8X52yenRsiYMdL5KyDgMWMxiZyKDkWVRQxJMR4=w546-h309" style="border: none; position: relative;" width="546" /></a></div><div class="separator" style="clear: both; color: #333333; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; font-size: 14.85px; text-align: center;">트랜스포머 구성요소(<a href="https://deepfrench.gitlab.io/deep-learning-project/" style="color: #992211; text-align: left; text-decoration-line: none;">The Attention Mechanism and the Transformer Model</a>)</div></div><div><br /></div><div>영어 > 프랑스어 번역의 경우, 프랑어 텍스트 토큰이 입력된다고 생각하면 된다. 토큰 당 임베딩벡터 크기는 구글 논문에서 언급한 512를 사용한다.</div><div><div><br /></div><div>리마인드 차원에서 앞서 설명한 디코드 단계를 한번 더 확인해 보자.</div><div><ol style="text-align: left;"><li>Output Embedding: 입력 데이터를 토큰으로 구분하고 임베딩한다. </li><li>Positional Encoding: 단어의 순서를 표현하는 위치를 인코딩해, 임베딩 벡터에 포함해준다.</li><li>Masked Multi-Head Attention: 디코딩에서는 다음 단어가 예측되도록, 앞의 단어 임베딩 벡터에 해당하는 계산은 Mask 처리해야 한다. 입력된 인코딩 벡터를 8개의 Multi-Head 벡터로 나누고, 다시 Query, Key, Value 벡터로 나누어, 어텐션을 코사인 벡터 유사도 함수 계산으로 해결한다. </li><li>Add & Normal: 잔차 연결과 정규화를 수행한다. </li><li>Multi-Head Attention: 인코더 출력은 Key, Value 값으로 디코더 Multi-Head Attention에 입력한다. Q는 앞에서 출력을 입력한다. 이를 통해, 인코더 어텐션과 디코딩될 Q가 함께 고려되어, 학습된다. </li><li>Add & Normal: 잔차 연결과 정규화를 수행한다. </li><li>Forward Feedback: 포워드 신경망을 연결해, 가중치를 계산한다. </li><li>Add & Normal: 6번 단계와 동일하다.</li><li>Linear: 선형 레이어로 가중치를 계산한다. </li><li>Softmax: 과거 토큰 A에 대한 미래 토큰 B의 확률을 얻기 위해, softmax를 적용한다.</li><li>단어 예측 결과를 출력한다.</li></ol></div><div>이 과정은 다음 그림과 같이, 인코더와 유사하다. 단, 3, 5번 과정에서 인코더에서 출력된 K, V 값을 입력받는 다는 점만 차이가 있다. </div></div><div><br /></div><div><div><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; color: #333333; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; font-size: 14.85px; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjZy0LCQ7effLm-dnYTxVLISSmSZBKEkmSb3nn5Wfg0gceqb6eYfdi6F4WY426jVqwFT2XqDhof2N5GMl7uacSL175q3kzDsQPMFGtDpwFCitnZPvMV4tn6eLnr3WI_WsjBe_VMJ3tyS3sag0WcPE3vn9rEQ4PqCJCK4KEwdAAq5oZrMm5EcTdv5wauXiqj/s871/a1.PNG" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="813" data-original-width="871" height="374" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjZy0LCQ7effLm-dnYTxVLISSmSZBKEkmSb3nn5Wfg0gceqb6eYfdi6F4WY426jVqwFT2XqDhof2N5GMl7uacSL175q3kzDsQPMFGtDpwFCitnZPvMV4tn6eLnr3WI_WsjBe_VMJ3tyS3sag0WcPE3vn9rEQ4PqCJCK4KEwdAAq5oZrMm5EcTdv5wauXiqj/w400-h374/a1.PNG" width="400" /></a></div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: center;"><span face="Arial, Tahoma, Helvetica, FreeSans, sans-serif" style="color: #333333;"><span style="font-size: 14.85px;">트랜스포머 디코더 학습 예시</span></span></div><div class="separator" style="clear: both;"><span face="Arial, Tahoma, Helvetica, FreeSans, sans-serif" style="color: #333333;"><span style="font-size: 14.85px;"><br /></span></span></div><div class="separator" style="clear: both;"><b>멀티헤드 크로스 어텐션 처리</b></div><div class="separator" style="clear: both;"><div class="separator" style="clear: both;">디코드는 인코더의 K, V값을 입력받고, 디코드의 4번에서 출력된 Q값을 입력받아, 어텐션 계산을 한다. 인코더와 수식은 동일하다. </div><div class="separator" style="clear: both;"><br /></div><div class="separator" style="clear: both;">코드 구현 및 설명은 다음과 같다('()'안은 차원 shape를 말함).</div><div class="separator" style="clear: both;">class MultiHeadCrossAttention(nn.Module):</div><div class="separator" style="clear: both;"> def __init__(self, d_model, num_heads):</div><div class="separator" style="clear: both;"> super().__init__()</div><div class="separator" style="clear: both;"> self.d_model = d_model # 차원수</div><div class="separator" style="clear: both;"> self.num_heads = num_heads # 멀티헤더 수. 논문에선 8 사용</div><div class="separator" style="clear: both;"> self.head_dim = d_model // num_heads # 헤더 당 차원수. 512/8=64가 됨.</div><div class="separator" style="clear: both;"> self.kv_layer = nn.Linear(d_model , 2 * d_model) # 가중치 학습용 KV텐서 신경망 준비. (512, 2*512)</div><div class="separator" style="clear: both;"> self.q_layer = nn.Linear(d_model , d_model) # 가중치 학습용 Q신경망 준비. (512,512) </div><div class="separator" style="clear: both;"> self.linear_layer = nn.Linear(d_model, d_model) # 선형 레이어 준비. (512,512)</div><div class="separator" style="clear: both;"> </div><div class="separator" style="clear: both;"> def forward(self, x, y, mask): # x는 인코더의 KV 벡터값, y는 Q벡터값 입력임</div><div class="separator" style="clear: both;"> batch_size, sequence_length, d_model = x.size() # 배치크기, 시퀀스크기=200, 512</div><div class="separator" style="clear: both;"> kv = self.kv_layer(x) # 입력값(예. 영어) X에서 KV 레이어 계산</div><div class="separator" style="clear: both;"> q = self.q_layer(y) # 목표값(예. 독일어) Y에서 Q레이어 계산</div><div class="separator" style="clear: both;"> # 어텐션 계산을 위해 텐서 행렬 모양 reshape</div><div class="separator" style="clear: both;"> kv = kv.reshape(batch_size, sequence_length, self.num_heads, 2 * self.head_dim)</div><div class="separator" style="clear: both;"> q = q.reshape(batch_size, sequence_length, self.num_heads, self.head_dim)</div><div class="separator" style="clear: both;"> kv = kv.permute(0, 2, 1, 3)</div><div class="separator" style="clear: both;"> q = q.permute(0, 2, 1, 3)</div><div class="separator" style="clear: both;"> k, v = kv.chunk(2, dim=-1) # 계산을 위해 KV텐서를 K와 V로 나눔</div><div class="separator" style="clear: both;"> values, attention = scaled_dot_product(q, k, v, mask) # KQ 유사도 계산. V와 어텐션 스코어 계산됨</div><div class="separator" style="clear: both;"> values = values.permute(0, 2, 1, 3).reshape(batch_size, sequence_length, d_model)</div><div class="separator" style="clear: both;"> out = self.linear_layer(values) # 출력된 V를 입력받아, 선형 레이어 계산</div><div class="separator" style="clear: both;"> return out</div><div class="separator" style="clear: both;"><br /></div><div class="separator" style="clear: both;">나머지 코드는 인코더와 거의 동일하다. </div><div class="separator" style="clear: both;"><br /></div><div class="separator" style="clear: both;"><b>디코더 처리</b></div><div class="separator" style="clear: both;">앞서 정의된 멀티헤더 크로스 어텐션 처리만 인코더 KV, 디코더 Q벡터값을 받아 처리하고, 마스크 행렬을 입력하는 하는 것 이외에는 논문 내용과 동일하다. </div><div class="separator" style="clear: both;"><br /></div><div class="separator" style="clear: both;">여기서, Q = Query (what I'm looking for). 질의어. 디코더의 출력값</div><div class="separator" style="clear: both;">K = Key (what I can offer). 토큰 간 관계 유사도 계산을 위해 Query와 비교할 때 사용됨</div><div class="separator" style="clear: both;">V = Value (what I actually offer). Query, Key에 대한 최종 출력으로 관계성 계산에 사용</div><div class="separator" style="clear: both;">dk = k 벡터의 차원</div><div class="separator" style="clear: both;">M = Mask (미래 데이터 토큰만 학습데이터로 고려함)</div><div class="separator" style="clear: both;"><br /></div><div class="separator" style="clear: both;">코드는 다음과 같다. </div><div class="separator" style="clear: both;"><span style="font-size: x-small;">class DecoderLayer(nn.Module):</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> def __init__(self, d_model, ffn_hidden, num_heads, drop_prob):</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> super(DecoderLayer, self).__init__()</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> # 멀티헤드, 레이어 정규화, 드롭아웃 정의</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> self.self_attention = MultiHeadAttention(d_model=d_model, num_heads=num_heads)</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> self.layer_norm1 = LayerNormalization(parameters_shape=[d_model])</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> self.dropout1 = nn.Dropout(p=drop_prob)</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"><br /></span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> # 인코더 KV와 디코더 Q입력받아 멀티헤드 클로스 어텐션하는 모듈 정의</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> self.encoder_decoder_attention = MultiHeadCrossAttention(d_model=d_model, num_heads=num_heads)</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> self.layer_norm2 = LayerNormalization(parameters_shape=[d_model])</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> self.dropout2 = nn.Dropout(p=drop_prob)</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"><br /></span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> # 논문의 FF 레이어 정의</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> self.ffn = PositionwiseFeedForward(d_model=d_model, hidden=ffn_hidden, drop_prob=drop_prob)</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> self.layer_norm3 = LayerNormalization(parameters_shape=[d_model])</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> self.dropout3 = nn.Dropout(p=drop_prob)</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"><br /></span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> def forward(self, x, y, self_attention_mask, cross_attention_mask):</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> _y = y.clone()</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> y = self.self_attention(y, mask=self_attention_mask)</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> y = self.dropout1(y)</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> y = self.layer_norm1(y + _y)</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"><br /></span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> _y = y.clone()</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> y = self.encoder_decoder_attention(x, y, mask=cross_attention_mask) # 인코더 KV 벡터 입력, 디코더 Q 벡터 입력. 마스크 처리. </span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> y = self.dropout2(y)</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> y = self.layer_norm2(y + _y)</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"><br /></span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> _y = y.clone()</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> y = self.ffn(y)</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> y = self.dropout3(y)</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> y = self.layer_norm3(y + _y)</span></div><div class="separator" style="clear: both;"><span style="font-size: x-small;"> return y</span></div><div class="separator" style="clear: both;"><br /></div><div class="separator" style="clear: both;">다음과 같이 디코더를 실행해본다. </div><div class="separator" style="clear: both;">d_model = 512</div><div class="separator" style="clear: both;">num_heads = 8</div><div class="separator" style="clear: both;">drop_prob = 0.1</div><div class="separator" style="clear: both;">batch_size = 30</div><div class="separator" style="clear: both;">max_sequence_length = 200</div><div class="separator" style="clear: both;">ffn_hidden = 2048</div><div class="separator" style="clear: both;">num_layers = 5</div><div class="separator" style="clear: both;"><br /></div><div class="separator" style="clear: both;">decoder = Decoder(d_model, ffn_hidden, num_heads, drop_prob, num_layers)</div><div class="separator" style="clear: both;"><br /></div><div class="separator" style="clear: both;">x = torch.randn((batch_size, max_sequence_length, d_model)) </div><div class="separator" style="clear: both;">y = torch.randn((batch_size, max_sequence_length, d_model)) </div><div class="separator" style="clear: both;">mask = torch.zeros(max_sequence_length, max_sequence_length)</div><div class="separator" style="clear: both;"><br /></div><div class="separator" style="clear: both;">out = decoder(x, y, mask)</div><div class="separator" style="clear: both;"> </div><div class="separator" style="clear: both;">print(out)</div><div class="separator" style="clear: both;"><br /></div><div class="separator" style="clear: both;">결과는 다음과 같다. </div></div></div></div></div></div><div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgno5E9WstfZY9m0dIP-7o3HlGp6Rs2Q83TPxncLqu6aN2PxDJ3zSYtWcqR6TwOyNk4o0j05935_MtsTIKjMEIn0WiQOYkYKkuRCw-qphj4ZKkQ85MRDjpup_e3_Sc6ZCqX_8ywi9VC0whINLFw5vZvHZ3pPuMyg_1r_jJi1N4WsowMfqWrfd0LGysJkWU5/s849/a2.PNG" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="321" data-original-width="849" height="179" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgno5E9WstfZY9m0dIP-7o3HlGp6Rs2Q83TPxncLqu6aN2PxDJ3zSYtWcqR6TwOyNk4o0j05935_MtsTIKjMEIn0WiQOYkYKkuRCw-qphj4ZKkQ85MRDjpup_e3_Sc6ZCqX_8ywi9VC0whINLFw5vZvHZ3pPuMyg_1r_jJi1N4WsowMfqWrfd0LGysJkWU5/w473-h179/a2.PNG" width="473" /></a></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjDu4iaReDaf1SEJGGNIT-h6mvZ6CA0qUyL6NiWCvbTkW_Ggs8EiySQeQWmrz3qCxR2j2-o_aIC09a5n7pDmw5LmcslUgSFwtFxKfKrY3ptKsT73eSmOrQsTFDaskCRgRjq4-2T-9WYeBhEEvLByJli5Ge-ROoqeHtgUpzNkowRHk2R0VQtT2H2FY_krcux/s808/a3.PNG" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="204" data-original-width="808" height="119" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjDu4iaReDaf1SEJGGNIT-h6mvZ6CA0qUyL6NiWCvbTkW_Ggs8EiySQeQWmrz3qCxR2j2-o_aIC09a5n7pDmw5LmcslUgSFwtFxKfKrY3ptKsT73eSmOrQsTFDaskCRgRjq4-2T-9WYeBhEEvLByJli5Ge-ROoqeHtgUpzNkowRHk2R0VQtT2H2FY_krcux/w472-h119/a3.PNG" width="472" /></a></div><div class="separator" style="clear: both; text-align: center;">트랜스포머 디코더 출력 결과 일부</div></div><div><div><br /></div><div><b>트랜스포머 전체 코드 구현</b></div><div>인코더와 디코더 전체를 포함한 트랜스포머 코드는 다음과 같다. </div><div><span style="font-size: x-small;">import numpy as np</span></div><div><span style="font-size: x-small;">import torch</span></div><div><span style="font-size: x-small;">import math</span></div><div><span style="font-size: x-small;">from torch import nn</span></div><div><span style="font-size: x-small;">import torch.nn.functional as F</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;">def get_device():</span></div><div><span style="font-size: x-small;"> return torch.device('cuda') if torch.cuda.is_available() else torch.device('cpu')</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;">def scaled_dot_product(q, k, v, mask=None): # 논문의 cosine 유사도 계산 로직</span></div><div><span style="font-size: x-small;"> d_k = q.size()[-1]</span></div><div><span style="font-size: x-small;"> scaled = torch.matmul(q, k.transpose(-1, -2)) / math.sqrt(d_k)</span></div><div><span style="font-size: x-small;"> if mask is not None:</span></div><div><span style="font-size: x-small;"> scaled = scaled.permute(1, 0, 2, 3) + mask</span></div><div><span style="font-size: x-small;"> scaled = scaled.permute(1, 0, 2, 3)</span></div><div><span style="font-size: x-small;"> attention = F.softmax(scaled, dim=-1)</span></div><div><span style="font-size: x-small;"> values = torch.matmul(attention, v)</span></div><div><span style="font-size: x-small;"> return values, attention</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;">class PositionalEncoding(nn.Module): # 포지션 인코딩 구현 모듈</span></div><div><span style="font-size: x-small;"> def __init__(self, d_model, max_sequence_length):</span></div><div><span style="font-size: x-small;"> super().__init__()</span></div><div><span style="font-size: x-small;"> self.max_sequence_length = max_sequence_length</span></div><div><span style="font-size: x-small;"> self.d_model = d_model</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;"> def forward(self):</span></div><div><span style="font-size: x-small;"> even_i = torch.arange(0, self.d_model, 2).float()</span></div><div><span style="font-size: x-small;"> denominator = torch.pow(10000, even_i/self.d_model)</span></div><div><span style="font-size: x-small;"> position = (torch.arange(self.max_sequence_length)</span></div><div><span style="font-size: x-small;"> .reshape(self.max_sequence_length, 1))</span></div><div><span style="font-size: x-small;"> even_PE = torch.sin(position / denominator)</span></div><div><span style="font-size: x-small;"> odd_PE = torch.cos(position / denominator)</span></div><div><span style="font-size: x-small;"> stacked = torch.stack([even_PE, odd_PE], dim=2)</span></div><div><span style="font-size: x-small;"> PE = torch.flatten(stacked, start_dim=1, end_dim=2)</span></div><div><span style="font-size: x-small;"> return PE</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;">class SentenceEmbedding(nn.Module):</span></div><div><span style="font-size: x-small;"> def __init__(self, max_sequence_length, d_model, language_to_index, START_TOKEN, END_TOKEN, PADDING_TOKEN):</span></div><div><span style="font-size: x-small;"> super().__init__()</span></div><div><span style="font-size: x-small;"> self.vocab_size = len(language_to_index)</span></div><div><span style="font-size: x-small;"> self.max_sequence_length = max_sequence_length</span></div><div><span style="font-size: x-small;"> self.embedding = nn.Embedding(self.vocab_size, d_model)</span></div><div><span style="font-size: x-small;"> self.language_to_index = language_to_index</span></div><div><span style="font-size: x-small;"> self.position_encoder = PositionalEncoding(d_model, max_sequence_length)</span></div><div><span style="font-size: x-small;"> self.dropout = nn.Dropout(p=0.1)</span></div><div><span style="font-size: x-small;"> self.START_TOKEN = START_TOKEN</span></div><div><span style="font-size: x-small;"> self.END_TOKEN = END_TOKEN</span></div><div><span style="font-size: x-small;"> self.PADDING_TOKEN = PADDING_TOKEN</span></div><div><span style="font-size: x-small;"> </span></div><div><span style="font-size: x-small;"> def batch_tokenize(self, batch, start_token, end_token):</span></div><div><span style="font-size: x-small;"> def tokenize(sentence, start_token, end_token):</span></div><div><span style="font-size: x-small;"> sentence_word_indicies = [self.language_to_index[token] for token in list(sentence)]</span></div><div><span style="font-size: x-small;"> if start_token:</span></div><div><span style="font-size: x-small;"> sentence_word_indicies.insert(0, self.language_to_index[self.START_TOKEN])</span></div><div><span style="font-size: x-small;"> if end_token:</span></div><div><span style="font-size: x-small;"> sentence_word_indicies.append(self.language_to_index[self.END_TOKEN])</span></div><div><span style="font-size: x-small;"> for _ in range(len(sentence_word_indicies), self.max_sequence_length):</span></div><div><span style="font-size: x-small;"> sentence_word_indicies.append(self.language_to_index[self.PADDING_TOKEN])</span></div><div><span style="font-size: x-small;"> return torch.tensor(sentence_word_indicies)</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;"> tokenized = []</span></div><div><span style="font-size: x-small;"> for sentence_num in range(len(batch)):</span></div><div><span style="font-size: x-small;"> tokenized.append( tokenize(batch[sentence_num], start_token, end_token) )</span></div><div><span style="font-size: x-small;"> tokenized = torch.stack(tokenized)</span></div><div><span style="font-size: x-small;"> return tokenized.to(get_device())</span></div><div><span style="font-size: x-small;"> </span></div><div><span style="font-size: x-small;"> def forward(self, x, start_token, end_token): # sentence</span></div><div><span style="font-size: x-small;"> x = self.batch_tokenize(x, start_token, end_token) # 입력 문장 토큰화</span></div><div><span style="font-size: x-small;"> x = self.embedding(x) # 임베딩</span></div><div><span style="font-size: x-small;"> pos = self.position_encoder().to(get_device()) # 포지션 인코딩</span></div><div><span style="font-size: x-small;"> x = self.dropout(x + pos) # 임베딩 + 포지션 임코딩 결과</span></div><div><span style="font-size: x-small;"> return x</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;">class MultiHeadAttention(nn.Module):</span></div><div><span style="font-size: x-small;"> def __init__(self, d_model, num_heads):</span></div><div><span style="font-size: x-small;"> super().__init__()</span></div><div><span style="font-size: x-small;"> self.d_model = d_model</span></div><div><span style="font-size: x-small;"> self.num_heads = num_heads</span></div><div><span style="font-size: x-small;"> self.head_dim = d_model // num_heads</span></div><div><span style="font-size: x-small;"> self.qkv_layer = nn.Linear(d_model , 3 * d_model)</span></div><div><span style="font-size: x-small;"> self.linear_layer = nn.Linear(d_model, d_model)</span></div><div><span style="font-size: x-small;"> </span></div><div><span style="font-size: x-small;"> def forward(self, x, mask): # 논문에서 설명한 것과 동일</span></div><div><span style="font-size: x-small;"> batch_size, sequence_length, d_model = x.size()</span></div><div><span style="font-size: x-small;"> qkv = self.qkv_layer(x)</span></div><div><span style="font-size: x-small;"> qkv = qkv.reshape(batch_size, sequence_length, self.num_heads, 3 * self.head_dim)</span></div><div><span style="font-size: x-small;"> qkv = qkv.permute(0, 2, 1, 3)</span></div><div><span style="font-size: x-small;"> q, k, v = qkv.chunk(3, dim=-1)</span></div><div><span style="font-size: x-small;"> values, attention = scaled_dot_product(q, k, v, mask) # QK 간 cosine 유사도 계산. V와 어텐션 스코어 리턴</span></div><div><span style="font-size: x-small;"> values = values.permute(0, 2, 1, 3).reshape(batch_size, sequence_length, self.num_heads * self.head_dim)</span></div><div><span style="font-size: x-small;"> out = self.linear_layer(values)</span></div><div><span style="font-size: x-small;"> return out</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;">class LayerNormalization(nn.Module): # 레이어 가중치 정규화</span></div><div><span style="font-size: x-small;"> def __init__(self, parameters_shape, eps=1e-5):</span></div><div><span style="font-size: x-small;"> super().__init__()</span></div><div><span style="font-size: x-small;"> self.parameters_shape=parameters_shape</span></div><div><span style="font-size: x-small;"> self.eps=eps</span></div><div><span style="font-size: x-small;"> self.gamma = nn.Parameter(torch.ones(parameters_shape))</span></div><div><span style="font-size: x-small;"> self.beta = nn.Parameter(torch.zeros(parameters_shape))</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;"> def forward(self, inputs):</span></div><div><span style="font-size: x-small;"> dims = [-(i + 1) for i in range(len(self.parameters_shape))]</span></div><div><span style="font-size: x-small;"> mean = inputs.mean(dim=dims, keepdim=True)</span></div><div><span style="font-size: x-small;"> var = ((inputs - mean) ** 2).mean(dim=dims, keepdim=True)</span></div><div><span style="font-size: x-small;"> std = (var + self.eps).sqrt()</span></div><div><span style="font-size: x-small;"> y = (inputs - mean) / std</span></div><div><span style="font-size: x-small;"> out = self.gamma * y + self.beta</span></div><div><span style="font-size: x-small;"> return out</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;"> </span></div><div><span style="font-size: x-small;">class PositionwiseFeedForward(nn.Module): # FF 레이어. 라벨 결과 출력으로 맵핑하는 역할</span></div><div><span style="font-size: x-small;"> def __init__(self, d_model, hidden, drop_prob=0.1):</span></div><div><span style="font-size: x-small;"> super(PositionwiseFeedForward, self).__init__()</span></div><div><span style="font-size: x-small;"> self.linear1 = nn.Linear(d_model, hidden)</span></div><div><span style="font-size: x-small;"> self.linear2 = nn.Linear(hidden, d_model)</span></div><div><span style="font-size: x-small;"> self.relu = nn.ReLU()</span></div><div><span style="font-size: x-small;"> self.dropout = nn.Dropout(p=drop_prob)</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;"> def forward(self, x):</span></div><div><span style="font-size: x-small;"> x = self.linear1(x)</span></div><div><span style="font-size: x-small;"> x = self.relu(x)</span></div><div><span style="font-size: x-small;"> x = self.dropout(x)</span></div><div><span style="font-size: x-small;"> x = self.linear2(x)</span></div><div><span style="font-size: x-small;"> return x</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;">class EncoderLayer(nn.Module):</span></div><div><span style="font-size: x-small;"> def __init__(self, d_model, ffn_hidden, num_heads, drop_prob):</span></div><div><span style="font-size: x-small;"> super(EncoderLayer, self).__init__()</span></div><div><span style="font-size: x-small;"> self.attention = MultiHeadAttention(d_model=d_model, num_heads=num_heads)</span></div><div><span style="font-size: x-small;"> self.norm1 = LayerNormalization(parameters_shape=[d_model])</span></div><div><span style="font-size: x-small;"> self.dropout1 = nn.Dropout(p=drop_prob)</span></div><div><span style="font-size: x-small;"> self.ffn = PositionwiseFeedForward(d_model=d_model, hidden=ffn_hidden, drop_prob=drop_prob)</span></div><div><span style="font-size: x-small;"> self.norm2 = LayerNormalization(parameters_shape=[d_model])</span></div><div><span style="font-size: x-small;"> self.dropout2 = nn.Dropout(p=drop_prob)</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;"> def forward(self, x, self_attention_mask):</span></div><div><span style="font-size: x-small;"> residual_x = x.clone()</span></div><div><span style="font-size: x-small;"> x = self.attention(x, mask=self_attention_mask) # 어텐션 계산 후 V리턴. </span></div><div><span style="font-size: x-small;"> x = self.dropout1(x) # 드롭 아웃</span></div><div><span style="font-size: x-small;"> x = self.norm1(x + residual_x) # 잔차 연결 및 정규화</span></div><div><span style="font-size: x-small;"> residual_x = x.clone()</span></div><div><span style="font-size: x-small;"> x = self.ffn(x) # FF 레이어 계산</span></div><div><span style="font-size: x-small;"> x = self.dropout2(x) # 드롭 아웃</span></div><div><span style="font-size: x-small;"> x = self.norm2(x + residual_x) # 잔차 연결 및 정규화</span></div><div><span style="font-size: x-small;"> return x</span></div><div><span style="font-size: x-small;"> </span></div><div><span style="font-size: x-small;">class SequentialEncoder(nn.Sequential):</span></div><div><span style="font-size: x-small;"> def forward(self, *inputs):</span></div><div><span style="font-size: x-small;"> x, self_attention_mask = inputs</span></div><div><span style="font-size: x-small;"> for module in self._modules.values():</span></div><div><span style="font-size: x-small;"> x = module(x, self_attention_mask)</span></div><div><span style="font-size: x-small;"> return x</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;">class Encoder(nn.Module):</span></div><div><span style="font-size: x-small;"> def __init__(self, </span></div><div><span style="font-size: x-small;"> d_model, </span></div><div><span style="font-size: x-small;"> ffn_hidden, </span></div><div><span style="font-size: x-small;"> num_heads, </span></div><div><span style="font-size: x-small;"> drop_prob, </span></div><div><span style="font-size: x-small;"> num_layers,</span></div><div><span style="font-size: x-small;"> max_sequence_length,</span></div><div><span style="font-size: x-small;"> language_to_index,</span></div><div><span style="font-size: x-small;"> START_TOKEN,</span></div><div><span style="font-size: x-small;"> END_TOKEN, </span></div><div><span style="font-size: x-small;"> PADDING_TOKEN):</span></div><div><span style="font-size: x-small;"> super().__init__()</span></div><div><span style="font-size: x-small;"> self.sentence_embedding = SentenceEmbedding(max_sequence_length, d_model, language_to_index, START_TOKEN, END_TOKEN, PADDING_TOKEN)</span></div><div><span style="font-size: x-small;"> self.layers = SequentialEncoder(*[EncoderLayer(d_model, ffn_hidden, num_heads, drop_prob)</span></div><div><span style="font-size: x-small;"> for _ in range(num_layers)]) # 입력 레이어 수만큼 레이어 생성</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;"> def forward(self, x, self_attention_mask, start_token, end_token):</span></div><div><span style="font-size: x-small;"> x = self.sentence_embedding(x, start_token, end_token)</span></div><div><span style="font-size: x-small;"> x = self.layers(x, self_attention_mask)</span></div><div><span style="font-size: x-small;"> return x</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;">class MultiHeadCrossAttention(nn.Module):</span></div><div><span style="font-size: x-small;"> def __init__(self, d_model, num_heads):</span></div><div><span style="font-size: x-small;"> super().__init__()</span></div><div><span style="font-size: x-small;"> self.d_model = d_model</span></div><div><span style="font-size: x-small;"> self.num_heads = num_heads</span></div><div><span style="font-size: x-small;"> self.head_dim = d_model // num_heads</span></div><div><span style="font-size: x-small;"> self.kv_layer = nn.Linear(d_model , 2 * d_model)</span></div><div><span style="font-size: x-small;"> self.q_layer = nn.Linear(d_model , d_model)</span></div><div><span style="font-size: x-small;"> self.linear_layer = nn.Linear(d_model, d_model)</span></div><div><span style="font-size: x-small;"> </span></div><div><span style="font-size: x-small;"> def forward(self, x, y, mask): # 정의한 멀티헤드 어텐션과 거의 동일. 단, y는 디코더 출력, x는 인코더 출력임</span></div><div><span style="font-size: x-small;"> batch_size, sequence_length, d_model = x.size() # in practice, this is the same for both languages...so we can technically combine with normal attention</span></div><div><span style="font-size: x-small;"> kv = self.kv_layer(x)</span></div><div><span style="font-size: x-small;"> q = self.q_layer(y)</span></div><div><span style="color: red; font-size: x-small;"> kv = kv.reshape(batch_size, sequence_length, self.num_heads, 2 * self.head_dim)</span></div><div><span style="color: red; font-size: x-small;"> q = q.reshape(batch_size, sequence_length, self.num_heads, self.head_dim)</span></div><div><span style="font-size: x-small;"> kv = kv.permute(0, 2, 1, 3)</span></div><div><span style="font-size: x-small;"> q = q.permute(0, 2, 1, 3)</span></div><div><span style="font-size: x-small;"> k, v = kv.chunk(2, dim=-1)</span></div><div><span style="color: red; font-size: x-small;"> values, attention = scaled_dot_product(q, k, v, mask) # 인코더 출력 KV와 디코더 출력 Q가 함께 고려된 V출력. 어텐션 스코더 계산 출력.</span></div><div><span style="font-size: x-small;"> values = values.permute(0, 2, 1, 3).reshape(batch_size, sequence_length, d_model)</span></div><div><span style="font-size: x-small;"> out = self.linear_layer(values)</span></div><div><span style="font-size: x-small;"> return out</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;">class DecoderLayer(nn.Module):</span></div><div><span style="font-size: x-small;"> def __init__(self, d_model, ffn_hidden, num_heads, drop_prob):</span></div><div><span style="font-size: x-small;"> super(DecoderLayer, self).__init__()</span></div><div><span style="font-size: x-small;"> self.self_attention = MultiHeadAttention(d_model=d_model, num_heads=num_heads)</span></div><div><span style="font-size: x-small;"> self.layer_norm1 = LayerNormalization(parameters_shape=[d_model])</span></div><div><span style="font-size: x-small;"> self.dropout1 = nn.Dropout(p=drop_prob)</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;"> self.encoder_decoder_attention = MultiHeadCrossAttention(d_model=d_model, num_heads=num_heads)</span></div><div><span style="font-size: x-small;"> self.layer_norm2 = LayerNormalization(parameters_shape=[d_model])</span></div><div><span style="font-size: x-small;"> self.dropout2 = nn.Dropout(p=drop_prob)</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;"> self.ffn = PositionwiseFeedForward(d_model=d_model, hidden=ffn_hidden, drop_prob=drop_prob)</span></div><div><span style="font-size: x-small;"> self.layer_norm3 = LayerNormalization(parameters_shape=[d_model])</span></div><div><span style="font-size: x-small;"> self.dropout3 = nn.Dropout(p=drop_prob)</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;"> def forward(self, x, y, self_attention_mask, cross_attention_mask):</span></div><div><span style="font-size: x-small;"> _y = y.clone()</span></div><div><span style="font-size: x-small;"> y = self.self_attention(y, mask=self_attention_mask) # 멀티헤드 어텐션 계산</span></div><div><span style="font-size: x-small;"> y = self.dropout1(y)</span></div><div><span style="font-size: x-small;"> y = self.layer_norm1(y + _y)</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;"> _y = y.clone()</span></div><div><span style="font-size: x-small;"> y = self.encoder_decoder_attention(x, y, mask=cross_attention_mask) # 멀티헤드 크로스 어텐션 계산</span></div><div><span style="font-size: x-small;"> y = self.dropout2(y)</span></div><div><span style="font-size: x-small;"> y = self.layer_norm2(y + _y)</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;"> _y = y.clone()</span></div><div><span style="font-size: x-small;"> y = self.ffn(y)</span></div><div><span style="font-size: x-small;"> y = self.dropout3(y)</span></div><div><span style="font-size: x-small;"> y = self.layer_norm3(y + _y)</span></div><div><span style="font-size: x-small;"> return y</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;">class SequentialDecoder(nn.Sequential):</span></div><div><span style="font-size: x-small;"> def forward(self, *inputs):</span></div><div><span style="font-size: x-small;"> x, y, self_attention_mask, cross_attention_mask = inputs</span></div><div><span style="font-size: x-small;"> for module in self._modules.values():</span></div><div><span style="font-size: x-small;"> y = module(x, y, self_attention_mask, cross_attention_mask)</span></div><div><span style="font-size: x-small;"> return y</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;">class Decoder(nn.Module):</span></div><div><span style="font-size: x-small;"> def __init__(self, </span></div><div><span style="font-size: x-small;"> d_model, </span></div><div><span style="font-size: x-small;"> ffn_hidden, </span></div><div><span style="font-size: x-small;"> num_heads, </span></div><div><span style="font-size: x-small;"> drop_prob, </span></div><div><span style="font-size: x-small;"> num_layers,</span></div><div><span style="font-size: x-small;"> max_sequence_length,</span></div><div><span style="font-size: x-small;"> language_to_index,</span></div><div><span style="font-size: x-small;"> START_TOKEN,</span></div><div><span style="font-size: x-small;"> END_TOKEN, </span></div><div><span style="font-size: x-small;"> PADDING_TOKEN):</span></div><div><span style="font-size: x-small;"> super().__init__()</span></div><div><span style="font-size: x-small;"> self.sentence_embedding = SentenceEmbedding(max_sequence_length, d_model, language_to_index, START_TOKEN, END_TOKEN, PADDING_TOKEN)</span></div><div><span style="font-size: x-small;"> self.layers = SequentialDecoder(*[DecoderLayer(d_model, ffn_hidden, num_heads, drop_prob) for _ in range(num_layers)])</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;"> def forward(self, x, y, self_attention_mask, cross_attention_mask, start_token, end_token):</span></div><div><span style="font-size: x-small;"> y = self.sentence_embedding(y, start_token, end_token)</span></div><div><span style="font-size: x-small;"> y = self.layers(x, y, self_attention_mask, cross_attention_mask)</span></div><div><span style="font-size: x-small;"> return y</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;">class Transformer(nn.Module):</span></div><div><span style="font-size: x-small;"> def __init__(self, </span></div><div><span style="font-size: x-small;"> d_model, </span></div><div><span style="font-size: x-small;"> ffn_hidden, </span></div><div><span style="font-size: x-small;"> num_heads, </span></div><div><span style="font-size: x-small;"> drop_prob, </span></div><div><span style="font-size: x-small;"> num_layers,</span></div><div><span style="font-size: x-small;"> max_sequence_length, </span></div><div><span style="font-size: x-small;"> kn_vocab_size,</span></div><div><span style="font-size: x-small;"> english_to_index,</span></div><div><span style="font-size: x-small;"> kannada_to_index,</span></div><div><span style="font-size: x-small;"> START_TOKEN, </span></div><div><span style="font-size: x-small;"> END_TOKEN, </span></div><div><span style="font-size: x-small;"> PADDING_TOKEN</span></div><div><span style="font-size: x-small;"> ):</span></div><div><span style="font-size: x-small;"> super().__init__()</span></div><div><span style="font-size: x-small;"> self.encoder = Encoder(d_model, ffn_hidden, num_heads, drop_prob, num_layers, max_sequence_length, english_to_index, START_TOKEN, END_TOKEN, PADDING_TOKEN)</span></div><div><span style="font-size: x-small;"> self.decoder = Decoder(d_model, ffn_hidden, num_heads, drop_prob, num_layers, max_sequence_length, kannada_to_index, START_TOKEN, END_TOKEN, PADDING_TOKEN)</span></div><div><span style="font-size: x-small;"> self.linear = nn.Linear(d_model, kn_vocab_size)</span></div><div><span style="font-size: x-small;"> self.device = torch.device('cuda') if torch.cuda.is_available() else torch.device('cpu')</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;"> def forward(self, </span></div><div><span style="font-size: x-small;"> x, </span></div><div><span style="font-size: x-small;"> y, </span></div><div><span style="font-size: x-small;"> encoder_self_attention_mask=None, </span></div><div><span style="font-size: x-small;"> decoder_self_attention_mask=None, </span></div><div><span style="font-size: x-small;"> decoder_cross_attention_mask=None,</span></div><div><span style="font-size: x-small;"> enc_start_token=False,</span></div><div><span style="font-size: x-small;"> enc_end_token=False,</span></div><div><span style="font-size: x-small;"> dec_start_token=False, # We should make this true</span></div><div><span style="font-size: x-small;"> dec_end_token=False): # x, y are batch of sentences</span></div><div><span style="font-size: x-small;"> x = self.encoder(x, encoder_self_attention_mask, start_token=enc_start_token, end_token=enc_end_token)</span></div><div><span style="font-size: x-small;"> out = self.decoder(x, y, decoder_self_attention_mask, decoder_cross_attention_mask, start_token=dec_start_token, end_token=dec_end_token) # 인코더는 소스 언어(영어), 디코더는 목표언어(독일어)를 학습하고, 인코더에서 계산된 값이 KV벡터로 디코더에 입력됨. 디코더 어텐션 출력은 앞의 KV값과 함께 어텐션 계산되어, 어텐션 스코어가 출력됨. 이 과정을 반복학습함.</span></div><div><span style="font-size: x-small;"> out = self.linear(out)</span></div><div><span style="font-size: x-small;"> return out</span></div><div><br /></div><div><b>트랜스포머 학습하기</b></div><div>이제 트랜스포머에 인코더와 디코더가 구현되었으므로, 다음과 같은 순서로 학습을 진행하면 된다. 학습은 소스(입력) 언어(영어)의 문장과 이에 일치되어 번역되어야 할 목표 언어(독일어)가 배치 데이터로 동일하게 각각 인코더, 디코더에 입력된다. 이후, 어텐션 수식에 따라 인코더 학습 후, 디코더가 인코더 출력을 받아 학습한다. </div><div><span style="font-size: x-small;">total_loss = 0</span></div><div><span style="font-size: x-small;">num_epochs = 10</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;">for epoch in range(num_epochs):</span></div><div><span style="font-size: x-small;"> print(f"Epoch {epoch}")</span></div><div><span style="font-size: x-small;"> iterator = iter(train_loader)</span></div><div><span style="font-size: x-small;"> for batch_num, batch in enumerate(iterator): # 배치마다 30개 소스 언어, 목표 언어 문장이 있음. 이를 학습하게 됨.</span></div><div><span style="font-size: x-small;"> transformer.train()</span></div><div><span style="font-size: x-small;"> eng_batch, target_batch = batch</span></div><div><span style="font-size: x-small;"> encoder_self_attention_mask, decoder_self_attention_mask, decoder_cross_attention_mask = create_masks(eng_batch, target_batch)</span></div><div><span style="font-size: x-small;"> optim.zero_grad()</span></div><div><span style="font-size: x-small;"> target_predictions = transformer(eng_batch,</span></div><div><span style="font-size: x-small;"> target_batch,</span></div><div><span style="font-size: x-small;"> encoder_self_attention_mask.to(device), </span></div><div><span style="font-size: x-small;"> decoder_self_attention_mask.to(device), </span></div><div><span style="font-size: x-small;"> decoder_cross_attention_mask.to(device),</span></div><div><span style="font-size: x-small;"> enc_start_token=False,</span></div><div><span style="font-size: x-small;"> enc_end_token=False,</span></div><div><span style="font-size: x-small;"> dec_start_token=True,</span></div><div><span style="font-size: x-small;"> dec_end_token=True) # 학습 시작. 인코더는 소스 언어(영어), 디코더는 목표언어(독일어)를 학습하고, 인코더에서 계산된 값이 KV벡터로 디코더에 입력됨. 디코더 어텐션 출력은 앞의 KV값과 함께 어텐션 계산되어, 어텐션 스코어가 출력됨. 이 과정을 반복학습함. </span></div><div><span style="font-size: x-small;"> labels = transformer.decoder.sentence_embedding.batch_tokenize(target_batch, start_token=False, end_token=True)</span></div><div><span style="font-size: x-small;"> loss = criterian(</span></div><div><span style="font-size: x-small;"> target_predictions.view(-1, target_vocab_size).to(device),</span></div><div><span style="font-size: x-small;"> labels.view(-1).to(device)</span></div><div><span style="font-size: x-small;"> ).to(device)</span></div><div><span style="font-size: x-small;"> valid_indicies = torch.where(labels.view(-1) == kannada_to_index[PADDING_TOKEN], False, True)</span></div><div><span style="font-size: x-small;"> loss = loss.sum() / valid_indicies.sum()</span></div><div><span style="font-size: x-small;"> loss.backward()</span></div><div><span style="font-size: x-small;"> optim.step()</span></div><div><span style="font-size: x-small;"> #train_losses.append(loss.item())</span></div><div><span style="font-size: x-small;"> if batch_num % 100 == 0:</span></div><div><span style="font-size: x-small;"> print(f"Iteration {batch_num} : {loss.item()}")</span></div><div><span style="font-size: x-small;"> print(f"English: {eng_batch[0]}")</span></div><div><span style="font-size: x-small;"> target_sentence_predicted = torch.argmax(target_predictions[0], axis=1)</span></div><div><span style="font-size: x-small;"> predicted_sentence = ""</span></div><div><span style="font-size: x-small;"> for idx in target_sentence_predicted:</span></div><div><span style="font-size: x-small;"> if idx == kannada_to_index[END_TOKEN]:</span></div><div><span style="font-size: x-small;"> break</span></div><div><span style="font-size: x-small;"> predicted_sentence += index_to_kannada[idx.item()]</span></div><div><span style="font-size: x-small;"> print(f"Target Prediction: {predicted_sentence}")</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;"> transformer.eval()</span></div><div><span style="font-size: x-small;"> target_sentence = ("",)</span></div><div><span style="font-size: x-small;"> for word_counter in range(max_sequence_length):</span></div><div><span style="font-size: x-small;"> encoder_self_attention_mask, decoder_self_attention_mask, decoder_cross_attention_mask= create_masks(eng_sentence, target_sentence)</span></div><div><span style="font-size: x-small;"> predictions = transformer(eng_sentence,</span></div><div><span style="font-size: x-small;"> target_sentence,</span></div><div><span style="font-size: x-small;"> encoder_self_attention_mask.to(device), </span></div><div><span style="font-size: x-small;"> decoder_self_attention_mask.to(device), </span></div><div><span style="font-size: x-small;"> decoder_cross_attention_mask.to(device),</span></div><div><span style="font-size: x-small;"> enc_start_token=False,</span></div><div><span style="font-size: x-small;"> enc_end_token=False,</span></div><div><span style="font-size: x-small;"> dec_start_token=True,</span></div><div><span style="font-size: x-small;"> dec_end_token=False)</span></div><div><span style="font-size: x-small;"> next_token_prob_distribution = predictions[0][word_counter] # not actual probs</span></div><div><span style="font-size: x-small;"> next_token_index = torch.argmax(next_token_prob_distribution).item()</span></div><div><span style="font-size: x-small;"> next_token = index_to_kannada[next_token_index]</span></div><div><span style="font-size: x-small;"> target_sentence = (target_sentence[0] + next_token, )</span></div><div><span style="font-size: x-small;"> if next_token == END_TOKEN:</span></div><div><span style="font-size: x-small;"> break</span></div><div><br /></div><div>결과적으로 소스 언어(영어)의 텍스트에서 토큰 간 상호관계를 유사도를 통해 계산하여 학습된 결과는 목표 언어(독일어)에서 동일한 과정을 거쳐 출력된 Q값과 디코더 KV를 유사도 계산하여, 어텐션하는 과정을 반복 학습한다. 결과적으로, 문맥에서 초점이 되는 단어는 목표 언어에서도 동일하게 초점이 어텐션되므로, 이를 합해 유사도를 계산해, Query에 대한 Value를 얻는 어텐션 스코어를 얻을 수 있다.</div><div><br /></div><div><b>트랜스포머 전체 로직 호출 순서 분석</b></div><div>학습 부분의 트랜스포머 호출 순서를 다음과 같이 분석해 본다.</div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjGOEiRbBM6vnalECUBXoqBq21dJOsPAapwwk6obOygsdDEWozKmSpdFG_5W2TGFdYCv4JQeuYDz5C82j9GdO10mcVy8K05Qydawt8-T9_z8kpYmW8EA-HMowrVqiXsxY_LJZfSjeKziNUhX3fNOkCtj7t_1ekbaiI5Ym8ZGV7PloT-DyH49r-X1SYlL5Bg" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="1041" data-original-width="1967" height="237" src="https://blogger.googleusercontent.com/img/a/AVvXsEjGOEiRbBM6vnalECUBXoqBq21dJOsPAapwwk6obOygsdDEWozKmSpdFG_5W2TGFdYCv4JQeuYDz5C82j9GdO10mcVy8K05Qydawt8-T9_z8kpYmW8EA-HMowrVqiXsxY_LJZfSjeKziNUhX3fNOkCtj7t_1ekbaiI5Ym8ZGV7PloT-DyH49r-X1SYlL5Bg=w449-h237" width="449" /></a></div><div class="separator" style="clear: both; text-align: center;">트랜스포머 처리 분석 화면 일부</div><br /></div><div>결론적으로, 다음 순서와 같이 논문에서 설계한 방식과 동일하다. 학습 부분은 다른 딥러닝 학습 루프와 크게 다르지 않다. 다만, transformer 호출 부분만 차이가 있다. 여기서, eng_batch는 소스(입력) 언어 문장들 배치 데이터셋, target_batch는 목표 언어 문장들 배치 데이터셋이다. </div><div><ol style="text-align: left;"><li>Epoch 만큼 학습</li><ol><li>배치 데이터셋 학습</li><ol><li>target_prediction = transformer(eng_batch, target_batch, encoder_mask, decoder_mask, decoder_cross_attention_mask)</li><ol><li>x = encoder(eng_batch, encoder_mask)</li><ol><li>x = sentence_embedding(eng_batch)</li><li>x = laysers(x, encoder_mask)</li><li>return x</li></ol><li>out = decoder(x, target_batch, decoder_mask, decoder_cross_attention_mask)</li><ol><li>y = sentence_embedding(target_batch)</li><li><span style="color: red;">y = layers(x, y, decoder_mask, decoder_cross_attention_mask)</span></li><li>return y</li></ol><li>out = linear(out)</li></ol><li>labels = transformer.batch_tokenize(target_batch)</li><li>loss = CrossEntropyLoss(target_prediction, labels)</li><li>loss.backward()</li><li>optim.step()</li></ol></ol></ol><div>인코더의 출력 KV와 디코더 Q를 이용해 어텐션 스코어를 계산하는 부분은 앞의 코드에서 적색 부분에 해당한다. 이 디코더의 멀티헤드 크로스 어텐션 부분만 좀 더 자세히 보자. 우선, 여기에서 사용되는 인코더의 출력 KV값이 계산되는 부분을 살펴보면 다음과 같다. </div></div><div><div><span style="font-size: x-small;">class MultiHeadAttention(nn.Module):</span></div><div><span style="font-size: x-small;"> def __init__(self, d_model, num_heads):</span></div><div><span style="font-size: x-small;"> super().__init__()</span></div><div><span style="font-size: x-small;"> ...</span></div><div><span style="font-size: x-small;"> </span></div><div><span style="font-size: x-small;"> def forward(self, x, mask):</span></div><div><span style="font-size: x-small;"> batch_size, sequence_length, d_model = x.size() # (1,200,512)</span></div><div><span style="font-size: x-small;"> qkv = self.qkv_layer(x) # (1,200,1536)</span></div><div><span style="font-size: x-small;"> qkv = qkv.reshape(batch_size, sequence_length, self.num_heads, 3 * self.head_dim) # (1,200,8,192)</span></div><div><span style="font-size: x-small;"> qkv = qkv.permute(0, 2, 1, 3) # (1,8,200,192)</span></div><div><span style="font-size: x-small;"> q, k, v = qkv.chunk(3, dim=-1) # (1,8,200,64), </span><span style="font-size: small;">(1,8,200,64), </span><span style="font-size: small;">(1,8,200,64)</span></div><div><span style="color: red; font-size: x-small;"> values, attention = scaled_dot_product(q, k, v, mask) # (1,8,200,64), (1,8,200,200)</span></div><div><span style="font-size: x-small;"> values = values.permute(0, 2, 1, 3).reshape(batch_size, sequence_length, self.num_heads * self.head_dim) # (1,200,512)</span></div><div><span style="font-size: x-small;"> out = self.linear_layer(values) # (1,200,512)</span></div><div><span style="font-size: x-small;"> return out</span></div></div><div><span style="font-size: x-small;"><br /></span></div><div><div><span style="font-size: x-small;">class EncoderLayer(nn.Module):</span></div><div><span style="font-size: x-small;"> def __init__(self, d_model, ffn_hidden, num_heads, drop_prob):</span></div><div><span style="font-size: x-small;"> super(EncoderLayer, self).__init__()</span></div><div><span style="font-size: x-small;"> self.attention = MultiHeadAttention(d_model=d_model, num_heads=num_heads)</span></div><div><span style="font-size: x-small;"> self.norm1 = LayerNormalization(parameters_shape=[d_model])</span></div><div><span style="font-size: x-small;"> self.dropout1 = nn.Dropout(p=drop_prob)</span></div><div><span style="font-size: x-small;"> self.ffn = PositionwiseFeedForward(d_model=d_model, hidden=ffn_hidden, drop_prob=drop_prob)</span></div><div><span style="font-size: x-small;"> self.norm2 = LayerNormalization(parameters_shape=[d_model])</span></div><div><span style="font-size: x-small;"> self.dropout2 = nn.Dropout(p=drop_prob)</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;"> def forward(self, x, self_attention_mask):</span></div><div><span style="font-size: x-small;"> residual_x = x.clone()</span></div><div><span style="color: red; font-size: x-small;"> x = self.attention(x, mask=self_attention_mask) # (1,200,512)</span></div><div><span style="font-size: x-small;"> x = self.dropout1(x) </span><span style="font-size: small;"># (1,200,512)</span></div><div><span style="font-size: x-small;"> x = self.norm1(x + residual_x) </span><span style="font-size: small;"># (1,200,512)</span></div><div><span style="font-size: x-small;"> residual_x = x.clone() </span><span style="font-size: small;"># ...</span></div><div><span style="font-size: x-small;"> x = self.ffn(x) </span></div><div><span style="font-size: x-small;"> x = self.dropout2(x)</span></div><div><span style="font-size: x-small;"> x = self.norm2(x + residual_x) </span><span style="font-size: small;"># (1,200,512)</span></div><div><span style="font-size: x-small;"> return x </span><span style="font-size: small;"># (1,200,512)</span></div></div><div><span style="font-size: x-small;"><br /></span></div><div><div><span style="font-size: x-small;">class Encoder(nn.Module):</span></div><div><span style="font-size: x-small;"> def __init__(self, ...</span></div><div><span style="font-size: x-small;"> super().__init__()</span></div><div><span style="font-size: x-small;"> self.sentence_embedding = SentenceEmbedding(max_sequence_length, d_model, language_to_index)</span></div><div><span style="font-size: x-small;"> self.layers = SequentialEncoder(*[EncoderLayer(d_model, ffn_hidden, num_heads, drop_prob)</span></div><div><span style="font-size: x-small;"> for _ in range(num_layers)])</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;"> def forward(self, x, self_attention_mask, start_token, end_token):</span></div><div><span style="font-size: x-small;"> x = self.sentence_embedding(x, start_token, end_token) # (1,200,512)</span></div><div><span style="font-size: x-small;"> x = self.layers(x, self_attention_mask) # (1,200,512)</span></div><div><span style="font-size: x-small;"> return x</span></div></div><div><span style="font-size: x-small;"><br /></span></div><div>단순히, 계산과정을 보았을 때는 소스 텍스트 입력 받아, 토큰으로 분리하고, 임베딩한 후, 이를 멀티헤드 어텐션 계산해, linear 레이어를 통과해준 것에 불과하다. 이 값을 디코더에서 받아, 멀티헤드 크로스 어텐션을 계산한다. 텐서 계산 과정 이해를 위해, 입력에 대해 계산 중간 텐서 모양이 어떻게 변화하는 지 확인해 보자.</div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;"><div>class Transformer(nn.Module):</div><div> def __init__(self, ...):</div><div> super().__init__()</div><div> self.encoder = Encoder(d_model, ffn_hidden, num_heads, drop_prob, num_layers, ...)</div><div> self.decoder = Decoder(d_model, ffn_hidden, num_heads, drop_prob, num_layers, ...)</div><div> self.linear = nn.Linear(d_model, kn_vocab_size)</div><div> self.device = torch.device('cuda') if torch.cuda.is_available() else torch.device('cpu')</div><div><br /></div><div> def forward(self, x, y, ...):</div><div><span style="color: red;"> x = self.encoder(x, encoder_self_attention_mask, start_token=enc_start_token, ...) # (1,200,512)</span></div><div> out = self.decoder(x, y, decoder_self_attention_mask, decoder_cross_attention_mask, ...) # </div><div> out = self.linear(out)</div><div> return out</div></span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;"><div>class Decoder(nn.Module):</div><div> def __init__(self, ...):</div><div> super().__init__()</div><div> self.sentence_embedding = SentenceEmbedding(max_sequence_length, d_model, ...)</div><div> self.layers = SequentialDecoder(*[DecoderLayer(d_model, ffn_hidden, num_heads, drop_prob) for _ in range(num_layers)])</div><div><br /></div><div> def forward(self, x, y, self_attention_mask, cross_attention_mask, start_token, end_token):</div><div><span style="color: red;"> y = self.sentence_embedding(y, start_token, end_token) # y=(1,200,512) from y input(token string)</span></div><div> y = self.layers(x, y, self_attention_mask, cross_attention_mask) # </div><div> return y</div><div><br /></div></span></div><div><div><span style="font-size: x-small;">class MultiHeadCrossAttention(nn.Module):</span></div><div><span style="font-size: x-small;"> def __init__(self, d_model, num_heads):</span></div><div><span style="font-size: x-small;"> super().__init__()</span></div><div><span style="font-size: x-small;"><div><span style="color: red;"> self.kv_layer = nn.Linear(d_model , 2 * d_model) # (512) => (2 * 512)</span></div><div> self.q_layer = nn.Linear(d_model , d_model) # (512) => (512)</div></span></div><div><span style="font-size: x-small;"> ...</span></div><div><span style="font-size: x-small;"> </span></div><div><span style="font-size: x-small;"> def forward(self, x, y, mask):</span></div><div><span style="font-size: x-small;"> batch_size, sequence_length, d_model = x.size() # in practice, this is the same for both languages...so we can technically combine with normal attention</span></div><div><span style="font-size: x-small;"> kv = self.kv_layer(x) # x=(1, 200, 512), kv=(1,200,1024)</span></div><div><span style="font-size: x-small;"> q = self.q_layer(y) # y=(1,200,512), kv=(1, 200, 1024)</span></div><div><span style="color: red; font-size: x-small;"> kv = kv.reshape(batch_size, sequence_length, self.num_heads, 2 * self.head_dim) # (1, 200, 8, 128)</span></div><div><span style="color: red; font-size: x-small;"> q = q.reshape(batch_size, sequence_length, self.num_heads, self.head_dim) # (1, 200, 8, 64)</span></div><div><span style="font-size: x-small;"> kv = kv.permute(0, 2, 1, 3) # (1, 8, 200, 128)</span></div><div><span style="font-size: x-small;"> q = q.permute(0, 2, 1, 3) # (1, 8, 200, 64)</span></div><div><span style="font-size: x-small;"> k, v = kv.chunk(2, dim=-1) # k=(1, 8, 200, 64), v=(1, 8, 200, 64)</span></div><div><span style="color: red; font-size: x-small;"> values, attention = scaled_dot_product(q, k, v, mask) # q=(1,8,200,64), k=(1,8,200,64), v=(1,8,200,64), values=(1,8,200,64), attention=(1,8,200,200)</span></div><div><span style="font-size: x-small;"> values = values.permute(0, 2, 1, 3).reshape(batch_size, sequence_length, d_model) # (1, 200, 512)</span></div><div><span style="font-size: x-small;"> out = self.linear_layer(values) # (1,200,512)</span></div><div><span style="font-size: x-small;"> return out</span></div></div><div><br /></div><div>트랜스포머에서 계산된 라벨값은 다음 수식으로 loss를 계산한 후 역전파하여, 학습 에포크가 진행될 수도록 loss가 줄어들도록 각 신경망층의 가중치를 조정해 나간다.</div><div><span style="font-size: x-small;"><br /></span></div><div style="text-align: left;"><span style="font-size: x-small;"> loss = CrossEntropyLoss(target_prediction, labels)</span></div><div><span style="font-size: x-small;"> loss.backward()</span></div><div><span style="font-size: x-small;"><br /></span></div><div>결론적으로 디코더의 어텐션 스코어는 입력 텍스트와 출력 텍스트 간의 label 오차가 최소가 되도록 계산되게 된다. </div><div><br /></div><div><b>마무리</b></div><div>이 글에서 트랜스포머 디코더 구현 과정을 살펴보았다. 지금까지 깊게 트랜스포머의 동작 원리를 코드 수준에서 구현하고 확인해 보았다. 허깅페이스 서비스 등에서는 관련된 코드와 예제를 모두 구현해 제공하고 있어, 간단한 호출로 트랜스포머를 사용할 수 있다.</div><div><ul style="text-align: left;"><li><a href="https://huggingface.co/docs/transformers/tasks/translation">Translation (huggingface.co)</a></li></ul></div><div>이 글은 많은 레퍼런스들을 참고해, 가능한 트랜스포머 동작방식을 이해하기 쉽도록 정리한 것이다. 관련해 궁금하다면, 이 글 마지막에 있는 레퍼런스들을 살펴보길 바란다. </div></div></div><div><br /></div><div><div><b><span style="font-size: medium;">레퍼런스</span></b></div><div><ul><li><a href="https://daddynkidsmakers.blogspot.com/2017/02/blog-post_24.html">머신러닝 딥러닝 신경망 개념, 종류 및 개발</a></li><li><a href="https://daddynkidsmakers.blogspot.com/2022/11/3.html">딥러닝 기반 3차원 스캔 데이터, 포인트 클라우드 학습</a></li><li><a href="https://daddynkidsmakers.blogspot.com/2023/03/generative-ai.html">생성(Generative) AI 오픈소스 딥러닝 모델 Stable Diffusion, ControlNet 개념 및 ComfyUI 사용법</a></li><li><a href="https://github.com/mac999/computer_vision_deeplearning/tree/main">Computer_vision_deeplearning: computer vision based on deep learning lecture materials, Github</a></li><li><a href="https://arxiv.org/abs/1706.03762">Attention Is All You Need</a>, Google</li><li><a href="https://uvadlc-notebooks.readthedocs.io/en/latest/tutorial_notebooks/tutorial6/Transformers_and_MHAttention.html">Transformers and Multi-Head Attention — UvA DL Notebooks v1.2 documentation (uvadlc-notebooks.readthedocs.io)</a></li><li><a href="https://www.youtube.com/watch?v=QCJQG4DuHT0&list=PLTl9hO2Oobd97qfWC40gOSU8C0iu0m2l4">Self Attention in Transformer Neural Networks</a></li><li><a href="https://dev.to/kjlubick/building-an-ml-transformer-in-a-spreadsheet-36g">Building a ML Transformer in a Spreadsheet - DEV Community</a></li><li><a href="https://e2eml.school/transformers.html">Transformers from Scratch (e2eml.school)</a></li><li><a href="https://theaisummer.com/transformer/">How Transformers work in deep learning and NLP: an intuitive introduction | AI Summer (theaisummer.com)</a></li><li><a href="https://rutube.ru/video/14cf5e9556207a6d27c05d9e870266a6/">Building a ML Transformer in a Spreadsheet</a></li><li><a href="https://docs.google.com/spreadsheets/d/1a66brmCdnU8oED2qCzAxX6NNa1KpTeJOv2_pSifXA5A/edit#gid=985698178">ML Transformer in a Spreadsheet Template - Google Sheets</a></li><li><a href="https://docs.google.com/spreadsheets/d/1nbz3z88-0b41dLliRkKcXPnEk_xGd3LmPCqjmUxSWtU/edit#gid=1641427179">ML Transformer in a Spreadsheet - Google Sheets</a></li><li><a href="https://arxiv.org/pdf/1706.03762.pdf">Attention is All you need</a> (<a href="https://silhyeonha-git.tistory.com/16">참고</a>1, <a href="https://cypsw.tistory.com/entry/Attention-Is-All-You-Need">참고</a>2)</li><li><a href="https://www.youtube.com/watch?v=XfpMkf4rD6E">Stanford CS25: V2 I Introduction to Transformers</a></li><li><a href="https://www.youtube.com/watch?v=67n0eudtcqI">Appendix to Building a ML Transformer in a Spreadsheet</a></li><li><a href="https://www.sciencedirect.com/science/article/abs/pii/S0926580521003800">Transformer machine learning language model for auto-alignment of long-term and short-term plans in construction - ScienceDirect</a></li><li><a href="https://www.youtube.com/watch?v=XfpMkf4rD6E">Stanford CS25: V2 I Introduction to Transformers w/ Andrej Karpathy</a></li><li><a href="https://kazemnejad.com/blog/transformer_architecture_positional_encoding/">Transformer Architecture: The Positional Encoding - Amirhossein Kazemnejad's Blog</a> (#<a href="https://www.blossominkyung.com/deeplearning/transfomer-positional-encoding">1</a>)</li><li><a href="https://medium.com/deeper-learning/glossary-of-deep-learning-word-embedding-f90c3cec34ca">Glossary of Deep Learning: Word Embedding | by Jaron Collis | Deeper Learning | Medium</a></li><li><a href="http://mccormickml.com/2016/04/19/word2vec-tutorial-the-skip-gram-model/">Word2Vec Tutorial - The Skip-Gram Model · Chris McCormick (mccormickml.com)</a></li><li><a href="https://synthesis.ai/2023/01/05/modern-generative-ai-an-overview/">Generative AI Models in Image Generation: Overview - Synthesis AI</a></li><li><a href="https://kazemnejad.com/blog/transformer_architecture_positional_encoding/">Transformer Architecture: The Positional Encoding - Amirhossein Kazemnejad's Blog</a></li><li><a href="https://lilianweng.github.io/posts/2018-06-24-attention/">Attention? Attention! | Lil'Log (lilianweng.github.io)</a></li><li><a href="https://arxiv.org/pdf/1601.06733.pdf" style="text-align: center;">Long Short-Term Memory-Networks for Machine Reading</a><span style="text-align: center;">, 2016</span></li><li><span style="text-align: center;"><a href="https://towardsdatascience.com/illustrated-self-attention-2d627e33b20a">Illustrated: Self-Attention. A step-by-step guide to self-attention… | by Raimi Karim | Towards Data Science</a></span></li><li><a href="https://www.analyticsvidhya.com/blog/2019/01/fundamentals-deep-learning-recurrent-neural-networks-scratch-python/">RNN From Scratch | Building RNN Model In Python (analyticsvidhya.com)</a></li><li><a href="https://medium.com/nerd-for-tech/recurrent-neural-networks-3a0adb1d4515">Recurrent Neural Networks From Scratch | by Maciej Balawejder | Nerd For Tech | Medium</a></li><li><a href="https://pub.towardsai.net/building-a-recurrent-neural-network-from-scratch-in-python-3ad244b1054f">Building A Recurrent Neural Network From Scratch In Python | by Youssef Hosni | Towards AI</a></li></ul></div></div><div><br /></div><div><br /></div><div><br /></div><div><br /></div></div><div><br /></div><div><br /></div><div><br /></div>Daddy Makerhttp://www.blogger.com/profile/02275758703365576780noreply@blogger.com0tag:blogger.com,1999:blog-5201956450461596914.post-4351270836501939612024-02-03T19:47:00.000-08:002024-03-03T02:10:58.779-08:00Django 사용한 간단한 데쉬보드 웹 어플리케이션 개발하기<div style="text-align: left;">이 글은 Django 사용한 간단한 데쉬보드 웹 어플리케이션 개발 이야기를 나눔한다.<br /></div><div style="text-align: left;"><br /></div><div style="text-align: left;">웹 어플리케이션 개발 방법은 다양하지만, 몇 년 사이에 유행하고 있는 트랜드는 마이크로 서비스 개발이 가능한 플라스크 같은 플랫폼을 사용하는 것이다. 여기서는 개발자가 애용하고 있는 파이썬 기반 Django(장고) 플랫폼을 사용해 웹 서비스 개발 방법을 알아본다.</div><div style="text-align: left;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjtIwVrgwFFU53A3wNX2RW9-aQ7itKQ5m_f2XtvlAod_rXExBKKEtqVMZQFKLJpZ_aj9La8tGN2z4myGQyrsihxCOrfbKsvM0cNXC3xW5ilG7DqHY_s_19gyXht-yqdcbPR04vL-GaSQezpXD-tJx1RHZlDAI-f-0wSQdbeImA-qQd0Ln9G9OblByY5dNi6" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="168" data-original-width="300" height="112" src="https://blogger.googleusercontent.com/img/a/AVvXsEjtIwVrgwFFU53A3wNX2RW9-aQ7itKQ5m_f2XtvlAod_rXExBKKEtqVMZQFKLJpZ_aj9La8tGN2z4myGQyrsihxCOrfbKsvM0cNXC3xW5ilG7DqHY_s_19gyXht-yqdcbPR04vL-GaSQezpXD-tJx1RHZlDAI-f-0wSQdbeImA-qQd0Ln9G9OblByY5dNi6=w200-h112" width="200" /></a></div><div style="text-align: left;">장고를 이용하면, 파이썬의 다양한 라이브러리를 사용한 웹 앱을 손쉽게 개발할 수 있다. 장고보다 경량의 플랫폼인 플라스크 사용법은 다음 링크를 참고한다.</div><div style="text-align: left;"><ul style="text-align: left;"><li><a href="https://daddynkidsmakers.blogspot.com/2020/09/django-flask-open-api.html">간단한 Flask, mysql, 파이썬 기반 Open API 웹서버 개발 방법 </a></li></ul></div>이 글은 다음 레퍼런스를 참고한다.</div><div style="text-align: left;"><ul style="text-align: left;"><li><a href="https://docs.djangoproject.com/en/5.0/topics/install/#database-installation">Django installation</a></li><li><a href="https://docs.djangoproject.com/en/5.0/">Django documentation | Django documentation | Django (djangoproject.com)</a></li></ul></div><div style="text-align: left;">이 글의 소스코드는 다음 링크를 참고한다. </div><div style="text-align: left;"><ul style="text-align: left;"><li><a href="https://github.com/mac999/django_dashboard/tree/main">mac999/django_dashboard (github.com)</a></li></ul></div><div style="text-align: left;"><br /></div><div style="text-align: left;"><b>개발 환경 준비</b></div><div style="text-align: left;">다음과 같이 개발 환경을 준비한다.</div><div style="text-align: left;"><ol style="text-align: left;"><li>데이터베이스 설치: Django 설치시 sqlite db가 설치된다. 만약, 다른 DB를 사용하고 싶다면, <a href="https://mariadb.org/download/?t=mariadb&p=mariadb&r=11.4.0&os=windows&cpu=x86_64&pkg=msi&m=blendbyte">MariaDB</a>, MySQL, PostgreDB 등을 설치한다. </li><li>데이터베이스 커넥터 설치: pip install mysqlclient 명령으로 커넥터 드라이버 설치한다.</li><li>장고 설치: pip install Django</li></ol><div>제대로 설치되었다면, 다음 코드를 입력해 실행한다. 정상동작하면 성공한 것이다. </div><div><div>import django</div><div>print(django.get_version())</div></div><div><br /></div><div><b>데쉬보드 개발해보기</b></div><div>터미널에서 다음 명령을 실행해본다.</div><div>django-admin startproject monitoring</div><div>cd monitoring</div><div>python manage.py runserver</div><div><br /></div><div>이제, <a href="http://127.0.0.1:8000/">The install worked successfully! Congratulations!</a>를 클릭해 본다. 다음과 같이 웹 앱이 실행되면 성공한 것이다. </div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgS_vbbkMDu0r2GJB9E7-zqVRFanOcNE8LFsAwyHJXVHNw3FDirZW4B7w17QQQQJERhmG_T8jS6O29iqBZgzDx4Wrr4PRfYhFRgnk3wVbF1ft7M9lbJw30rRJlCDrJ14XrjjEdjDt1aFQ1GfXY_oBkEV3peB-qyJuZU3MntY4Cg-OjCj4ACM_VrS0YJuQ4_" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="311" data-original-width="611" height="204" src="https://blogger.googleusercontent.com/img/a/AVvXsEgS_vbbkMDu0r2GJB9E7-zqVRFanOcNE8LFsAwyHJXVHNw3FDirZW4B7w17QQQQJERhmG_T8jS6O29iqBZgzDx4Wrr4PRfYhFRgnk3wVbF1ft7M9lbJw30rRJlCDrJ14XrjjEdjDt1aFQ1GfXY_oBkEV3peB-qyJuZU3MntY4Cg-OjCj4ACM_VrS0YJuQ4_=w400-h204" width="400" /></a></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEi160QbxabQBXiXzzfg48yo-xUkhbyxzjLVu5SCClwF5fZMU6k4PU6rLCw84_gQ7l3FIepFckEVtlkIGpnQ4MlVHPQCmvprEJHgArFdJOmj4_F2EowHV4DjHvmMEcIGiWaU_LzuhLqOBgzbtd5sXo3HxOgrqQ_YTmYrgYSKl8JGg_FG-EysU9YAs1mqLz9A" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="491" data-original-width="1001" height="196" src="https://blogger.googleusercontent.com/img/a/AVvXsEi160QbxabQBXiXzzfg48yo-xUkhbyxzjLVu5SCClwF5fZMU6k4PU6rLCw84_gQ7l3FIepFckEVtlkIGpnQ4MlVHPQCmvprEJHgArFdJOmj4_F2EowHV4DjHvmMEcIGiWaU_LzuhLqOBgzbtd5sXo3HxOgrqQ_YTmYrgYSKl8JGg_FG-EysU9YAs1mqLz9A=w400-h196" width="400" /></a></div><div><br /></div>터미널에 다음 같이 실행한다. 이 결과로 dashboard 앱 코드가 자동생성될 것이다. </div><div>python manage.py startapp dashboard</div><div><br /></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEi3Bav7uJ6r222pP86KDjpQ9SHTZJIVxcaYSsfDznE9X6BSazM4VlceLKEAFVtQ97arqq22iabAhenSIgXJAaBIc3jGn2cY6Q8JhLjhAxLEWNZMSFIdAK9lbL46OSd7o27y-MqsX6-b77VwWFAA6rukzjZi-LnDvnaI6m45IlkjyP3k4v-3DaUd_VGaVq-_" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="295" data-original-width="368" height="240" src="https://blogger.googleusercontent.com/img/a/AVvXsEi3Bav7uJ6r222pP86KDjpQ9SHTZJIVxcaYSsfDznE9X6BSazM4VlceLKEAFVtQ97arqq22iabAhenSIgXJAaBIc3jGn2cY6Q8JhLjhAxLEWNZMSFIdAK9lbL46OSd7o27y-MqsX6-b77VwWFAA6rukzjZi-LnDvnaI6m45IlkjyP3k4v-3DaUd_VGaVq-_" width="299" /></a></div><div class="separator" style="clear: both; text-align: center;">자동생성된 앱 코드 소스 파일</div><div><br /></div>각 파일 역할은 다음과 같다. </div><div><ul style="text-align: left;"><li>__init__.py : 이 앱에 필요한 파이썬 패키지를 초기화하고, 임포트함</li><li>admin.py : 장고 관리자 페이지를 위한 설정</li><li>apps.py : app 설정 </li><li>models.py : 장고 <a href="https://en.wikipedia.org/wiki/Object%E2%80%93relational_mapping">ORM(Object Relationship Mapping)</a>을 위한 클래스 정의</li><li>tests.py : 테스트 클래스 정의</li><li>views.py : 데이터가 웹 템플릿에 의해 보여지는 방식을 정의</li></ul>생성된 웹 앱을 등록하기 위해, monitoring/settings.py을 다음과 같이 편집한다. </div><div><div>INSTALLED_APPS = [</div><div> 'django.contrib.admin',</div><div> 'django.contrib.auth',</div><div> 'django.contrib.contenttypes',</div><div> 'django.contrib.sessions',</div><div> 'django.contrib.messages',</div><div> 'django.contrib.staticfiles',</div><div> 'dashboard'</div><div>]</div></div><div><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhxcbVdoJd1s_C8mz1fFwVDbaMYCi63-wdmGl7QbUt4GE6nLIx2jn7QXzRRokWAsmRu3kWpeOM7jzfj8Xc_WNNI_UXuNRNIe5S7ee8VqKhdH--jvmPNIYs1mutzoi3hA13QhBg3oCOJDHVtRe7K_9_CPI7Oj9wOZnkSKsEkPOzAnt9v3p0AkuR6Li9McTaL" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="515" data-original-width="781" height="211" src="https://blogger.googleusercontent.com/img/a/AVvXsEhxcbVdoJd1s_C8mz1fFwVDbaMYCi63-wdmGl7QbUt4GE6nLIx2jn7QXzRRokWAsmRu3kWpeOM7jzfj8Xc_WNNI_UXuNRNIe5S7ee8VqKhdH--jvmPNIYs1mutzoi3hA13QhBg3oCOJDHVtRe7K_9_CPI7Oj9wOZnkSKsEkPOzAnt9v3p0AkuR6Li9McTaL" width="320" /></a></div>편집 화면 일부<br /></div><br />views.py에 데쉬보드가 보여질 방법을 코딩한다. 이 파일을 편집해 다음과 같이 코딩한다. </div><div><div>from django.http import JsonResponse</div><div>from django.shortcuts import render</div><div>from dashboard.models import Order</div><div>from django.core import serializers</div><div><br /></div><div>def dashboard(request): // 이 함수는 사용자가 URL 입력 시 호출됨</div><div> return render(request, 'dashboard.html', {}) // 이때 HTML 템플릿을 사용해 렌더링</div><div><br /></div><div>def order_data(request): // HTML 렌더링 시 전달될 DB의 데이터는 JSON 형태로 변환 리턴</div><div> dataset = Order.objects.all()</div><div> data = serializers.serialize('json', dataset)</div><div> return JsonResponse(data, safe=False)</div><div><br /></div></div><div>여기서, 데쉬보드에 렌더링될 템플릿 HTML파일을 리턴하는 dashboard..()함수, 보여질 데이터를 준비하는 pivot...() 함수를 준비해 놓았다. 참고로, 아직 구현되지 않은 HTML과 Order 모델은 이후 작업될 것이다. </div><div><br /></div><div><b>URL-함수 맵핑</b></div><div>앞서 정의된 함수를 사용자 URL 입력 시 실행되도록 맵핑해야 한다. </div><div>이를 위해, monitoring/urls.py를 다음과 같이 편집한다. </div><div><div>from django.contrib import admin</div><div>from django.urls import path, include</div><div><br /></div><div>urlpatterns = [</div><div> path('admin/', admin.site.urls),</div><div> path('dashboard/', include('dashboard.urls'))</div><div>]</div></div><div><br /></div><div>위 코드에서 include에 urls.py를 사용하므로, 이 파일을 코딩해야 한다. dashboard/urls.py 파일을 다음같이 편집한다. 앞서 정의된 함수와 URL이 연결된 것을 알 수 있다.</div><div><div><div>from django.urls import path</div><div>from . import views</div><div><br /></div><div>urlpatterns = [</div><div> path('', views.dashboard, name='dashboard'),</div><div> path('data', views.order_data, name='order_data'),</div><div>]</div></div></div><div><br /><b>모델 개발</b></div><div>이제 보여줄 데이터를 가지고 있는 모델을 코딩한다. 여기서는 가벼운 SQLite를 데이터베이스로 사용한다. </div><div><div>from django.db import models</div><div><br /></div><div>class Order(models.Model):</div><div> product_category = models.CharField(max_length=20)</div><div> payment_method = models.CharField(max_length=50)</div><div> shipping_cost = models.CharField(max_length=50)</div><div> unit_price = models.DecimalField(max_digits=5, decimal_places=2)</div></div><div><br /></div><div>이제 정의된 모델 클래스를 데이터베이스 테이블로 생성해줘야 데이터를 저장할 수 있다. 이 처리를 위해, 다음 명령으로 마이그레이션을 한다.</div><div><div>python manage.py makemigrations dashboard</div><div><br /></div></div><div>결과, 다음과 같이 자동으로 지정 폴더의 model을 읽어 ORM처리 하도록 설정된다. </div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEiXvm-06hL16c15AV0d_HpYSvfNvenVtRF8Y08Qr84cScbrho07-I2D05gy8Xej59RkwC1qVe5m8Ajd_6YQcCPMHfzS6WBLLsk9_Us2U2VkZkO6aWARCOULBLNX9ZrflwJ8nYI55TKTR3iqUEqpwDi49XVg3Tj4HXc7bt8E1wCWpOsNbwxKL2n5ddSBZoSA" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="91" data-original-width="756" height="62" src="https://blogger.googleusercontent.com/img/a/AVvXsEiXvm-06hL16c15AV0d_HpYSvfNvenVtRF8Y08Qr84cScbrho07-I2D05gy8Xej59RkwC1qVe5m8Ajd_6YQcCPMHfzS6WBLLsk9_Us2U2VkZkO6aWARCOULBLNX9ZrflwJ8nYI55TKTR3iqUEqpwDi49XVg3Tj4HXc7bt8E1wCWpOsNbwxKL2n5ddSBZoSA=w504-h62" width="504" /></a></div><br />다음 명령으로 DB 테이블을 만든다.</div><div>python manage.py migrate dashboard</div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhNLk3ogvY6treZ8DmHsrwrQZnJECuSu3BECpv1ABM5xvkNRQ51HGITAkESjb7X49ZPISAW6_c-Kn_eWZ8SCzbObx30o-4o4FMrDN__IRSYmAxYXhQm6W1MQSuKyP2uIjfJPTPkr44iV9CezDmmW9s8_ATrtOAzdehBexhkDw7NeqqXfmin1qX0XEIwzGhH" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="100" data-original-width="686" height="73" src="https://blogger.googleusercontent.com/img/a/AVvXsEhNLk3ogvY6treZ8DmHsrwrQZnJECuSu3BECpv1ABM5xvkNRQ51HGITAkESjb7X49ZPISAW6_c-Kn_eWZ8SCzbObx30o-4o4FMrDN__IRSYmAxYXhQm6W1MQSuKyP2uIjfJPTPkr44iV9CezDmmW9s8_ATrtOAzdehBexhkDw7NeqqXfmin1qX0XEIwzGhH=w497-h73" width="497" /></a></div><div class="separator" style="clear: both; text-align: center;">명령 실행 결과</div><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgJXny070ITA8UdEpVoruyA9G8zuuAOvNXmTmPd5kfx3NlRzr3fKd_Sv-zusAELUUDXRNyZsl0JBnylLNPYMTErL0v8OrSmzZk_7B-TXEL_x4MDCmp-0hGEKu1LopBLUIPt-BWAZKh3ouzfau9DR5pR38otk0yVKr9oRBqwf9PPerelLNnJ7QpJkwqCnYjF" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="127" data-original-width="263" height="92" src="https://blogger.googleusercontent.com/img/a/AVvXsEgJXny070ITA8UdEpVoruyA9G8zuuAOvNXmTmPd5kfx3NlRzr3fKd_Sv-zusAELUUDXRNyZsl0JBnylLNPYMTErL0v8OrSmzZk_7B-TXEL_x4MDCmp-0hGEKu1LopBLUIPt-BWAZKh3ouzfau9DR5pR38otk0yVKr9oRBqwf9PPerelLNnJ7QpJkwqCnYjF=w190-h92" width="190" /></a></div>테이블 생성 후 Sqlite DB 파일<br /></div><div><br /></div>DB 테이블이 생성되었으니, 예제로 사용할 데이터를 INSERT해보자. 다음 명령을 실행한다.</div><div><div>python manage.py shell</div><div><br /></div><div>DB를 조작할 수 있는 터미널이 실행될 것이다. 여기서 파이썬 코드를 실행할 수 있다. 다음을 입력한다. </div><div><div>from dashboard.models import Order</div><div><br /></div><div>o1 = Order(</div><div> product_category='Books',</div><div> payment_method='Credit Card',</div><div> shipping_cost=39,</div><div> unit_price=59</div><div>)</div><div><br /></div><div>o1.save()</div></div><div><br /></div><div>이런 방식으로 원하는 만큼 데이터 레코드를 INSERT할 수 있다.</div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEg2Yvq5M1x38SH6AJ0Badtq2E0N7hV34ECG3TKgdHevAquLX7LqVSkFV0CuKL9Zzlm58Qh8OMn6dCdmDwLGwuy9Fexz8-YF1cyDjjiGf1hHV2112M6dn5V2YQFp58TLVx7DgO_mLc5nLvz5Hng6k1qUXhYl37edauc0KWSRpz9Ebqh2cYdF6v6R-KtlXRcA" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="354" data-original-width="401" height="240" src="https://blogger.googleusercontent.com/img/a/AVvXsEg2Yvq5M1x38SH6AJ0Badtq2E0N7hV34ECG3TKgdHevAquLX7LqVSkFV0CuKL9Zzlm58Qh8OMn6dCdmDwLGwuy9Fexz8-YF1cyDjjiGf1hHV2112M6dn5V2YQFp58TLVx7DgO_mLc5nLvz5Hng6k1qUXhYl37edauc0KWSRpz9Ebqh2cYdF6v6R-KtlXRcA" width="272" /></a></div><div class="separator" style="clear: both; text-align: center;">데이터 생성 모습</div><div><br /></div><div>참고로, 다음 명령으로 생성된 데이터를 리스트할 수 있다. 셀의 상세 기능은 <a href="https://docs.djangoproject.com/en/5.0/">여기</a>를 참고한다. </div><div><div>for u in Order.objects.all():</div><div> print(u)</div><div> dic = u.__dict__</div><div> for f, v in dic.items():</div><div> print(f'{f}: {v}')</div></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgJLKRmOzt6nWyFeu4Q6eSQH2fGNedv_RbLSWBYylQIUJGxo5vgBYGZNbQWQN0NyqUTDQ85-bOqsNPx4UTTKUYBzf5MukVGRGyj8imuYQ_ZL_801tr73hBbPNOM2evns_cHWvdawpanFHcykQWUuGHmMZaCFqF4eje5HoJHR4q20Wjj0EDwFl7sufPzv7Ih" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="491" data-original-width="662" height="237" src="https://blogger.googleusercontent.com/img/a/AVvXsEgJLKRmOzt6nWyFeu4Q6eSQH2fGNedv_RbLSWBYylQIUJGxo5vgBYGZNbQWQN0NyqUTDQ85-bOqsNPx4UTTKUYBzf5MukVGRGyj8imuYQ_ZL_801tr73hBbPNOM2evns_cHWvdawpanFHcykQWUuGHmMZaCFqF4eje5HoJHR4q20Wjj0EDwFl7sufPzv7Ih" width="320" /></a></div><div class="separator" style="clear: both; text-align: center;">데이터 리스트 모습</div></div><div><br /></div><b>데쉬보드 UI와 데이터 연결</b></div><div>이제 준비된 데이터를 데쉬보드 UI에 연결해야 한다. </div><div><br /></div><div>웹에서 UI 처리는 크게 '요청-응답' 방식, 비동기 요청 방식(AJAX)이 있다. 여기서는 AJAX 방식을 사용한다. 이를 위해 jQuery $.ajax 함수를 사용하였다. 관련된 상세 사용법은 아래 링크를 참고한다. </div><div><ul style="text-align: left;"><li><a href="https://api.jquery.com/jQuery.ajax/">jQuery $.ajax 함수 메뉴얼</a></li></ul></div><div>templates/dashboard...html 파일을 다음과 같이 편집한다. 데쉬보드는 <a href="https://www.flexmonster.com/">Flexmonster</a> UI로 만든 테이블, 원 차트 두개로 구성된다. </div><div><div><script></div><div>function processData(dataset) {</div><div> var result = []</div><div> dataset = JSON.parse(dataset);</div><div> dataset.forEach(item => result.push(item.fields));</div><div> return result;</div><div>}</div><div>$.ajax({ // 비동기 jQuery 호출. 데이터는 DB에 미리 준비됨. URL HTML 렌더링은 비동기. </div><div> url: $("#pivot-table-container").attr("data-url"),</div><div> dataType: 'json',</div><div> success: function(data) {</div><div> new Flexmonster({</div><div> container: "#pivot-table-container",</div><div> componentFolder: "https://cdn.flexmonster.com/",</div><div> width: "100%",</div><div> height: 430,</div><div> toolbar: true,</div><div> report: {</div><div> dataSource: {</div><div> type: "json",</div><div> data: processData(data)</div><div> },</div><div> slice: {}</div><div> }</div><div> });</div><div> new Flexmonster({</div><div> container: "#pivot-chart-container",</div><div> componentFolder: "https://cdn.flexmonster.com/",</div><div> width: "100%",</div><div> height: 430,</div><div> //toolbar: true,</div><div> report: {</div><div> dataSource: {</div><div> type: "json",</div><div> data: processData(data)</div><div> },</div><div> slice: {},</div><div> "options": {</div><div> "viewType": "charts",</div><div> "chart": {</div><div> "type": "pie"</div><div> }</div><div> }</div><div> }</div><div> });</div><div> }</div><div>});</div><div></script></div></div><div><br /></div><div>여기서 사용된 데쉬보드 UI인 Flexmonster는 ajax모드에서 pivot-table-container ID를 가진 div에 임베딩되도록 설정되어 있다. </div><div><br />여기서, processData는 URL질의 결과 리턴되는 JSON 텍스트를 파싱해, result 리스트에 각 아이템을 추가하는 역할을 한다. 이 함수는 Flexmonster란 데쉬보드 UI의 report의 datasource에 지정되어, UI와 데이터를 연결하는 역할을 한다. </div><div><br /></div><div>Flexmonster 데쉬보드는 데이터소스 속성이 보여지는 방식을 설정할 수 있다. 다음과 같이 편집한다. </div><div><div>dataSource: {</div><div><span style="white-space: normal;"><span style="white-space: pre;"> </span>type: "json",</span></div><div><span style="white-space: normal;"><span style="white-space: pre;"> </span>data: processData(data),</span></div><div><span style="white-space: normal;"><span style="white-space: pre;"> </span>mapping: {</span></div><div><span style="white-space: normal;"><span style="white-space: pre;"> </span>"product_category": {</span></div><div><span style="white-space: normal;"><span style="white-space: pre;"> </span>"caption": "Product Category",</span></div><div><span style="white-space: normal;"><span style="white-space: pre;"> </span>"type": "string"</span></div><div><span style="white-space: normal;"><span style="white-space: pre;"> </span>},</span></div><div><span style="white-space: normal;"><span style="white-space: pre;"> </span>"payment_method": {</span></div><div><span style="white-space: normal;"><span style="white-space: pre;"> </span>"caption": "Payment Method",</span></div><div><span style="white-space: normal;"><span style="white-space: pre;"> </span>"type": "string"</span></div><div><span style="white-space: normal;"><span style="white-space: pre;"> </span>},</span></div><div><span style="white-space: normal;"><span style="white-space: pre;"> </span>"shipping_cost": {</span></div><div><span style="white-space: normal;"><span style="white-space: pre;"> </span>"caption": "Shipping Cost",</span></div><div><span style="white-space: normal;"><span style="white-space: pre;"> </span>"type": "number"</span></div><div><span style="white-space: normal;"><span style="white-space: pre;"> </span>},</span></div><div><span style="white-space: normal;"><span style="white-space: pre;"> </span>"unit_price": {</span></div><div><span style="white-space: normal;"><span style="white-space: pre;"> </span>"caption": "Unit Price",</span></div><div><span style="white-space: normal;"><span style="white-space: pre;"> </span>"type": "number"</span></div><div><span style="white-space: normal;"><span style="white-space: pre;"> </span>}</span></div><div><span style="white-space: normal;"><span style="white-space: pre;"> </span>}<span style="white-space: pre;"> </span></span></div><div>},</div></div><div><br /></div></div><div>이제 다음 명령으로 개발한 웹 앱을 실행해 본다. </div><div><div>python manage.py runserver</div><div><br /></div><div>URL은 <a href="http://127.0.0.1:8000/dashboard/">Dashboard</a>를 클릭한다. 다음과 같이 보여지면 성공한 것이다. </div></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjqXWR-Dv7ipGZT9vgrmEJ_hSe7HBq4E0b1Tgz7zXYoiN2sleoW8ZgDVJ46kvMA3WScc_OjZrmMq1jc9N0qbs4TIeY9OwsHTi_6ciXeKA19UWWTywkVu0dBBuSrur46cVW7JwuxlAnTPNXkRujIhCEROW-8L3HhDQG3FC_E3kUcLsQT49wh_faq-trBHFoQ" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="1087" data-original-width="1017" height="313" src="https://blogger.googleusercontent.com/img/a/AVvXsEjqXWR-Dv7ipGZT9vgrmEJ_hSe7HBq4E0b1Tgz7zXYoiN2sleoW8ZgDVJ46kvMA3WScc_OjZrmMq1jc9N0qbs4TIeY9OwsHTi_6ciXeKA19UWWTywkVu0dBBuSrur46cVW7JwuxlAnTPNXkRujIhCEROW-8L3HhDQG3FC_E3kUcLsQT49wh_faq-trBHFoQ=w293-h313" width="293" /></a></div><div class="separator" style="clear: both; text-align: center;">장고로 개발된 웹 앱 실행 결과</div><div><br /></div><div><b>맵 연동 </b></div><div>이제 맵과 연동해 본다. 다음은 <a href="https://leafletjs.com/examples/quick-start/">리플릿</a>과 Plotly을 이용한 간단한 장고 웹 앱 실행 결과이다.</div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgB6DCTWQhWwAJ2AZQiBcaMv0zCw8TCQuNV1x6f9YR8UJrW27PzbBxiBb0vg1iiXV_oh13rztpHDQAeXFUQLhlPvfU21kkJZ4GqFVQQyPfM1HFXmWPjsiGtmXcLWTIyACTc-Wzw2ANglGQkvGpnY9ysDe_r068v0Cza9fsobUBAoyHmcv7XK2j5Khor_hkv" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="1024" data-original-width="928" height="303" src="https://blogger.googleusercontent.com/img/a/AVvXsEgB6DCTWQhWwAJ2AZQiBcaMv0zCw8TCQuNV1x6f9YR8UJrW27PzbBxiBb0vg1iiXV_oh13rztpHDQAeXFUQLhlPvfU21kkJZ4GqFVQQyPfM1HFXmWPjsiGtmXcLWTIyACTc-Wzw2ANglGQkvGpnY9ysDe_r068v0Cza9fsobUBAoyHmcv7XK2j5Khor_hkv=w275-h303" width="275" /></a><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjsDzNr0p5A8hKXCfxWBFpsUfg-kbBerVX-xrNxJuuknz2RP0wocBzujjXCTRPBDirore5Fkcf9_MEeGfmIHHV9sy-v8iyDXSzt1YqbuPMB2q7HbpYP3EwOXjFfaSSiyXoLgGQ0ZlwsZKPeJ4cCcHH7EG0rIYyq_fT6jBgmjN5wY85iaxFCNcAYrD2PvzGW" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="1061" data-original-width="1002" height="303" src="https://blogger.googleusercontent.com/img/a/AVvXsEjsDzNr0p5A8hKXCfxWBFpsUfg-kbBerVX-xrNxJuuknz2RP0wocBzujjXCTRPBDirore5Fkcf9_MEeGfmIHHV9sy-v8iyDXSzt1YqbuPMB2q7HbpYP3EwOXjFfaSSiyXoLgGQ0ZlwsZKPeJ4cCcHH7EG0rIYyq_fT6jBgmjN5wY85iaxFCNcAYrD2PvzGW=w287-h303" width="287" /></a><br /><br /></div><br /></div><div><b>코드 디버깅</b></div><div>장고로 개발한 코드를 디버깅할 수 있으면, 실행 상태에서 변수, 콜 스택 등을 쉽게 확인할 수 있다. 우선, 디버깅 버튼의 launch.json 생성 버튼을 클릭해, .vscode 폴더에 launch.json을 생성한다.</div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEiwOOBrC371VPhObfLHU8ivwyalNXbgYVg-e-fPZwQzeRjGRMNl-bglvW0grVWFdlWD9kmKwRqGu5UdgQNi8uFWBTfOXXgVekNMEIbBMqitMcA6qHliEsiI-NLSq7xLalcnTZCNjiH_4RiWUDrBXQWC25IRdbA7LAOpTl3pwYLTY9wP_tZ4fmJ2_Ip27mTD" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="610" data-original-width="1057" height="185" src="https://blogger.googleusercontent.com/img/a/AVvXsEiwOOBrC371VPhObfLHU8ivwyalNXbgYVg-e-fPZwQzeRjGRMNl-bglvW0grVWFdlWD9kmKwRqGu5UdgQNi8uFWBTfOXXgVekNMEIbBMqitMcA6qHliEsiI-NLSq7xLalcnTZCNjiH_4RiWUDrBXQWC25IRdbA7LAOpTl3pwYLTY9wP_tZ4fmJ2_Ip27mTD" width="320" /></a></div><div class="separator" style="clear: both; text-align: center;">Debug의 create a launch.json 버튼</div><div class="separator" style="clear: both; text-align: center;"><br /></div></div><div>다음과 같이 launch.json를 수정한다. 보다시피, 디버깅할 programd의 args를 설정해 두었다. 본 실습에는 chrome대신 edge를 사용하였기에, 두번째 설정은 msedge로 타입을 지정하였다.</div><div><div>{</div><div> "version": "0.2.0",</div><div> "configurations": [</div><div> {</div><div> "name": "Django",</div><div> "type": "debugpy",</div><div> "request": "launch",</div><div> "program": "${workspaceFolder}\\manage.py",</div><div> "args": [</div><div> "runserver",</div><div> "--noreload"</div><div> ], </div><div> "django": true,</div><div> "justMyCode": true</div><div> }, </div><div> {</div><div> "name": "Edge",</div><div> "type": "msedge",</div><div> "request": "launch",</div><div> "url": "http://localhost:8000",</div><div> "webRoot": "${workspaceFolder}" </div><div> } </div><div> ]</div><div>}</div></div><div><br /></div><div style="text-align: center;"><img alt="" data-original-height="704" data-original-width="1109" height="255" src="https://blogger.googleusercontent.com/img/a/AVvXsEjxtcuFLpCYnoiSMX1cSVNesnvLjrU66CwmvPmu1yt5peUQX8kmo8H53GS_KUJFrXXCNjCte0WZWiHteO89BMdvURJ-ivnv_pOM2UFbGBIcQRprSb_ChHmAxpo7R0C0Eu-myKj940rlYDlBr9YOENplO52AYtkMKJcyszPsMWLLzPL2_Pblefb-7Yrnpw91=w402-h255" style="color: #0000ee;" width="402" /></div><div style="text-align: center;">생성된 launch.json 파일</div><div><br /></div><div>이제 manager.py를 선택하고 디버깅 버튼을 클릭하면 다음과 같이, 코드를 디버그할 수 있다.</div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgOoQkxThtHTFHj-KumRu38AV4Pag1kv9WOBBFUYBYLjnMjjd5gK5w-C9xijno_qI0KYTNbh2Daf89UhHuuhwyQQLbkWNiw-J1hn58spWBNB1ZORZeX7aFknC31ElTs9LieGtcNHLhAwWx8dI8RiyyLJgtBVvLSIUM1PoRkwVSKnTMM_Ms4cr5pUaaWXazm" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="1099" data-original-width="2048" height="172" src="https://blogger.googleusercontent.com/img/a/AVvXsEgOoQkxThtHTFHj-KumRu38AV4Pag1kv9WOBBFUYBYLjnMjjd5gK5w-C9xijno_qI0KYTNbh2Daf89UhHuuhwyQQLbkWNiw-J1hn58spWBNB1ZORZeX7aFknC31ElTs9LieGtcNHLhAwWx8dI8RiyyLJgtBVvLSIUM1PoRkwVSKnTMM_Ms4cr5pUaaWXazm" width="320" /></a></div><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEj-AHbJykFjgP05bzVoLW0-a9UsOrmw_Jgvb5OZX4bIpcvVkHa8tG80_A6h8EImMcoxFaq5s1empc0XtCl5TQhlZH3Gx3XmgNNq5kiw4hNeNtEOkJc6zvuQumb7xrYN_5T1AbZiAL0GfUbfAI_KD7PZS3rygoFZWC_WqPUbpBA-T3erJNgGHMEs6_gdHqyM" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="756" data-original-width="1118" height="216" src="https://blogger.googleusercontent.com/img/a/AVvXsEj-AHbJykFjgP05bzVoLW0-a9UsOrmw_Jgvb5OZX4bIpcvVkHa8tG80_A6h8EImMcoxFaq5s1empc0XtCl5TQhlZH3Gx3XmgNNq5kiw4hNeNtEOkJc6zvuQumb7xrYN_5T1AbZiAL0GfUbfAI_KD7PZS3rygoFZWC_WqPUbpBA-T3erJNgGHMEs6_gdHqyM" width="320" /></a></div><div class="separator" style="clear: both; text-align: center;">장고 웹 앱 디버깅 모습</div></div></div><div><br /></div><div>로그는 settings.py에 다음 LOGGING 사전을 추가하고, 원하는 코드에 사용하면 된다. </div><div><div>LOGGING = {</div><div> "version": 1,</div><div> "disable_existing_loggers": False,</div><div> "handlers": {</div><div> "console": {"class": "logging.StreamHandler"},</div><div> },</div><div> "loggers": {</div><div> "django": {</div><div> "handlers": ["console"],</div><div> "level": "INFO",</div><div> },</div><div> }</div><div>}</div></div><div><br /></div><div>코드는 다음과 같다. </div><div><div>import logging</div><div><div>import logging.config</div></div><div>logger = logging.getLogger('django')</div><div><br /></div><div>logger.info('here goes your message')</div></div><div><br /></div><div>def your_func():</div><div><div> logging.config.fileConfig('logging.conf')</div><div> logger = logging.getLogger('applog')</div><div><br /></div><div> logger.debug('debug message')</div><div> logger.info('info message')</div><div> logger.warn('warn message')</div><div> logger.error('error message')</div><div> logger.critical('critical message')</div></div><div><br /></div><div>좀 더 상세한 디버깅 방법은 다음을 참고한다.</div><div><ul style="text-align: left;"><li><a href="https://code.visualstudio.com/docs/python/tutorial-django">Python and Django tutorial in Visual Studio Code</a></li><li><a href="https://www.appsloveworld.com/django/100/429/how-to-debug-django-javascript-within-vscode">How to Debug Django Javascript within VSCode?</a></li></ul></div><div><br /></div><b>마무리</b></div><div>이 글을 통해, 간단히 장고로 만든 앱 앱을 코딩하고 실행해 보았다. 이와 같은 방식으로 다양한 웹 앱을 개발할 수 있을 것이다. </div><div><br /></div><div><b>레퍼런스</b></div><div><ul style="text-align: left;"><li><a href="https://realpython.com/location-based-app-with-geodjango-tutorial/">Make a Location-Based Web App With Django and GeoDjango – Real Python</a></li><li><a href="https://www.freecodecamp.org/news/how-to-create-an-analytics-dashboard-in-django-app/">How to create an analytics dashboard in a Django app (freecodecamp.org)</a></li><li><a href="https://simpleisbetterthancomplex.com/article/2017/08/07/a-minimal-django-application.html">A Minimal Django Application (simpleisbetterthancomplex.com)</a></li><li><a href="https://medium.com/@h4k1m0u/displaying-a-map-in-a-django-webapp-2-3-develop-a-gis-webapp-with-geodjango-c831522ccf79">Displaying a map in a Django Webapp (2/3): Develop a GIS webapp with GeoDjango | by Hakim Benoudjit | Medium</a></li><li><a href="https://codeeverywhere.medium.com/top-10-data-visualization-tools-with-node-js-c4a5f2e0012f">Top 10 Data Visualization Tools with Node.js | by Code Everywhere | Jul, 2023 | Medium | Medium</a></li><li><a href="https://www.mongodb.com/docs/compass/current/query/filter/?utm_source=compass&utm_medium=product">Query Your Data — MongoDB Compass</a></li><li><a href="https://dev.to/kuba_szw/django-logging-forget-about-print-when-debugging-3g11">Django logging - forget about 'print' when debugging - DEV Community</a></li></ul></div><div><b>부록: Mongo 데이터베이스 연결</b></div><div>일반적으로, Django가 지원하는 database는 sqlite, mysql, postgresql 이다. 그러므로, mongo database를 settings.py에서 설정한 후 실행해보면, 다음과 같이 에러가 발생할 수 있다.</div><div><br /></div><div><div>django.core.exceptions.ImproperlyConfigured: 'django_mongodb_engine' isn't an available database backend.</div><div>Try using 'django.db.backends.XXX', where XXX is one of:</div><div>u'base', u'mysql', u'oracle', u'postgresql_psycopg2', u'sqlite3'</div><div>Error was: cannot import name BaseDatabaseFeatures</div></div><div><br /></div><div>이 경우, 다음과 같이 패키지를 설치한다. <a href="https://github.com/doableware/djongo">djongo</a>는 mongodb와 django를 연결해 준다. </div><div><div>pip install djongo</div></div><div><div>pip install pytz</div><div><br /></div></div><div><br /></div></div>Daddy Makerhttp://www.blogger.com/profile/02275758703365576780noreply@blogger.com0tag:blogger.com,1999:blog-5201956450461596914.post-322755343192647522023-12-25T21:47:00.000-08:002024-01-26T06:04:32.267-08:00딥러닝 강화학습 기본 개념, 내부 동작 구조, 라이브러리와 개발방법<div style="text-align: left;">이 글은 생성AI 모델 학습과 같이 현재도 다양한 곳에서 필수적으로 사용되는 강화학습 딥러닝 기술의 기본 개념, 이론적 배경, 내부 작동 메커니즘을 확인한다. 그리고, 케라스 및 파이토치 기반 강화학습 라이브러리를 이용한 인공지능 게임 학습, 주식 자동 거래 개발 방법을 나눔한다. </div><div style="text-align: left;"><br /></div><div style="text-align: left;">참고로, 이글에서 실습한 내용은 다음 github에서 다운로드 가능하다.</div><div style="text-align: left;"><ul style="text-align: left;"><li><a href="https://github.com/mac999/reinforcement_learning">latest code for reinforcement learning (github.com)</a></li></ul></div><div style="text-align: left;"><br /></div><div style="text-align: left;"><span style="font-size: medium;"><b>머리말</b></span></div><div style="text-align: left;">강화학습은 바둑, 로봇 제어와 같은 제한된 환경에서 최대 효과를 얻는 응용에 주로 많이 사용된다. 강화학습 코딩 전에 사전에 강화학습 개념을 미리 이해하고 있어야 제대로 된 개발이 가능하다. 대부분 강화학습에 대해 설명한 인터넷글은 핵심 개념에 대해 다루기 보다는 실행 코드만 나열한 경우가 많아, 실행 메커니즘을 이해하기 어렵다. 메커니즘을 이해할 수 없으면, 응용 기술을 개발하기 어렵다. 그러므로, 이 글은 강화학습 메커니즘과 개념 발전의 역사를 먼저 살펴본다.</div><div style="text-align: left;"><div><br /></div></div><div style="text-align: left;">강화학습 개발 시 이 글은 OpenAI가 개발한 <a href="https://www.gymlibrary.dev/index.html">Gym</a>을 사용해 기본적인 강화학습 실행 방법을 확인한다. 참고로, github 등에 공유된 강화학습 예시는 대부분 게임, 로보틱스 분야에 치중되어 있는 것을 확인할 수 있다. 여기서는 CartPole 예제로 기본적인 라이브러리 사용법을 확인하고, 게임 이외에 주식 트레이딩, 가상화폐, ESG 탄소 트레이딩, 에너지 활용 설비 운영과 같은 실용적인 문제를 풀기 위한 방법을 알아본다.</div><div style="text-align: left;"><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEh2S_PLA6XkImqog3NSf_QyxelKwWHMZbYeMf6AsbtTQIe2c6836TM1a472t815B3_Xw1m4P4T95fq6qyj6bLyRpmjS-LvieNq0iCxEvwDd9hS1fvGsNwBQx2V7-xewkFIqb1UcSJuGWQYIgEs4Lpd4Vz3c70UMUULJ2AdXBnjLzSC4Jlk9yRGCrs21FdqJ" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="644" data-original-width="1434" height="164" src="https://blogger.googleusercontent.com/img/a/AVvXsEh2S_PLA6XkImqog3NSf_QyxelKwWHMZbYeMf6AsbtTQIe2c6836TM1a472t815B3_Xw1m4P4T95fq6qyj6bLyRpmjS-LvieNq0iCxEvwDd9hS1fvGsNwBQx2V7-xewkFIqb1UcSJuGWQYIgEs4Lpd4Vz3c70UMUULJ2AdXBnjLzSC4Jlk9yRGCrs21FdqJ=w366-h164" width="366" /></a></div><div class="separator" style="clear: both; text-align: center;">강화학습 개념(Google)</div></div><div class="separator" style="clear: both; text-align: center;"><br /></div><div style="text-align: left;"><b><span style="font-size: medium;">강화학습 동작 메커니즘</span></b></div><div style="text-align: left;"><div>강화학습 개발 전에 동작 메커니즘을 간략히 정리하고 지나가자. </div><div><br /></div></div><div style="text-align: left;"><b>1. 강화학습 에이전트, 환경, 정책, 보상</b></div><div style="text-align: left;">강화학습의 목적은 주어진 환경(environment) 내에서 에이전트(agent)가 액션(action)을 취할 때, 보상 정책(policy)에 따라 관련된 변수 상태 s와 보상이 수정된다. 이를 반복하여, 총보상 r을 최대화하는 방식으로 모델을 학습한다. 정책은 보상 방식을 알고리즘화한 것이다. 다음 그림은 이를 보여준다. 이는 우리가 게임을 하며, 학습하는 것과 매우 유사한 방식이다. </div><div style="text-align: left;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEh7qNX4NZofT1D3BHtS8SQ2zK5caFErA98SzCkVOfWmrfM9rV7qaJLADecp-b1dnea4PXRTBn7xf6nBLByw909QbD_608nouWz3qpRcK9AhspLAw4NO_1GSEXFC69_0rktsZE9UGl4IUzXR3mQ4bHZkCJFTZcwF-yQTVE2lWL2OQ-kk1mxR-CeVFdKYjL9j" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="289" data-original-width="798" height="145" src="https://blogger.googleusercontent.com/img/a/AVvXsEh7qNX4NZofT1D3BHtS8SQ2zK5caFErA98SzCkVOfWmrfM9rV7qaJLADecp-b1dnea4PXRTBn7xf6nBLByw909QbD_608nouWz3qpRcK9AhspLAw4NO_1GSEXFC69_0rktsZE9UGl4IUzXR3mQ4bHZkCJFTZcwF-yQTVE2lWL2OQ-kk1mxR-CeVFdKYjL9j=w400-h145" width="400" /></a></div><div class="separator" style="clear: both; text-align: center;">강화학습 에이전트, 환경, 액션, 보상 개념(towardsdatascience)</div><br /></div><div style="text-align: left;">강화학습 설계자는 처음부터 시간에 따른 보상 개념을 고려했다. 모든 시간 경과에 따른 보상치를 동시에 계산하는 것은 무리가 있으므로, 이를 해결하기 위해, DQN(Deep Q-Network)과 같은 알고리즘이 개발되었다. </div><div style="text-align: left;"><br /></div><div style="text-align: left;">모든 강화학습 라이브러리는 이런 개념을 일반화한 클래스, 함수를 제공한다. 다음은 강화학습 라이브러리를 사용한 일반적인 개발 코드 패턴을 보여준다.</div><div style="text-align: left;"><br /></div><div style="text-align: left;"><span style="color: #274e13;">train_data, test_data = load_dataset() # 학습, 테스트용 데이터셋 로딩</span></div><div style="text-align: left;"><span style="color: #274e13;"><br /></span></div><div style="text-align: left;"><span style="color: #274e13;">class custom_env(gym): # 환경 정책 클래스 정의</span></div><div style="text-align: left;"><span style="color: #274e13;"> def __init__(self, data):</span></div><div style="text-align: left;"><span style="color: #274e13;"> # 환경 변수 초기화</span></div><div style="text-align: left;"><span style="color: #274e13;"> def reset():</span></div><div style="text-align: left;"><span style="color: #274e13;"> # 학습 초기 상태로 리셋</span></div><div style="text-align: left;"><span style="color: #274e13;"> def step(action):</span></div><div><span style="color: #274e13;"> # 학습에 필요한 관찰 데이터 변수 획득</span></div><div><span style="color: #274e13;"> # 액션을 취하면, 그때 관찰 데이터, 보상값을 리턴함</span></div><div style="text-align: left;"><span style="color: #274e13;"><br /></span></div><div style="text-align: left;"><span style="color: #274e13;">env = custom_env(train_data) # 학습환경 생성. 관찰 데이터에 따른 보상을 계산함</span></div><div style="text-align: left;"><span style="color: #274e13;">model = AgentModel(env) # 에이전트 학습 모델 정의. 보상을 극대화하도록 설계</span></div><div style="text-align: left;"><span style="color: #274e13;">model.learn() # 보상이 극대화되도록 학습</span></div><div style="text-align: left;"><span style="color: #274e13;">model.save('trained_model') # 학습된 파일 저장</span></div><div style="text-align: left;"><span style="color: #274e13;"><br /></span></div><div style="text-align: left;"><span style="color: #274e13;"># 학습된 강화학습 모델 기반 시뮬레이션 및 성능 비교</span></div><div><span style="color: #274e13;">env = custom_env(test_data) # 테스트환경 생성</span></div><div style="text-align: left;"><span style="color: #274e13;">observed_state = env.reset()</span></div><div style="text-align: left;"><span style="color: #274e13;">while not done:</span></div><div style="text-align: left;"><span style="color: #274e13;"> action = model.predict(observed_state) </span><span style="color: #274e13;"># 테스트 관찰 데이터에 따른 극대화된 보상 액션</span></div><div style="text-align: left;"><span style="color: #274e13;"> observed_state, reward, done, info = env.step(action) </span></div><div style="text-align: left;"><div><div><span style="color: #274e13;"> # al1_reward = env.step(al1_action) # 다른 알고리즘에 의한 액션 보상값과 성능비교</span></div><div><span style="color: #274e13;"> # human_reward = env.step(human_action) # 인간의 액션 보상값과 성능비교</span></div><div><br /></div></div></div><div style="text-align: left;"><div class="separator" style="clear: both;">이 코드에서 gym은 강화학습의 보상정책이 포함된 알고리즘을 캡슐화한 클래스이다. 많은 강화학습 라이브러리는 이런 환경을 다양하게 준비해 제공한다. 일반적으로 라이브러리에서 제공되는 강화학습 환경에는 게임, 로봇제어 등이며, 모두 규칙에 따른 보상을 계산하는 로직을 포함한다. 강화학습을 지원하는 유명 오픈소스 개발도구들은 다음과 같다. </div><div class="separator" style="clear: both;"><ul><li><a href="https://builtin.com/software-engineering-perspectives/openai-gym">OpenAI Gym:</a> 강화학습 기반 알고리즘, 게임, 로봇 시뮬레이션 예제 제공. (예. <a href="https://tradingtechai.medium.com/building-an-algorithmic-trading-strategy-using-reinforcement-learning-4ab12488d190">트레이딩</a>, <a href="https://towardsdatascience.com/convenient-reinforcement-learning-with-stable-baselines3-dccf466b7585">CartPole</a>)</li><li><a href="https://stable-baselines3.readthedocs.io/en/v0.11.1/">Stable Baselines3</a>: 파이토치 기반 DQN, PPO, A2C, SAC 알고리즘 제공</li><li><a href="https://www.tensorflow.org/agents/tutorials/2_environments_tutorial">tf-agents</a>: 텐서플로우 기반 강화학습 알고리즘 제공</li><li><a href="https://github.com/keras-rl/keras-rl">Keras-RL</a>: 간단한 모듈식 API 제공 (예. <a href="https://www.mlq.ai/deep-reinforcement-learning-for-trading-with-tensorflow-2-0/">주식 거래</a>)</li><li><a href="https://docs.ray.io/en/latest/rllib/index.html">RLlib</a>: 강화학습 알고리즘 및 분산 학습지원</li><li><a href="https://github.com/google/dopamine/tree/master">Dopamine</a>: 구글에서 개발 제공. 다양한 알고리즘 유틸리티 구현</li><li><a href="https://github.com/Unity-Technologies/ml-agents">Unity ML-Agents</a>: 유니티 환경에서 RL 에이전트 학습 제공</li></ul></div><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjI4MhDNTKVK6_TIhveGg-74UnmrYn_shcEXqIz7m8AaIfp_bknIcGg89bda-eixrPO9Yi9ZtqUXcCHY2XdMD_3cv_t4W8NFNPuFVjMFbbPY3xOvqQKFfELBhHjWxcYk1HfsR6ouvEiv0XsQMJW9M7nEJZ4bP6mA_U6STmm1NQ66mxaIfv061k3z_7-N7Is" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="432" data-original-width="768" height="180" src="https://blogger.googleusercontent.com/img/a/AVvXsEjI4MhDNTKVK6_TIhveGg-74UnmrYn_shcEXqIz7m8AaIfp_bknIcGg89bda-eixrPO9Yi9ZtqUXcCHY2XdMD_3cv_t4W8NFNPuFVjMFbbPY3xOvqQKFfELBhHjWxcYk1HfsR6ouvEiv0XsQMJW9M7nEJZ4bP6mA_U6STmm1NQ66mxaIfv061k3z_7-N7Is" width="320" /></a></div>Unity ML-Agents 제공 예제<br /><br /></div><div class="separator" style="clear: both;">이외, 다음과 같이 강화학습을 이용한 예제들이 있다.</div><div class="separator" style="clear: both;"><ul><li>아마존 딥레이서(<a href="https://aws.amazon.com/ko/deepracer/">DeepRacer</a>): 무인자율주행 시뮬레이션</li><li><a href="https://mujoco.org/">MuJoCo</a>: 물리 시뮬레이션 </li><li>OpenAI Gym 주식 트레이딩 봇 <a href="https://github.com/AminHP/gym-anytrading">gym-anytrading</a>: 주식 거래 강화학습</li><li><a href="https://github.com/iSarmad/RL-GAN-Net">RL-GAN-Net</a>: 누락된 점군을 이용한 형상 생성</li><li><a href="https://github.com/AndreaVidali/Deep-QLearning-Agent-for-Traffic-Signal-Control">SUMO</a>: 교통 시뮬레이션</li><li><a href="https://openai.com/blog/ingredients-for-robotics-research/">Hindsight Experience</a>: 로봇 암 시뮬레이션</li></ul></div><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgHuZm3P4IVmK-emY-jujmFdvZQlA2kHHWH_jv4c2T2QvuUZ_sxX6Qt4w4WA9KgAwtJrzKkatvDyWuPMuzKX7oKDQd4UMyQgsJ5ZGOp3fPE3pD3SGAwbjziuKgiWpJVt5C3tYViMjZS9qe7dBWRKyObQvhB16Oz1OiNhClQkNTx6cWZcxLu4pptTuUPjJ_y" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="371" data-original-width="696" height="214" src="https://blogger.googleusercontent.com/img/a/AVvXsEgHuZm3P4IVmK-emY-jujmFdvZQlA2kHHWH_jv4c2T2QvuUZ_sxX6Qt4w4WA9KgAwtJrzKkatvDyWuPMuzKX7oKDQd4UMyQgsJ5ZGOp3fPE3pD3SGAwbjziuKgiWpJVt5C3tYViMjZS9qe7dBWRKyObQvhB16Oz1OiNhClQkNTx6cWZcxLu4pptTuUPjJ_y=w400-h214" width="400" /></a></div>MuJoCo 시뮬레이션 예시<br /></div><div class="separator" style="clear: both; text-align: center;"><br /></div></div><div style="text-align: left;"><b>2. 딥러닝 기반 Q-Learning 알고리즘(DQN) 동작 방식</b></div><div style="text-align: left;"><div>강화학습 시 기본이 되는 알고리즘은 딥러닝 기반 Q-Learning 알고리즘(DQN)이다. DQN은 Q-Learning을 비용 함수로 사용해 딥러닝 학습을 하는 방식이다. DQN은 2015년 DeepMind에서 개발되어, 알파고 등에 사용되었다. </div><div><br /></div><div>Q-Learning은 Q-function의 개념을 기반으로 한다. 정책 π, Q(S, a)의 Q-function(state-action value function)은 먼저 행동 α를 취한 후, 정책 π를 따름으로서, 상태 s로 부터 얻는 보상의 할인된 합계를 계산한다. 최적의 Q-function Q*(s, a)은 관찰값 s로 부터 시작해, 행동α를 취하고, 이후 최적 보상을 받는 정책에 따름으로서 최대 이익을 얻는다. 이를 위해, 벨만 방정식(Bellman equation)을 사용한다. </div><div><div style="text-align: center;"><div>Q*(s, a) = E[r + γ max α' Q*(s', a')]</div><div>이때, </div><div>s = state</div><div>a = action</div><div>r = 보상</div><div>Q = reward 함수</div><div>Q* = 최적 reward</div><div>γ = 현재 action을 취했을 때, 다음 보상의 할인율. 현재 보상 비율을 조정하는 역할</div><div>s' = 다음 상태</div><div>a' = 다음 액션</div><div>E = 기대되는 보상</div><div><br /></div></div><div>Q-Learning의 기본 개념은 벨만 방정식(<a href="https://en.wikipedia.org/wiki/Bellman_equation">Bellman optimality</a>)을 사용한다. Q-Learning은 벨만 방정식을 사용해 총보상값을 과거 보상값을 고려해, 재귀 반복 업데이트하며 최적해를 찾아간다(참고 - <a href="https://www.cs.toronto.edu/%7Evmnih/docs/dqn.pdf">DQN 논문</a>. Q-Learning 동작 메커니즘은 아래 3번 글을 참고). DQN 논문에서 실험한 결과, 이를 통해, 특정 상태에서 시작한 Qi는 최적 총보상인 Q*로 수렴한다는 것을 확인했다. </div><div><br /></div></div></div><div style="text-align: left;"><div>전체 Q-Learning 알고리즘은 다음과 같다. </div><div><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhYcWvNUvXidkW6LiaRvdhYTl9VviKkv2qRjtqye6ycYJekd9HolhlSIRPWWU44oO8lF0tBNzV5XTCLZNX5-gD2tafjC8nNFqa-ZAQxHP_9xoOf-Wf58uxUpH4p_OgnYIA25XDSpMLu9RYkQ8tWsuKLueqmNm6lpydWCJaJ7BCbNUHL-N2VzmJJ0PSB2w13" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="344" data-original-width="677" height="283" src="https://blogger.googleusercontent.com/img/a/AVvXsEhYcWvNUvXidkW6LiaRvdhYTl9VviKkv2qRjtqye6ycYJekd9HolhlSIRPWWU44oO8lF0tBNzV5XTCLZNX5-gD2tafjC8nNFqa-ZAQxHP_9xoOf-Wf58uxUpH4p_OgnYIA25XDSpMLu9RYkQ8tWsuKLueqmNm6lpydWCJaJ7BCbNUHL-N2VzmJJ0PSB2w13=w555-h283" width="555" /></a></div><div class="separator" style="clear: both;">DQN 알고리즘(<a href="https://draft.blogger.com/blog/post/edit/5201956450461596914/32275534319264752#">DeepMind</a>, 2015)</div></div></div></div><div style="text-align: left;"><br /></div><div style="text-align: left;"><div>대부분의 문제에서 Q-function을 s, a 각 조합의 표로 나타내는 것은 비현실적이다. 대신, 매개변수를 가진 신경망같은 함수 근사기를 사용해, 훈련할 수 있다면, 효과적으로 이 문제를 풀 수 있다. 즉, Q(s, a; θ) ≈ Q*(s, a) 가 가능하다. 각 보상 학습 단계에서 다음 수식의 손실을 최소화하도록 설계한다.</div><div style="text-align: center;"><div>Loss함수 = Li(θi) = E * s, a, r, s' ~ ρ(.) [(yi - Q(s, a; θi))²]</div><div>이때, </div><div>yi = r + γ max α' Q(s', a'; θi-1)] </div><div><br /></div></div><div><div>여기서, ρ는 사전 수집된 s, a, r, s'에 대한 분포를 나타낸다. 이전 학습인 θi-1의 Q변수값들은 다음 학습을 위해 고정되어 업데이트되지 않는다. 그러므로, 이 값들은 메모리에 보관(스냅샷)해 두어야 한다. 이 스냅샷 사본을 대상 네트워크라 한다. </div><div><br /></div><div>이렇게 Loss 함수를 설계하면, loss 가 이전 보상과 차이가 적은 쪽으로 수렴하도록 동작된다. 결국 보상 정책 a = max α Q(s, a, θ)에 대해 계산하게 된다. 그리고, 이때, 지역해에 빠지지 않도록 랜덤 액션 선택 확률값 ϵ를 도입해, 1 - ϵ 인 greedy action과 ϵ 확률로 행동을 선택하도록 ϵ-greedy 정책(알고리즘)을 적용한다. </div></div><div><br /></div><div>DQN 비용함수의 수식 표현은 다음과 같다. </div><div><div><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhKpPBAhgbAf-UjATyKj1_mvp7jwyhUZLSKV9ereKPR9G3CRcSPerWvyUmUmWG9bX5A2RQxnRCCBjY6yfy4uaffo8nbp_q36I7jJPCHbZlsoEVpECp2urFCqZRuYB7INePLR3RmjepjBrixDAH3UrVoAtP-6eaQn7MKw7wrp5peYjkeEijlTD9hxWnnUpcU" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="75" data-original-width="688" height="41" src="https://blogger.googleusercontent.com/img/a/AVvXsEhKpPBAhgbAf-UjATyKj1_mvp7jwyhUZLSKV9ereKPR9G3CRcSPerWvyUmUmWG9bX5A2RQxnRCCBjY6yfy4uaffo8nbp_q36I7jJPCHbZlsoEVpECp2urFCqZRuYB7INePLR3RmjepjBrixDAH3UrVoAtP-6eaQn7MKw7wrp5peYjkeEijlTD9hxWnnUpcU=w372-h41" width="372" /></a></div></div><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both;">Deep Q Network cost function</div></div></div><div><br /></div></div><div>DQN에서 전체 보상값을 계산하지 않고, 일부 스냅샷만으로 최적해를 구하기 위해서는, 확률적 경사하강법을 사용한다. </div><div><br /></div><div>DQN을 통해 아타리(Atari) 게임을 강화학습으로 구현한 사례에서, 신경망 학습 가중치를 안정적으로 업데이트하기 위해, 경험 재현(Experience Replay) 기법이 적용되었다. 이는 각 학습 타임 단계(step)에서 재현 버퍼 메모리에 앞서 언급한 스냅샷 데이터를 추가한다. 이 버퍼는 원형 Queue 자료 구조를 가진다. 학습 시 배치 입력 데이터는 이 재현 버퍼에서 랜덤 샘플링된 데이터를 사용한다(미니 배치 학습 기법). 이를 통해, 안정적인 학습을 유도한다.</div><div><br /></div><div><a href="https://www.tensorflow.org/agents/tutorials/0_intro_rl?hl=ko">TF-Agents</a>와 같은 라이브러리는 이와 같은 딥러닝 강화학습에 필요한 함수와 객체를 제공한다. </div><div><br /></div><div><b>3. Q-Learning의 배경 개념인 마르코프 체인, 벨만 방정식 개발 역사</b></div><div>Q-Learning은 마르코프 체인(<a href="https://en.wikipedia.org/wiki/Markov_chain#:~:text=A%20Markov%20chain%20or%20Markov,the%20state%20of%20affairs%20now.%22">Markov chain</a>, 1906년) 개념에서 발전된 것이다. 수학자 <a href="https://en.wikipedia.org/wiki/Andrey_Markov">안드레이 마르코프</a>는 현재 상태로만 미래를 예측하는 과정을 연쇄적으로 연결함으로써 최적해를 구할 수 있도록 마르코프 연쇄 기법을 개발했다. 그는 강화학습에서 환경과 정책이라 불리는 시스템, 상태에 따른 확률적 전환(transition) 개념(강화학습에서 확률적으로 보상이 높은 액션을 선택하는 행위로 사용됨)을 관련 논문에서 제안하였다. 마르코프 체인은 상태 변화의 단계가 이산적으로 계산될 수 있도록 하였고, 그 단계는 시간, 물리적 거리 등 측정가능한 것이 될 수 있도록 수식으로 일반화하였다. </div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjumoRthbBJ8pXwD4kdj9m46XGTEJCP50buotYvKPFJjI2Uqbk9nojRQiXJ2v59mOL-g7oiAD0U0Olv9s-MdxQEBrDxxpskb42QzZO2qX9t27S7cSTSjO_qrNb2BOq2fC758LxsyMkDLFMWMJskIDl0f6o1UspfBig_va-FEv6SBMc7nDwisbQTZMx5V63H" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="149" data-original-width="733" height="88" src="https://blogger.googleusercontent.com/img/a/AVvXsEjumoRthbBJ8pXwD4kdj9m46XGTEJCP50buotYvKPFJjI2Uqbk9nojRQiXJ2v59mOL-g7oiAD0U0Olv9s-MdxQEBrDxxpskb42QzZO2qX9t27S7cSTSjO_qrNb2BOq2fC758LxsyMkDLFMWMJskIDl0f6o1UspfBig_va-FEv6SBMc7nDwisbQTZMx5V63H=w433-h88" width="433" /></a></div><div class="separator" style="clear: both; text-align: center;">마르코프 체인 수식(<a href="https://www.sciencedirect.com/science/article/pii/S0024379504000357">Gely P. Basharin</a>, 2004)</div><br /></div></div><div style="text-align: left;">처음 딥러닝을 사용하지 않았던 강화학습 기술은 에이전트가 최대 보상을 받는 방법은 이전 상태와 행동을 취한 현재상태의 보상만 관련된다고 가정하였다. 이를 통해, 게임의 전체 상태를 알지 않아도 된다고 가정한다. 그렇지 않으면, 무한대의 상태를 한정된 컴퓨터 메모리에서 계산할 수 있는 방법이 없다. </div><div style="text-align: left;"><br /></div><div style="text-align: left;">그러므로, 이런 가정에 잘 부합하는 것이 마르코프 체인이다. 마르코프 체인은 선택의 직전 상태와 이로 인한 결과만 고려한다(과거 데이터는 손실된다). 이를 이용해서, 다음 그림과 같은 문제를 해결할 수 있다. </div><div style="text-align: left;"><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEg6IxMFf_DCkug6QEn3461dcX-g9dpyV9Hr1YWtlm7Mr2p-R169K2JiVPaNg2KznCGs9HLhfH0Ahk3VEXepWFRNQSTsztcbSYtjjV-M9cN9FfhsY7MXaPqBgD5wBCVnI8HyfGTuqsQ-z3uGuqxmogGbAG1Lt-R_4FkfAa4l5tf_KIeEVPgLcdBopVFYYlAV" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="580" data-original-width="1452" height="128" src="https://blogger.googleusercontent.com/img/a/AVvXsEg6IxMFf_DCkug6QEn3461dcX-g9dpyV9Hr1YWtlm7Mr2p-R169K2JiVPaNg2KznCGs9HLhfH0Ahk3VEXepWFRNQSTsztcbSYtjjV-M9cN9FfhsY7MXaPqBgD5wBCVnI8HyfGTuqsQ-z3uGuqxmogGbAG1Lt-R_4FkfAa4l5tf_KIeEVPgLcdBopVFYYlAV" width="320" /></a></div>Markov Chain 예시</div><div class="separator" style="clear: both; text-align: center;"><br /></div></div><div style="text-align: left;">다만, 좀 더 먼 과거와 많은 상태들을 고려해야 하는 문제는 탐색할 해와 계산량이 무한히 증가하는 <a href="https://en.wikipedia.org/wiki/Curse_of_dimensionality">차원의 저주</a>가 발생한다. 이를 해결하기 위해, 리처드 벨만이 개발한 벨만(<a href="https://en.wikipedia.org/wiki/Bellman_equation">Bellman</a>) 방정식이 사용되었다. 이 방정식은 재귀적 특성을 가지고 있어, 과거 경험을 누적하도록 정의되어 있다. 벨만 방정식 개발 목적이 동적 프로그래밍 문제 해결(예. 물류 최적 이동)에 필요한 최적해 탐색까지 이동시간, 비용 최소화를 고려해 설계되었으므로, 벨만 방정식은 누적 보상이 제일 큰 상태를 선택할 수 있다. </div><div style="text-align: left;"><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhGx08delXPxNMtjDPf6KQS7CZyNNEKjzsNJfKuxGvO7eVUy5ybKHeoueJJOU8VrnjazgpMBp57Pc1y4lE2zgWgdhMsNPzVnQz6BRXN-94Q1qEF8SnrzGelVb0Q3TmgvmRbMcS8V63usZ0nlMur9qP5tcaQlsiiAMXX2Zp8NQ-_eIe7xxrRQvhxfiTia5W8" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="120" data-original-width="1100" height="54" src="https://blogger.googleusercontent.com/img/a/AVvXsEhGx08delXPxNMtjDPf6KQS7CZyNNEKjzsNJfKuxGvO7eVUy5ybKHeoueJJOU8VrnjazgpMBp57Pc1y4lE2zgWgdhMsNPzVnQz6BRXN-94Q1qEF8SnrzGelVb0Q3TmgvmRbMcS8V63usZ0nlMur9qP5tcaQlsiiAMXX2Zp8NQ-_eIe7xxrRQvhxfiTia5W8=w496-h54" width="496" /></a></div><div class="separator" style="clear: both;">재귀적 성질을 가진 벨만 방정식(*주. 현재와 미래 함수간의 관계를 통해 최적 보상 정책을 제공할 수 있음) </div><div class="separator" style="clear: both;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjjKZvSzc8_uS6soGAoKWDM3JvdNvReXvBMQ-69wG-D8mc8k8ab1qYn0QzFSxw1EciN5wjRMKGZvWua22MyCpaob3zxVs9IXajKOjiCjSDuIRco9nSDS-ZwGGs-0e2K2Xbw9Qmj4adabNSfDHZFpoRTSvx2c20Ibf17ypO8svpGhBiCeot7Hyke-hAc1PEq" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="320" data-original-width="256" height="155" src="https://blogger.googleusercontent.com/img/a/AVvXsEjjKZvSzc8_uS6soGAoKWDM3JvdNvReXvBMQ-69wG-D8mc8k8ab1qYn0QzFSxw1EciN5wjRMKGZvWua22MyCpaob3zxVs9IXajKOjiCjSDuIRco9nSDS-ZwGGs-0e2K2Xbw9Qmj4adabNSfDHZFpoRTSvx2c20Ibf17ypO8svpGhBiCeot7Hyke-hAc1PEq=w124-h155" width="124" /></a><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgZoR-Bss6uDGHYHqmH62x6hZtwqExSTWTaBMtaL_LcICRvZNPnpI9npxGlOJIBsYnXqdkpdJIznAwCplpRdp428l3K4wBNUmvzu5JAkupz2zMmScD1g3XGsdiZiA0IGGrCsIJNrLZCKSDv3SheB4vIYaKzn5d5R07140lHC4U_XOc3kfeqxVp9NVtQRINZ" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="247" data-original-width="665" height="151" src="https://blogger.googleusercontent.com/img/a/AVvXsEgZoR-Bss6uDGHYHqmH62x6hZtwqExSTWTaBMtaL_LcICRvZNPnpI9npxGlOJIBsYnXqdkpdJIznAwCplpRdp428l3K4wBNUmvzu5JAkupz2zMmScD1g3XGsdiZiA0IGGrCsIJNrLZCKSDv3SheB4vIYaKzn5d5R07140lHC4U_XOc3kfeqxVp9NVtQRINZ=w406-h151" width="406" /></a><br /></div></div><div class="separator" style="clear: both;"><a href="https://en.wikipedia.org/wiki/Richard_E._Bellman">리처드 E. 벨만</a>(<a href="https://www.ncbi.nlm.nih.gov/pmc/articles/PMC1063639/?page=1">동적 프로그래밍</a> 기술 개발자. 1920-1984)</div><div class="separator" style="clear: both;"><br /></div></div><div class="separator" style="clear: both; text-align: center;"></div></div><div style="text-align: left;"><div>이 벨만 방정식을 이용한 강화학습 방법이 Q-Learning이다. </div><div><br /></div><div><div>Q-Learning 강화학습 알고리즘은 상태, 액션의 집합으로 Q-Table을 메모리에 생성한다. 탐색해 공간에서 에이전트 시작과 목표를 알고 있다면, 보상값을 계산하는 Q(s, a)함수를 정의할 수 있고, 다음 그림과 같이 표에 Q값을 계산할 수 있다.</div><div><br /></div><div><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgA5tUoJq_dHWDobCllZBuiBimqTZ2WtsLtIKf2XZhRX6BcGBsty-rQHg2UtfW3kkAz3BSaAtYWlJZRG-0RQ2rQ5fENw0GzH04-0Bf9M4ud8sJ-DmOF2iQdysqY9i3LFGXeh4cwKw0bH0vBUGb9ITQ2vpzCL4HZkjlKA0MCfWm8_ZPRZybHqqlJMW5qvQZP/s1159/3.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="477" data-original-width="1159" height="178" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgA5tUoJq_dHWDobCllZBuiBimqTZ2WtsLtIKf2XZhRX6BcGBsty-rQHg2UtfW3kkAz3BSaAtYWlJZRG-0RQ2rQ5fENw0GzH04-0Bf9M4ud8sJ-DmOF2iQdysqY9i3LFGXeh4cwKw0bH0vBUGb9ITQ2vpzCL4HZkjlKA0MCfWm8_ZPRZybHqqlJMW5qvQZP/w433-h178/3.png" width="433" /></a></div></div><div class="separator" style="clear: both; text-align: center;">Q Table 예시</div><div class="separator" style="clear: both; text-align: center;"></div></div><div><br /></div><div>이 그림에서는 에이전트(자동차)가 목적지를 도달하는 액션을 선택할 때 Q(s, a)함수값이 1.0으로 제일 크게 하는 것을 알 수 있다(그림 표에서 4행 3열). Q함수값을 미리 계산할 수 있다면, 에이전트는 보상이 큰 쪽으로 따라가면, 자동적으로 목표에 도달할 수 있다. Q-Table이 액션이 탐색하는 경로를 보여준다는 것을 알 수 있다. </div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjQsc1I1UbXrx7kk92pxar_Oe9THC-qSvFaebQ2FrWT8uuhYLblkX3UUPAMA0wGmBIcDkrY2NBIDk0-9KSSfEmHjfvaUD9Z33nrdnCbEHWl8hZX9a5-cBlSksnvXEAhcK_d_8wzIkRLQcxPEFb70NjAtz4lG_OmNFPJGry0CjK6Yjo0ZWznhWwLvjuFKQ0D" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="477" data-original-width="1159" height="132" src="https://blogger.googleusercontent.com/img/a/AVvXsEjQsc1I1UbXrx7kk92pxar_Oe9THC-qSvFaebQ2FrWT8uuhYLblkX3UUPAMA0wGmBIcDkrY2NBIDk0-9KSSfEmHjfvaUD9Z33nrdnCbEHWl8hZX9a5-cBlSksnvXEAhcK_d_8wzIkRLQcxPEFb70NjAtz4lG_OmNFPJGry0CjK6Yjo0ZWznhWwLvjuFKQ0D" width="320" /></a></div><br /></div><div>이를 바탕으로 한, Q-Learning 알고리즘은 여러 에피소드 실험을 반복해, 정책에 따라 생성된 데이터를 입력받아, 최적 해를 탐색한다. 지역해에 빠지지 않도록 e-greedy 방법을 이용해 임의의 탐색경로를 선택한다. 초기 Q 함수값은 램덤값으로 균등하게 분포되어 있다고 가정한다. 각 상태가 단계마다 업데이트될 때 보상값도 갱신될 수 있다. 보상값은 해탐색 공간에서 현재지점과 목표지점 간 거리 등을 사용할 수 있다.</div><div><br /></div><div>Q-Learning은 그 자체만으로도 효과적인 학습이 가능하여, 자율주행, 게임, 로봇제어, 재고관리 등에 사용되고 있다. 다만, 이는 다음 같은 한계가 있다. </div><div><ul><li>전체 탐색에 따른 보상을 미리 알고 있을 때 한해 탐색이 가능하다.</li><li>선택할 수 있는 상태가 무한대일 경우, Q Table이 메모리 계산 범위를 넘어간다(차원의 저주). </li></ul></div><div>현실의 문제에서는 선택할 수 있는 상태가 무한히 많은 경우(예. 주식 트레이딩 등)가 일반적이다. 이 문제를 해결하고자, 에이전트가 처한 상태 s의 특징(예. 게임에서 플레이어와 먹이 혹은 몬스터와의 거리)을 계산하고, 이를 통해, 상태 s를 일반화하여, 각 특징의 가중치를 곱하는 Approximate Q-Learning을 사용할 수 있다. 하지만, 이 또한 차원의 저주에서는 자유롭지 않아, DQN 알고리즘이 개발된 것이다. </div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEiW84A80nOei8hN36iw8ZJhX33V4WM6sV9RrPww2wYacOeVSt97tBy_mxAhSMHRBaKeuzXRX7ZXhgkeP-xF3OeyON1adMuwVch7iZIkO_swSO0S14hj1kbdgUh0RfyjtpGhmIARSbgD8NcJwRNY9g2NWKykeDZYIO6LSf0KurQAjGJJhxRA8ekDj-gK3P9U" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="895" data-original-width="1565" height="229" src="https://blogger.googleusercontent.com/img/a/AVvXsEiW84A80nOei8hN36iw8ZJhX33V4WM6sV9RrPww2wYacOeVSt97tBy_mxAhSMHRBaKeuzXRX7ZXhgkeP-xF3OeyON1adMuwVch7iZIkO_swSO0S14hj1kbdgUh0RfyjtpGhmIARSbgD8NcJwRNY9g2NWKykeDZYIO6LSf0KurQAjGJJhxRA8ekDj-gK3P9U=w400-h229" width="400" /></a></div><div class="separator" style="clear: both; text-align: center;">신경망 기반 강화학습 네트워크 예시(<a href="https://www.nature.com/articles/nature14236">Demis Hassabis</a>, 2015, Nature)</div><div class="separator" style="clear: both; text-align: center;"><br /></div></div></div></div><div style="text-align: left;"><div><b>4. 강화학습 알고리즘의 분류</b></div><div>앞서 설명한 DQN은 강화학습 알고리즘 중 하나이다. 강화학습 알고리즘은 정책 최적화를 통해, 액션의 보상을 크게 만드는 것이 목적이다. 강화학습 알고리즘은 크게 Policy Optimization과 Q-Learning 계열로 나뉜다. </div><div><ul style="text-align: left;"><li></li><li>Poicy optimization: 이 방법은 policy π의 parameter θ를 이용해, πθ(a|s) 함수를 정의하고, θ를 학습시킨다. 이를 위해, 비용함수 J(πθ)의 θ에 대한 기술기를 계산한다. 이를 위해서는 gradient ascent를 통해 ∇J(πθ)를 계산해야 한다. 이 알고리즘 계열은 A2C(Advantage Actor Critic), A3C, PPO 등이 있다. </li><li>Q-learning: 앞서 언급한 최적해 Q*(s,a)를 구하기 위해, 근사해 Qθ(s,a)를 구한다. 이를 위해, 벨만 방정식을 사용한다. 이 방식은 최적화 과정에서 정책 파라메터인 θ를 직접 사용하지 않고, 관찰시점의 샘플데이터를 학습용으로 사용한다. 그러므로, off-policy라 불린다. 이 계열 알고리즘은 DQN, C51등이 있다. </li></ul></div></div><div style="text-align: left;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEiF1s1Fv87sgRdYad8Q8YIgo2uObDxMCa4q9TJmsq_-ojnNlfWfn494I2ynj6C77jV67GsKOxWf6VkUhATnvN8pnO02QZDMg-Hmrz5gi1pW8kZKpNyxxpd3gIMc10k_s-0mDRIViedwb_gkXe0-rF3w0YBUz8CTKbUlYvE7S3KcJRMrj4M3WQ2uTAFKTz8I" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="1008" data-original-width="1978" height="204" src="https://blogger.googleusercontent.com/img/a/AVvXsEiF1s1Fv87sgRdYad8Q8YIgo2uObDxMCa4q9TJmsq_-ojnNlfWfn494I2ynj6C77jV67GsKOxWf6VkUhATnvN8pnO02QZDMg-Hmrz5gi1pW8kZKpNyxxpd3gIMc10k_s-0mDRIViedwb_gkXe0-rF3w0YBUz8CTKbUlYvE7S3KcJRMrj4M3WQ2uTAFKTz8I=w400-h204" width="400" /></a></div><div class="separator" style="clear: both; text-align: center;">RL 알고리즘 계열</div><br /></div><div style="text-align: left;">좀 더 상세한 강화학습의 기본개념은 아래 글을 참고한다.</div><div style="text-align: left;"><ul style="text-align: left;"><li><a href="https://www.tensorflow.org/agents/tutorials/0_intro_rl?hl=ko">RL 및 Deep Q 네트워크 소개 | TensorFlow Agents</a></li><li><a href="https://www.cs.toronto.edu/%7Evmnih/docs/dqn.pdf">Playing Atari with Deep Reinforcement Learning</a>, DeepMind Technologies</li><li><a href="https://en.wikipedia.org/wiki/Markov_chain#:~:text=A%20Markov%20chain%20or%20Markov,the%20state%20of%20affairs%20now.%22">Markov chain</a></li><li><a href="https://towardsdatascience.com/qrash-course-deep-q-networks-from-the-ground-up-1bbda41d3677">Qrash Course: Reinforcement Learning 101 & Deep Q Networks in 10 Minutes | by Shaked Zychlinski | Towards Data Science</a></li><li><a href="https://wikidocs.net/165849">Q-Learning (Q-Table) - Deep Learning Bible - 5. Reinforcement Learning</a></li><li><a href="https://velog.io/@d2h10s/%EA%B0%95%ED%99%94%ED%95%99%EC%8A%B5-Q-Learning%EC%9D%98-%EA%B0%9C%EB%85%90-%EC%9D%B4%ED%95%B4">Q-Learning (velog.io</a>, <a href="https://spacebike.tistory.com/53">tistory.com, approximate)</a></li></ul><div><br /></div></div><div style="text-align: left;"><b><span style="font-size: medium;">강화학습 기반 CartPole 기본 예제 테스트해보기</span></b></div><div style="text-align: left;"><b>1. Keras-rl2 기반 CartPole 강화학습</b></div><div style="text-align: left;">강화학습 동작방식을 확인하기 위해 카트 위에 서 있는 막대를 시뮬레이션해 강화학습하는 예제를 실행해 보기로 한다. 이 기본예제를 통해 강화학습 방법을 좀 더 직관적으로 이해할 수 있다. </div><div style="text-align: left;"><br /></div><div style="text-align: left;">이를 위해 구글에서 개발된 <a href="https://github.com/taylormcnally/keras-rl2">keras-rl2</a> 라이브러리를 사용한다. 현재 최신 버전 강화학습 라이브러리 활용 예제는 다음 장을 참고하라. 우선, virtualenv로 가상환경을 만든 후, 이와 관련된 패키지를 설치한다.</div><div style="text-align: left;">pip install gym pygame tensorflow-gpu keras-rl2</div><div style="text-align: left;"><br /></div><div style="text-align: left;"><div>참고로, 현재 이 예제와 관련된 많은 코드는 <a href="https://github.com/taylormcnally/keras-rl2">keras-rl2</a> 라이브러리 기반으로 개발되어 있다. 이 라이브러리 개발은 2021년부터 중단되었다(참고. <a href="https://github.com/google/dopamine">dopamine</a>). 아울러, 해당 예제를 실행해 보면, 최신 버전에서, fit() shape 불일치, display 라이브러리 불일치 등 수많은 에러가 발생한다. 그러므로, 다음과 같이 0.1.1버전을 설치한다.</div><div>pip install tf-agents==0.1.1 </div></div><div style="text-align: left;"><br /></div><div style="text-align: left;">강화 학습의 기본 예제 테스트를 위해, gym라이브러리에서 미리 만들어 놓은 CartPole이란 막대기로 균형잡기 학습 환경을 준비해 본다. 이 예제는 조인트로 카트에 부착된 마찰없는 트랙을 따라 움직이는 막대기의 균형을 잡는 문제이다. </div><div style="text-align: left;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEicyLJII9uuQPJYkAQ_m3a9yAgVnHsgNk2mImvl-XYRF2Yt6Cuvho0P4M7h-PGNzW5KH06991H_LdLdH-JL35LdHJcat7eN3tToPlYi7jVwcTJNcAs7l_UdrcAbyn0EwbTM_tnnzj0J9-dLJ8OU-j6ekhkpvCrgpzPW3taHpXKkTTvOfIX-mZ_s2LuBgwwa" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="539" data-original-width="749" height="230" src="https://blogger.googleusercontent.com/img/a/AVvXsEicyLJII9uuQPJYkAQ_m3a9yAgVnHsgNk2mImvl-XYRF2Yt6Cuvho0P4M7h-PGNzW5KH06991H_LdLdH-JL35LdHJcat7eN3tToPlYi7jVwcTJNcAs7l_UdrcAbyn0EwbTM_tnnzj0J9-dLJ8OU-j6ekhkpvCrgpzPW3taHpXKkTTvOfIX-mZ_s2LuBgwwa" width="320" /></a></div><div class="separator" style="clear: both; text-align: center;"></div></div><div style="text-align: left;"><br /></div><div style="text-align: left;">이 환경의 관측치(Observation) 상태는 4개의 튜플을 가진다. 관측 값은 카트의 위치 및 속도, 막대의 각도 및 각속도를 나타내는 4D 벡터이다. 이 값은 딥러닝 기반 강화학습 시 입력 데이터로 사용될 것이다. </div><div style="text-align: left;">Observation = {Cart Position, Cart Velocity, Pole Angle, Pole Velopcity At Tip}</div><div style="text-align: left;"><br /></div><div style="text-align: left;">모든 관측치에는 0.05사이의 균일 랜던값이 할당된다. 이 문제에서는 평균 보상이 특정 값 이상일때 문제가 해결된 것으로 판단한다.</div><div style="text-align: left;"><br /></div><div style="text-align: left;"><div>에이전트는 두 가지 행동 중 하나를 취하여 시스템을 제어할 수 한다. 카트를 오른쪽 (+1) 또는 왼쪽 (-1)으로 움직인다. 막대가 똑바로 유지되는 모든 타임 스탭은 보상을 제공한다. 일부 각도 제한을 넘어가거나, 움직이는 레일 밖을 벗어나면 막대는 쓰러진다(<a href="https://www.tensorflow.org/agents/tutorials/0_intro_rl?hl=ko">참고</a>). 에이전트 목표는 보상 합이 최대가 되는 정책을 학습하는 것이다. </div></div><div style="text-align: left;"><br /></div><div style="text-align: left;">다음 코드를 이용해 간단한 강화학습모델로 테스트를 수행한다. </div><div style="text-align: left;"><span style="color: #274e13;">import random</span></div><div style="text-align: left;"><div><span style="color: #274e13;">import gym</span></div><div><span style="color: #274e13;"><br /></span></div><div><span style="color: #274e13;">env = gym.make('CartPole-v1', render_mode='human') </span></div><div><span style="color: #274e13;"><br /></span></div><div><span style="color: #274e13;">episodes = 10</span></div><div><span style="color: #274e13;">for episodes in range(1, episodes+1):</span></div><div><span style="color: #274e13;"> state = env.reset()</span></div><div><span style="color: #274e13;"> done = False</span></div><div><span style="color: #274e13;"> score = 0</span></div><div><span style="color: #274e13;"><br /></span></div><div><span style="color: #274e13;"> while not done:</span></div><div><span style="color: #274e13;"> action = random.choice([0, 1])</span></div><div><span style="color: #274e13;"> n_state, reward, done, flag, info = env.step(action)</span></div><div><span style="color: #274e13;"> score += reward</span></div><div><span style="color: #274e13;"> env.render()</span></div><div><span style="color: #274e13;"> </span></div><div><span style="color: #274e13;"> print('Episode:{} Score:{}'.format(episodes, score))</span></div><div><span style="color: #274e13;"><br /></span></div><div><span style="color: #274e13;">env.close()</span></div></div><div style="text-align: left;"><br /></div><div style="text-align: left;">실행 결과는 다음과 같다. </div><div style="text-align: left;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhKlwArjGQe3ndN1wLIvjf-DknKvbG5I9HqcrOV-EZYr6HVbwlXmHWFkOxyRiJmVe6H7PzGw5MUSD-AsdJfc6rfozm0ZLEmwOElgYUwQIfGI-LmTATeLtLEOvkO7PX0L2FMkNzqQtH2RVv4rxR6q7qoqYxloCqYuDOArkk3zhr5KtutwFx-DaEQAi9ZpfVg" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="185" data-original-width="514" height="115" src="https://blogger.googleusercontent.com/img/a/AVvXsEhKlwArjGQe3ndN1wLIvjf-DknKvbG5I9HqcrOV-EZYr6HVbwlXmHWFkOxyRiJmVe6H7PzGw5MUSD-AsdJfc6rfozm0ZLEmwOElgYUwQIfGI-LmTATeLtLEOvkO7PX0L2FMkNzqQtH2RVv4rxR6q7qoqYxloCqYuDOArkk3zhr5KtutwFx-DaEQAi9ZpfVg" width="320" /></a></div><div style="text-align: left;"><br /></div>결과와 같이 학습 Score가 그리 높지 않다. 이제 이 환경을 이용해 DQN 딥러닝 모델을 만들고 학습해 보자.</div><div style="text-align: left;"><br /></div><div style="text-align: left;"><b>2. CartPole 딥러닝 강화학습 모델 개발<br /></b></div><div style="text-align: left;">앞의 강화학습 환경의 입력, 출력, 보상을 이용해, 딥러닝 학습 모델을 만들고, 학습해 본다. 강화학습을 사용하지 않으면, 상태 x 액션 차원의 Q 테이블을 정의해야 한다. 이는 메모리에서 계산 범위를 넘어간다. 그러므로, DQN 기반 강화학습 기법을 이용한다. </div><div style="text-align: left;"><br /></div><div style="text-align: left;">다음 코드를 입력한다. </div><div style="text-align: left;"><br /></div><div style="text-align: left;"><div><span style="color: #274e13;">import random, gym, torch</span></div><div><span style="color: #274e13;">import numpy as np</span></div><div><span style="color: #274e13;"><br /></span></div><div><span style="color: #274e13;">from tensorflow.keras.models import Sequential</span></div><div><span style="color: #274e13;">from tensorflow.keras.layers import Dense, Flatten</span></div><div><span style="color: #274e13;">from tensorflow.keras.optimizers import Adam</span></div><div><span style="color: #274e13;">from rl.agents import DQNAgent</span></div><div><span style="color: #274e13;">from rl.policy import BoltzmannQPolicy</span></div><div><span style="color: #274e13;">from rl.memory import SequentialMemory</span></div><div><span style="color: #274e13;"><br /></span></div><div><span style="color: #274e13;">env = gym.make('CartPole-v1') # , render_mode='human')</span></div><div><span style="color: #274e13;"><br /></span></div><div><span style="color: #274e13;">states = env.observation_space.shape[0]</span></div><div><span style="color: #274e13;">actions = env.action_space.n</span></div><div><span style="color: #274e13;">print('States {}, Actions {}'.format(states, actions))</span></div><div><span style="color: #274e13;"><br /></span></div><div><span style="color: #274e13;">model = Sequential()</span></div><div><span style="color: #274e13;">model.add(Flatten(input_shape=(1, states)))</span></div><div><span style="color: #274e13;">model.add(Dense(24, activation='relu'))</span></div><div><span style="color: #274e13;">model.add(Dense(24, activation='relu'))</span></div><div><span style="color: #274e13;">model.add(Dense(actions, activation='linear'))</span></div><div><span style="color: #274e13;"><br /></span></div><div><span style="color: #274e13;">agent = DQNAgent(</span></div><div><span style="color: #274e13;"> model=model,</span></div><div><span style="color: #274e13;"> memory=SequentialMemory(limit=50000, window_length=1),</span></div><div><span style="color: #274e13;"> policy=BoltzmannQPolicy(),</span></div><div><span style="color: #274e13;"> nb_actions=actions,</span></div><div><span style="color: #274e13;"> nb_steps_warmup=10,</span></div><div><span style="color: #274e13;"> target_model_update=1e-2</span></div><div><span style="color: #274e13;">)</span></div><div><span style="color: #274e13;"><br /></span></div><div><span style="color: #274e13;">agent.compile(Adam(lr=1e-3), metrics=['mae'])</span></div><div><span style="color: #274e13;">agent.fit(env, nb_steps=50000, visualize=False, verbose=1)</span></div><div><span style="color: #274e13;"><br /></span></div><div><span style="color: #274e13;">results = agent.test(env, nb_episodes=10, visualize=True)</span></div><div><span style="color: #274e13;">print(np.mean(results.history['episode_reward']))</span></div><div><span style="color: #274e13;"><br /></span></div><div><span style="color: #274e13;">env.close()</span></div><div><br /></div><div>실행하면, 다음과 같이 fit() 호출 후 학습을 시작한다.</div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjOXGSd6QQAZ38An-MqiQQVm6Qrif2QDhZuIKoNpYNGo3VqhY8IBTTgM-cRMSGdUdRcqeF-OIrMDPiLx-vOV8Iq-LdOJ_lNYvElh5hlEJR4Z7Pk0k-oi8YDEBzf1q7HIukvIr2yobK_P9G1fP_z-h9tjclDhYb73trfwpJ6iOXU9KPPOlF0J3fGDhUtowET" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="241" data-original-width="908" height="106" src="https://blogger.googleusercontent.com/img/a/AVvXsEjOXGSd6QQAZ38An-MqiQQVm6Qrif2QDhZuIKoNpYNGo3VqhY8IBTTgM-cRMSGdUdRcqeF-OIrMDPiLx-vOV8Iq-LdOJ_lNYvElh5hlEJR4Z7Pk0k-oi8YDEBzf1q7HIukvIr2yobK_P9G1fP_z-h9tjclDhYb73trfwpJ6iOXU9KPPOlF0J3fGDhUtowET=w400-h106" width="400" /></a></div><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjLmNi15lg_gpxjLBWRAXnIPr_R72G26ZXPgLzIILnF14m4Uzm9Frwbfz8Jx5h2GFEs6b2IwegPWaNuftLXGNyS5YGGidCsnsqyATR9bXeYc36ZT7AiwGhD_2U6wizU5k9MJxpQP7-1MfQghF-WxQuZc9wghQ-h-rTtQjwah-T08ddMVpStqIFXY8v8nfR-" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="404" data-original-width="724" height="225" src="https://blogger.googleusercontent.com/img/a/AVvXsEjLmNi15lg_gpxjLBWRAXnIPr_R72G26ZXPgLzIILnF14m4Uzm9Frwbfz8Jx5h2GFEs6b2IwegPWaNuftLXGNyS5YGGidCsnsqyATR9bXeYc36ZT7AiwGhD_2U6wizU5k9MJxpQP7-1MfQghF-WxQuZc9wghQ-h-rTtQjwah-T08ddMVpStqIFXY8v8nfR-=w402-h225" width="402" /></a></div></div><div><br /></div><div>학습된 최종 스코어 결과는 다음과 같다.</div><div><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEiQa23veMMtTgB8XjkvtHld8wkt6bsKX5hikz2OsaxXM2xO6KLnov0pDSkU0m3f9pD8EP3ZF6IVbScWRButCtb4jXv2-cfxQatXM7Fchwyuwwhis1g5MwkPnJDZkUJ99roC1A5lXTBFIEuqXwlxeyteIX7hpHDGXpEQ-zCobIVeX0pn3_D0WGLNZaRy8lwX" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="443" data-original-width="741" height="191" src="https://blogger.googleusercontent.com/img/a/AVvXsEiQa23veMMtTgB8XjkvtHld8wkt6bsKX5hikz2OsaxXM2xO6KLnov0pDSkU0m3f9pD8EP3ZF6IVbScWRButCtb4jXv2-cfxQatXM7Fchwyuwwhis1g5MwkPnJDZkUJ99roC1A5lXTBFIEuqXwlxeyteIX7hpHDGXpEQ-zCobIVeX0pn3_D0WGLNZaRy8lwX" width="320" /></a></div></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEggojqG6RMEqB5hlwACelsvXyv-ZGdHRi3oWOcSI5JQt1K8j_z0WlAzqN8AhJj3s6uITDLwk-0dIHmgn1KfTxRZIGVFBp-omJuXtBzbQ9uTkBR7-O9jwyFN0v5y_UKWuGLfJodkmV5ALtRIJpq2axJpzsqnSJZ2xn0KHAlcYACei_SETqk7YqoQyLLBL2pt" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="219" data-original-width="368" height="190" src="https://blogger.googleusercontent.com/img/a/AVvXsEggojqG6RMEqB5hlwACelsvXyv-ZGdHRi3oWOcSI5JQt1K8j_z0WlAzqN8AhJj3s6uITDLwk-0dIHmgn1KfTxRZIGVFBp-omJuXtBzbQ9uTkBR7-O9jwyFN0v5y_UKWuGLfJodkmV5ALtRIJpq2axJpzsqnSJZ2xn0KHAlcYACei_SETqk7YqoQyLLBL2pt" width="320" /></a></div><br />학습된 데이터는 다음과 같이 저장한 후 사용하면 된다. </div><div><div><span style="color: #274e13;">model.save('trained_model_weights.h5') # agent.save_weights('trained_model_weights.h5')</span></div><div><span style="color: #274e13;">env.close()</span></div><div><span style="color: #274e13;"><br /></span></div><div><span style="color: #274e13;">from keras.models import load_model</span></div><div><span style="color: #274e13;">model = load_model('trained_model_weights.h5')</span></div><div><span style="color: #274e13;"><br /></span></div><div><span style="color: #274e13;"># predict</span></div><div><span style="color: #274e13;">new_state = np.array([[0.1, 0.2, 0.3, 0.4]]) # Replace with your actual state</span></div><div><span style="color: #274e13;">new_state = new_state.reshape(1,1,4)</span></div><div><span style="color: #274e13;">q_values = model.predict(new_state)</span></div><div><span style="color: #274e13;">predicted_action = np.argmax(q_values)</span></div><div><span style="color: #274e13;">print(f'q_value = {q_values}, predict = {predicted_action}') # predicted_action==0 then left, not then right</span></div></div><div><br /></div><div>실행 결과는 다음과 같다. 관찰 상태가 [0.1, 0.2, 0.3, 0.4] 이면, 액션이 1(우측 이동) 결과를 얻는 것을 확인할 수 있다. </div><div style="text-align: center;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEi7r1jZYB628gneUZaJMqgdA0I6ZyHYgGb-4bBTD9bxXJzY6Da2vGQyKEQR9xLc8mGX-UJS-f3fP8cNNEXIorAsbIFTUClNZnGiXDBG_lIjcno2TZIqLWa3bo0jUgRDpWmZqC1999uIi7adO4aECkg1GrjFMxzEpO-6ETAvb1mIqrJLJEzQXQZvWGzsHoW-" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="156" data-original-width="618" height="101" src="https://blogger.googleusercontent.com/img/a/AVvXsEi7r1jZYB628gneUZaJMqgdA0I6ZyHYgGb-4bBTD9bxXJzY6Da2vGQyKEQR9xLc8mGX-UJS-f3fP8cNNEXIorAsbIFTUClNZnGiXDBG_lIjcno2TZIqLWa3bo0jUgRDpWmZqC1999uIi7adO4aECkg1GrjFMxzEpO-6ETAvb1mIqrJLJEzQXQZvWGzsHoW-=w400-h101" width="400" /></a></div></div><div><br /></div><div><b>3. DQN 강화학습 에이전트 코드 개발</b></div><div>앞서 사용한 딥러닝 개발 방식을 좀 더 깊게 살펴보기 위해, 케라스를 이용해 DQN 강화학습 시 사용된 에이전트를 직접 코딩해 본다. </div><div><br /></div><div>이 예제에서는 딥러닝 모델을 어떻게 강화학습에 적용하는 지 확인할 수 있다. 앞서 수식에 있었던 대로, Q-Learning에서 얻은 상태 데이터들을 배치 학습으로 딥러닝 모델에 입력해 학습시키기 위해, 딥러닝 모델을 메인, 목표 네트워크 두개를 생성한다. 이 네트워크는 에피소드가 반복될 때마다, 가중치값을 학습된 모델과 동기화하여, 최적해를 탐색할 수 있도록 한다(<a href="https://towardsdatascience.com/applied-reinforcement-learning-iv-implementation-of-dqn-7a9cb2c12f97">참고</a>). 실제 코드는 다음과 같다.</div><div> </div><div><div><span style="color: #274e13;"><span style="font-size: x-small;">import json, </span><span style="font-size: small;">random, </span><span style="font-size: small;">time</span></span></div><div><span style="color: #274e13; font-size: x-small;">from collections import deque</span></div><div><span style="color: #274e13; font-size: x-small;"><br /></span></div><div><span style="color: #274e13; font-size: x-small;">import gym</span></div><div><span style="color: #274e13; font-size: x-small;">import numpy as np</span></div><div><span style="color: #274e13; font-size: x-small;">import matplotlib.pyplot as plt</span></div><div><span style="color: #274e13; font-size: x-small;"><br /></span></div><div><span style="color: #274e13; font-size: x-small;">from keras.layers import Dense</span></div><div><span style="color: #274e13; font-size: x-small;">from keras.models import Sequential</span></div><div><span style="color: #274e13; font-size: x-small;">from keras.optimizers import Adam</span></div><div><span style="color: #274e13;"><br /></span></div><div><span style="color: #274e13; font-size: x-small;">class DQNAgent:</span></div><div><span style="color: #274e13; font-size: x-small;"> def __init__(self, state_size, action_size):</span></div><div><span style="color: #274e13; font-size: x-small;"> self.state_size = state_size</span></div><div><span style="color: #274e13; font-size: x-small;"> self.action_size = action_size</span></div><div><span style="color: #274e13; font-size: x-small;"><br /></span></div><div><span style="color: #274e13; font-size: x-small;"> # 재생 메모리 버퍼 크기 설정. 큐 자료구조.</span></div><div><span style="color: #274e13; font-size: x-small;"> self.replay_buffer = deque(maxlen=40000)</span></div><div><span style="color: #274e13; font-size: x-small;"><br /></span></div><div><span style="color: #274e13; font-size: x-small;"> # 하이퍼파리메터 설정</span></div><div><span style="color: #274e13; font-size: x-small;"> self.gamma = 0.99</span></div><div><span style="color: #274e13; font-size: x-small;"> self.epsilon = 1.</span></div><div><span style="color: #274e13; font-size: x-small;"> self.epsilon_min = 0.01</span></div><div><span style="color: #274e13; font-size: x-small;"> self.epsilon_decay = 0.98</span></div><div><span style="color: #274e13; font-size: x-small;"> self.learning_rate = 0.001</span></div><div><span style="color: #274e13; font-size: x-small;"> self.update_rate = 10</span></div><div><span style="color: #274e13; font-size: x-small;"><br /></span></div><div><span style="color: #274e13; font-size: x-small;"> # 메인 및 목표 네트워크 생성</span></div><div><span style="color: #274e13; font-size: x-small;"> self.main_network = self.create_nn()</span></div><div><span style="color: #274e13; font-size: x-small;"> self.target_network = self.create_nn()</span></div><div><span style="color: #274e13; font-size: x-small;"><br /></span></div><div><span style="color: #274e13; font-size: x-small;"> # 메인 네트워크 가중치와 동일하게 목표 네트워크 가중치 초기화</span></div><div><span style="color: #274e13; font-size: x-small;"> self.target_network.set_weights(self.main_network.get_weights())</span></div><div><span style="color: #274e13; font-size: x-small;"><br /></span></div><div><span style="color: #274e13; font-size: x-small;"> def create_nn(self):</span></div><div><span style="color: #274e13; font-size: x-small;"> model = Sequential() # 3개 레이어를 가진 신경망 정의</span></div><div><span style="color: #274e13; font-size: x-small;"> model.add(Dense(32, activation='relu', input_dim=self.state_size))</span></div><div><span style="color: #274e13; font-size: x-small;"> model.add(Dense(32, activation='relu'))</span></div><div><span style="color: #274e13; font-size: x-small;"> model.add(Dense(self.action_size, activation='linear'))</span></div><div><span style="color: #274e13; font-size: x-small;"> model.compile(loss='mse', optimizer=Adam(learning_rate=self.learning_rate))</span></div><div><span style="color: #274e13; font-size: x-small;"><br /></span></div><div><span style="color: #274e13; font-size: x-small;"> return model</span></div><div><span style="color: #274e13; font-size: x-small;"><br /></span></div><div><span style="color: #274e13; font-size: x-small;"> def update_target_network(self):</span></div><div><span style="color: #274e13; font-size: x-small;"> # 메인 및 목표 네트워크 가중치 동일하게 설정</span></div><div><span style="color: #274e13; font-size: x-small;"> self.target_network.set_weights(self.main_network.get_weights())</span></div><div><span style="color: #274e13; font-size: x-small;"><br /></span></div><div><span style="color: #274e13; font-size: x-small;"> def save_experience(self, state, action, reward, next_state, terminal):</span></div><div><span style="color: #274e13; font-size: small;"> # 재생 메모리에 경험 데이터를 추가함</span></div><div><span style="color: #274e13; font-size: x-small;"> self.replay_buffer.append((state, action, reward, next_state, terminal))</span></div><div><span style="color: #274e13; font-size: x-small;"><br /></span></div><div><span style="color: #274e13; font-size: x-small;"> def sample_experience_batch(self, batch_size):</span></div><div><span style="color: #274e13; font-size: x-small;"> # 재생 메모리에서 경험 데이터들 샘플링. 배치크기만큼 획득</span></div><div><span style="color: #274e13; font-size: x-small;"> exp_batch = random.sample(self.replay_buffer, batch_size)</span></div><div><span style="color: #274e13; font-size: x-small;"><br /></span></div><div><span style="color: #274e13; font-size: x-small;"> # s, a, r, s', 종료 정보에 대한 샘플링 데이터 획득</span></div><div><span style="color: #274e13; font-size: x-small;"> state_batch = np.array([batch[0] for batch in exp_batch]).reshape(batch_size, self.state_size)</span></div><div><span style="color: #274e13; font-size: x-small;"> action_batch = np.array([batch[1] for batch in exp_batch])</span></div><div><span style="color: #274e13; font-size: x-small;"> reward_batch = [batch[2] for batch in exp_batch]</span></div><div><span style="color: #274e13; font-size: x-small;"> next_state_batch = np.array([batch[3] for batch in exp_batch]).reshape(batch_size, self.state_size)</span></div><div><span style="color: #274e13; font-size: x-small;"> terminal_batch = [batch[4] for batch in exp_batch]</span></div><div><span style="color: #274e13; font-size: x-small;"><br /></span></div><div><span style="color: #274e13; font-size: x-small;"> # 생성된 학습용 배치 데이터셋을 튜플 형태로 리턴</span></div><div><span style="color: #274e13; font-size: x-small;"> return state_batch, action_batch, reward_batch, next_state_batch, terminal_batch</span></div><div><span style="color: #274e13; font-size: x-small;"><br /></span></div><div><span style="color: #274e13; font-size: x-small;"> def pick_epsilon_greedy_action(self, state):</span></div><div><span style="color: #274e13; font-size: x-small;"> # 확률 ε 로 액션 선택</span></div><div><span style="color: #274e13; font-size: x-small;"> if random.uniform(0, 1) < self.epsilon:</span></div><div><span style="color: #274e13; font-size: x-small;"> return np.random.randint(self.action_size)</span></div><div><span style="color: #274e13; font-size: x-small;"><br /></span></div><div><span style="color: #274e13; font-size: x-small;"> # 메인 네트워크에서 주어진 s로 액션 선택</span></div><div><span style="color: #274e13; font-size: x-small;"> state = state.reshape((1, self.state_size)) # 배치 차원 형태로 텐서 차원 변환</span></div><div><span style="color: #274e13; font-size: x-small;"> q_values = self.main_network.predict(state, verbose=0)</span></div><div><span style="color: #274e13; font-size: x-small;"> return np.argmax(q_values[0]) # 최대 보상값을 얻는 액션 인덱스 획득</span></div><div><span style="color: #274e13; font-size: x-small;"><br /></span></div><div><span style="color: #274e13; font-size: x-small;"> def train(self, batch_size):</span></div><div><span style="color: #274e13; font-size: x-small;"> # 경험 데이터에서 배치 데이터 샘플링 </span></div><div><span style="color: #274e13; font-size: x-small;"> state_batch, action_batch, reward_batch, next_state_batch, terminal_batch = self.sample_experience_batch(batch_size)</span></div><div><span style="color: #274e13; font-size: x-small;"><br /></span></div><div><span style="color: #274e13; font-size: x-small;"> # 목표(미래) 네트워크에서 최대 Q 보상값을 가진 액션 획득</span></div><div><span style="color: #274e13; font-size: x-small;"> next_q = self.target_network.predict(next_state_batch, verbose=0)</span></div><div><span style="color: #274e13; font-size: x-small;"> max_next_q = np.amax(next_q, axis=1)</span></div><div><span style="color: #274e13; font-size: x-small;"> # 메인(현재) 네트워크에서 보상값 획득</span></div><div><span style="color: #274e13; font-size: x-small;"> q_values = self.main_network.predict(state_batch, verbose=0)</span></div><div><span style="color: #274e13; font-size: x-small;"><br /></span></div><div><span style="color: #274e13; font-size: x-small;"> # 현재 액션의 보상값은 미래 보상값의 감마(감쇠율)을 곱해 할당함</span></div><div><span style="color: #274e13; font-size: x-small;"> for i in range(batch_size):</span></div><div><span style="color: #274e13; font-size: x-small;"> q_values[i][action_batch[i]] = reward_batch[i] if terminal_batch[i] else reward_batch[i] + self.gamma * max_next_q[i]</span></div><div><span style="color: #274e13; font-size: x-small;"><br /></span></div><div><span style="color: #274e13; font-size: x-small;"> # 과거 보상치와 현재 보상치의 loss가 없는 방향으로 메인(현재) 네트워크 학습시킴. </span></div><div><span style="color: #274e13; font-size: x-small;"> self.main_network.fit(state_batch, q_values, verbose=0)</span></div><div><span style="color: #274e13;"><br /></span></div><div><span style="color: #274e13; font-size: x-small;">if __name__ == '__main__':</span></div><div><span style="color: #274e13; font-size: x-small;"> # CartPole 환경 설정</span></div><div><span style="color: #274e13; font-size: x-small;"> env = gym.make("CartPole-v1")</span></div><div><span style="color: #274e13; font-size: x-small;"> state = env.reset()</span></div><div><span style="color: #274e13; font-size: x-small;"><br /></span></div><div><span style="color: #274e13; font-size: x-small;"> # 환경의 상태, 액션 크기 설정</span></div><div><span style="color: #274e13; font-size: x-small;"> state_size = env.observation_space.shape[0]</span></div><div><span style="color: #274e13; font-size: x-small;"> action_size = env.action_space.n</span></div><div><span style="color: #274e13; font-size: x-small;"><br /></span></div><div><span style="color: #274e13; font-size: x-small;"> # 에피소드, 에피소드 당 타임단계, 배치 크기 설정</span></div><div><span style="color: #274e13; font-size: x-small;"> num_episodes = 20 # 150</span></div><div><span style="color: #274e13; font-size: x-small;"> num_timesteps = 200 # 500</span></div><div><span style="color: #274e13; font-size: x-small;"> batch_size = 64</span></div><div><span style="color: #274e13; font-size: x-small;"> dqn_agent = DQNAgent(state_size, action_size)</span></div><div><span style="color: #274e13; font-size: x-small;"> time_step = 0 # 목표 네트워크 갱신에 사용되는 타임단계값 초기화</span></div><div><span style="color: #274e13; font-size: x-small;"> rewards, epsilon_values = list(), list() # 학습 후 출력위해 보상, 엠실론 값 리스트 초기화</span></div><div><span style="color: #274e13; font-size: x-small;"><br /></span></div><div><span style="color: #274e13; font-size: x-small;"> for ep in range(num_episodes):</span></div><div><span style="color: #274e13; font-size: x-small;"> tot_reward = 0</span></div><div><span style="color: #274e13; font-size: x-small;"> state = env.reset()</span></div><div><span style="color: #274e13; font-size: x-small;"><br /></span></div><div><span style="color: #274e13; font-size: x-small;"> print(f'\nTraining on EPISODE {ep+1} with epsilon {dqn_agent.epsilon}')</span></div><div><span style="color: #274e13; font-size: x-small;"> start = time.time()</span></div><div><span style="color: #274e13; font-size: x-small;"><br /></span></div><div><span style="color: #274e13; font-size: x-small;"> for t in range(num_timesteps):</span></div><div><span style="color: #274e13; font-size: x-small;"> time_step += 1</span></div><div><span style="color: #274e13; font-size: x-small;"><br /></span></div><div><span style="color: #274e13; font-size: x-small;"> # 타임스템마다 메인 네트워크 가중치로 목표 네트워크 갱신</span></div><div><span style="color: #274e13; font-size: x-small;"> if time_step % dqn_agent.update_rate == 0:</span></div><div><span style="color: #274e13; font-size: x-small;"> dqn_agent.update_target_network()</span></div><div><span style="color: #274e13; font-size: x-small;"><br /></span></div><div><span style="color: #274e13; font-size: x-small;"> action = dqn_agent.pick_epsilon_greedy_action(state) # ε-greedy policy으로 미래 액션 선택</span></div><div><span style="color: #274e13; font-size: x-small;"> next_state, reward, terminal, _ = env.step(action) # 환경에서 액션 실행</span></div><div><span style="color: #274e13; font-size: x-small;"> dqn_agent.save_experience(state, action, reward, next_state, terminal) # 재생 메모리에 경험 저장</span></div><div><span style="color: #274e13; font-size: x-small;"><br /></span></div><div><span style="color: #274e13; font-size: x-small;"> # 현재 상태를 다음 상태로 설정</span></div><div><span style="color: #274e13; font-size: x-small;"> state = next_state</span></div><div><span style="color: #274e13; font-size: x-small;"> tot_reward += reward</span></div><div><span style="color: #274e13; font-size: x-small;"><br /></span></div><div><span style="color: #274e13; font-size: x-small;"> if terminal: # 강화학습 종료 조건 만족 시 루프 종료</span></div><div><span style="color: #274e13; font-size: x-small;"> print('Episode: ', ep+1, ',' ' terminated with Reward ', tot_reward)</span></div><div><span style="color: #274e13; font-size: x-small;"> break</span></div><div><span style="color: #274e13; font-size: x-small;"><br /></span></div><div><span style="color: #274e13; font-size: x-small;"> # 재생 메모리가 가득차면, 딥러닝 모델 학습함</span></div><div><span style="color: #274e13; font-size: x-small;"> if len(dqn_agent.replay_buffer) > batch_size:</span></div><div><span style="color: #274e13; font-size: x-small;"> dqn_agent.train(batch_size)</span></div><div><span style="color: #274e13; font-size: x-small;"><br /></span></div><div><span style="color: #274e13; font-size: x-small;"> rewards.append(tot_reward)</span></div><div><span style="color: #274e13; font-size: x-small;"> epsilon_values.append(dqn_agent.epsilon)</span></div><div><span style="color: #274e13; font-size: x-small;"><br /></span></div><div><span style="color: #274e13; font-size: x-small;"> # 매 에피소드 종료 마다 엠실론 값을 감소시키로도록 갱신</span></div><div><span style="color: #274e13; font-size: x-small;"> if dqn_agent.epsilon > dqn_agent.epsilon_min:</span></div><div><span style="color: #274e13; font-size: x-small;"> dqn_agent.epsilon *= dqn_agent.epsilon_decay</span></div><div><span style="color: #274e13; font-size: x-small;"><br /></span></div><div><span style="color: #274e13; font-size: x-small;"> # 수행 결과 출력</span></div><div><span style="color: #274e13; font-size: x-small;"> elapsed = time.time() - start</span></div><div><span style="color: #274e13; font-size: x-small;"> print(f'Time elapsed during EPISODE {ep+1}: {elapsed} seconds = {round(elapsed/60, 3)} minutes')</span></div><div><span style="color: #274e13; font-size: x-small;"><br /></span></div><div><span style="color: #274e13; font-size: x-small;"> # 만약 최근 10개 에피소드 보상이 4990 보다 크면 종료</span></div><div><span style="color: #274e13; font-size: x-small;"> if sum(rewards[-10:]) > 4990:</span></div><div><span style="color: #274e13; font-size: x-small;"> print('Training stopped because agent has performed a perfect episode in the last 10 episodes')</span></div><div><span style="color: #274e13; font-size: x-small;"> break</span></div><div><span style="color: #274e13; font-size: x-small;"><br /></span></div><div><span style="color: #274e13; font-size: x-small;"> # plot rewards</span></div><div><span style="color: #274e13; font-size: x-small;"> plt.plot(rewards)</span></div><div><span style="color: #274e13; font-size: x-small;"> plt.xlabel('Episode')</span></div><div><span style="color: #274e13; font-size: x-small;"> plt.ylabel('Reward')</span></div><div><span style="color: #274e13; font-size: x-small;"> plt.title('Rewards of the training')</span></div><div><span style="color: #274e13; font-size: x-small;"> plt.show()</span></div><div><span style="color: #274e13; font-size: x-small;"><br /></span></div><div><div><span style="color: #274e13; font-size: x-small;"> # plot epsilon values</span></div><div><span style="color: #274e13; font-size: x-small;"> plt.plot(epsilon_values)</span></div><div><span style="color: #274e13; font-size: x-small;"> plt.xlabel('Episode')</span></div><div><span style="color: #274e13; font-size: x-small;"> plt.ylabel('Epsilon')</span></div><div><span style="color: #274e13; font-size: x-small;"> plt.title('Epsilon values of the training')</span></div><div><span style="color: #274e13; font-size: x-small;"> plt.show()</span></div></div><div><span style="color: #274e13; font-size: x-small;"><br /></span></div><div><span style="color: #274e13; font-size: x-small;"> # Save trained model. 앞의 예제와 같은 방식으로 저장된 학습 가중치를 로딩해 사용할 수 있도록 함</span></div><div><span style="color: #274e13; font-size: x-small;"> dqn_agent.main_network.save('trained_agent.h5')</span></div><div><span style="color: #274e13; font-size: x-small;"> print("Trained agent saved in 'trained_agent.h5'")</span></div></div><div><br /></div><div>수행 결과는 보상은 다음과 같다. 에피소드를 충분히 크게 설정하면, 전체 보상이 최적해를 탐색하는 수준으로 증가, 수렴하는 것을 확인할 수 있다. </div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhLUi0HfM-BlNIIccbaI6H2SizdmOfWJaJNcwewr3VqguQyNtgK1o20JWPFF0vlCAWff5uJuHZVEgFwOLSZx8_NClGZZ2X_8YEovo9-3mdFY0vNaB8MLBs7sq2Ei_ouj59e_jRIOKSPGJP_9vzBy2zWibvBUWqd2fhQjaJbYildrofbWbpzu243EvydFBxW" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="666" data-original-width="862" height="240" src="https://blogger.googleusercontent.com/img/a/AVvXsEhLUi0HfM-BlNIIccbaI6H2SizdmOfWJaJNcwewr3VqguQyNtgK1o20JWPFF0vlCAWff5uJuHZVEgFwOLSZx8_NClGZZ2X_8YEovo9-3mdFY0vNaB8MLBs7sq2Ei_ouj59e_jRIOKSPGJP_9vzBy2zWibvBUWqd2fhQjaJbYildrofbWbpzu243EvydFBxW" width="311" /></a><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhqVq8W7SPCmbLGoA7EHAB4a_3cvJKp4BBo0Uu1xAVtC8Gngir1rEueSqdybN-RT2DyoSyhqvJttHyua7RYXhWV3KXNZ9CHauY606A2SsYxiJnjtSxuc5oRdYkpAki81W0NrPBeKtvZ5pNtEwRJdRijZmTezx9cwjBl4U2Tek35oSZj2La0LQaTFoaOmDdY" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="663" data-original-width="855" height="240" src="https://blogger.googleusercontent.com/img/a/AVvXsEhqVq8W7SPCmbLGoA7EHAB4a_3cvJKp4BBo0Uu1xAVtC8Gngir1rEueSqdybN-RT2DyoSyhqvJttHyua7RYXhWV3KXNZ9CHauY606A2SsYxiJnjtSxuc5oRdYkpAki81W0NrPBeKtvZ5pNtEwRJdRijZmTezx9cwjBl4U2Tek35oSZj2La0LQaTFoaOmDdY" width="310" /></a><br /></div><div class="separator" style="clear: both; text-align: center;">보상 및 엡실론 감쇠 결과(에피소드=20)</div><div><br /></div></div><div><b><span style="font-size: medium;">Stable Baseline3 기반 주식 트레이딩 강화학습 코딩</span></b></div><div>좀 더 실용적인 문제에 강화학습을 적용하기 위해, 주식 트레이딩 봇을 만들어 본다. 이 예제는 주식 데이터를 관찰해, 거래 시 높은 보상을 받을 수 있도록 모델을 강화학습한다. </div><div><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiCoIHGam801a9ddeXNr0ojvHLK6n31XCGWjELoC3oeDMhntDaK1Lw-Mv3DdHMw0Y2q7vFGc8Jqy34PZtJPlt0wb_r5PB5puLJZj-0_SXLhr3o4FKlYKygwBm62R7lSX8sf_i8SkGoa2HAX2JOoZEqOM4rUj1D50B4OkjHsiKZlxxEZ4sfkGh1p9MzNh9Mu/s1670/f1.JPG" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="591" data-original-width="1670" height="158" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiCoIHGam801a9ddeXNr0ojvHLK6n31XCGWjELoC3oeDMhntDaK1Lw-Mv3DdHMw0Y2q7vFGc8Jqy34PZtJPlt0wb_r5PB5puLJZj-0_SXLhr3o4FKlYKygwBm62R7lSX8sf_i8SkGoa2HAX2JOoZEqOM4rUj1D50B4OkjHsiKZlxxEZ4sfkGh1p9MzNh9Mu/w449-h158/f1.JPG" width="449" /></a></div></div>학습에 사용된 주식 거래 이력 데이터 예시(Nasdaq 애플 주식)<br /><br /></div></div><div>강화학습을 위해 <a href="https://stable-baselines3.readthedocs.io/en/master/">stable-baselines3</a> 강화학습 라이브러리를 사용한다. </div><div style="text-align: center;"><img alt="" data-original-height="537" data-original-width="616" height="166" src="https://blogger.googleusercontent.com/img/a/AVvXsEgGfKHuW7K4ZdC1-O9xVgDsm8YvFgALXSRqzrLB_1VdU0hxW_xjfdjisROaIO-sc1qNODWeR2f4hVsliH1_wtCyKIrJwFCwQ1NhnU3tp-qcl-9CE85iXlkOKo3UnZzEhbI7G8uFy4EnS2Gq0Sr8FPSA9Tgi5TVTlt6T1Qj0RIiOhTHG0ktfNHf2e08Q1s42=w191-h166" style="color: #0000ee;" width="191" /></div><div style="text-align: center;"><a href="https://stable-baselines3.readthedocs.io/en/master/" style="text-align: left;">stable-baselines3</a></div><div><br /></div><div>우선 다음과 같이 터미널에서 개발환경을 준비한다.</div><div>conda create -n venv_rl python=3.8</div><div><div>conda activate venv_rl</div><div>pip install mpi4py numpy pandas pytest psutil scipy seaborn tensorflow torch tqdm joblib ipython gym gymnasium[classic_control]</div><div><div>pip install stable_baselines3 shimmy yfinance</div></div><div><div class="separator" style="clear: both; text-align: center;"><br /></div></div></div><div>강화학습 전략은 널리 알려진 알고리즘인 <a href="https://draft.blogger.com/blog/post/edit/5201956450461596914/32275534319264752#">PPO</a>(Proximal Policy Optimization)을 사용한다. 참고로, PPO는 OpenAI가 공개한 것으로 기존 Policy Graident Learning의 에피소드 단위가 아닌 Step 단위로 학습데이터를 만들어 학습을 시키는 방식이다. 이 예제는 미리 정의된 PPO MLP모델을 사용한다. 만약 직접 설계한 딥러닝 아키텍처를 사용하고자 하면, stable-baseline3의 <a href="https://stable-baselines3.readthedocs.io/en/v0.11.1/guide/custom_policy.html">Custom Policy Network 정의하기</a> 문서를 참고한다. </div><div>예제에서 거래 시 액션은 주식 매입, 판매, 보유 3가지이다. 보상은 순자산이 증가하는 방향으로 액션을 선택하는 단순한 방법을 사용한다. 학습 후, 테스트할 주식 데이터셋을 통해 시뮬레이션하여, 결과를 확인한다. 상세 코드는 다음과 같다.</div><div><br /></div><div><div><span style="color: #274e13; font-size: x-small;">import yfinance as yf</span></div><div><span style="color: #274e13; font-size: x-small;">import numpy as np</span></div><div><span style="color: #274e13; font-size: x-small;">import pandas as pd</span></div><div><span style="color: #274e13; font-size: x-small;"><br /></span></div><div><span style="color: #274e13; font-size: x-small;"># 강화 학습용 구글 주가 데이터 준비</span></div><div><span style="color: #274e13; font-size: x-small;">ticker = "GOOG" # Google stock market symbol</span></div><div><span style="color: #274e13; font-size: x-small;">data = yf.download(ticker, start="2018-01-01", end="2024-12-31") # Download the historical price data</span></div><div><span style="color: #274e13; font-size: x-small;">print(data.head()) # Display the first few rows of the data</span></div><div><span style="color: #274e13; font-size: x-small;">data = data.dropna() # 누락값이나 NaN 값을 제거</span></div><div><span style="color: #274e13; font-size: x-small;"><br /></span></div><div><span style="color: #274e13; font-size: x-small;"># 데이터 정규화</span></div><div><span style="color: #274e13; font-size: x-small;">from sklearn.preprocessing import MinMaxScaler</span></div><div><span style="color: #274e13; font-size: x-small;">scaler = MinMaxScaler()</span></div><div><span style="color: #274e13; font-size: x-small;">data_normalized = scaler.fit_transform(data)</span></div><div><span style="color: #274e13; font-size: x-small;">data = pd.DataFrame(data_normalized, columns=data.columns) </span></div><div><span style="color: #274e13; font-size: x-small;"><br /></span></div><div><span style="color: #274e13; font-size: x-small;"># 학습용, 테스트용 데이터 분할</span></div><div><span style="color: #274e13; font-size: x-small;">train_size = int(len(data) * 0.8)</span></div><div><span style="color: #274e13; font-size: x-small;">train_data = data[:train_size]</span></div><div><span style="color: #274e13; font-size: x-small;">test_data = data[train_size:]</span></div><div><span style="color: #274e13; font-size: x-small;"><br /></span></div><div><span style="color: #274e13; font-size: x-small;"># 강화 학습 환경 준비</span></div><div><span style="color: #274e13; font-size: x-small;">from stable_baselines3 import PPO</span></div><div><span style="color: #274e13; font-size: x-small;">import gym</span></div><div><span style="color: #274e13; font-size: x-small;">class TradingEnvironment(gym.Env):</span></div><div><span style="color: #274e13; font-size: x-small;"> def __init__(self, data):</span></div><div><span style="color: #274e13; font-size: x-small;"> self.data = data</span></div><div><span style="color: #274e13; font-size: x-small;"> self.action_space = gym.spaces.Discrete(3) # 액션은 주식 구입, 판매, 보유 3가지</span></div><div><span style="color: #274e13; font-size: x-small;"> self.observation_space = <a href="https://www.gymlibrary.dev/api/spaces/#box">gym.spaces.Box</a>(low=0, high=1, shape=(len(data.columns),)) # <a href="https://www.gymlibrary.dev/api/spaces/">샘플링공간</a> 정의</span></div><div><span style="color: #274e13; font-size: x-small;"><br /></span></div><div><span style="color: #274e13; font-size: x-small;"> def reset(self):</span></div><div><span style="color: #274e13; font-size: x-small;"> self.current_step = 0</span></div><div><span style="color: #274e13; font-size: x-small;"> self.account_balance = 100000 # 초기 주식 계좌 보유액. Initial account balance</span></div><div><span style="color: #274e13; font-size: x-small;"> self.shares_held = 0</span></div><div><span style="color: #274e13; font-size: x-small;"> self.net_worth = self.account_balance</span></div><div><span style="color: #274e13; font-size: x-small;"> self.max_net_worth = self.account_balance</span></div><div><span style="color: #274e13; font-size: x-small;"><br /></span></div><div><span style="color: #274e13; font-size: x-small;"> return self._next_observation()</span></div><div><span style="color: #274e13; font-size: x-small;"><br /></span></div><div><span style="color: #274e13; font-size: x-small;"> def _next_observation(self):</span></div><div><span style="color: #274e13; font-size: x-small;"> return self.data.iloc[self.current_step].values</span></div><div><span style="color: #274e13; font-size: x-small;"><br /></span></div><div><span style="color: #274e13; font-size: x-small;"> def step(self, action):</span></div><div><span style="color: #274e13; font-size: x-small;"> self._take_action(action)</span></div><div><span style="color: #274e13; font-size: x-small;"> self.current_step += 1</span></div><div><span style="color: #274e13; font-size: x-small;"><br /></span></div><div><span style="color: #274e13; font-size: x-small;"> if self.current_step > len(self.data) - 1:</span></div><div><span style="color: #274e13; font-size: x-small;"> self.current_step = 0</span></div><div><span style="color: #274e13; font-size: x-small;"><br /></span></div><div><span style="color: #274e13; font-size: x-small;"> return self._next_observation(), self._get_reward(), self.net_worth, {}</span></div><div><span style="color: #274e13; font-size: x-small;"><br /></span></div><div><span style="color: #274e13; font-size: x-small;"> def _take_action(self, action):</span></div><div><span style="color: #274e13; font-size: x-small;"> if action == 0: # 주식 구매</span></div><div><span style="color: #274e13; font-size: x-small;"> self.shares_held += self.account_balance / self.data.iloc[self.current_step].values[0] # 보유할 주식수</span></div><div><span style="color: #274e13; font-size: x-small;"> self.account_balance -= self.account_balance # 잔고 모두 주식 구매에 사용</span></div><div><span style="color: #274e13; font-size: x-small;"> elif action == 1: # 주식 판매</span></div><div><span style="color: #274e13; font-size: x-small;"> self.account_balance += self.shares_held * self.data.iloc[self.current_step].values[0] # 판매할 주식수</span></div><div><span style="color: #274e13; font-size: x-small;"> self.shares_held -= self.shares_held # 판매되고 남은 주식</span></div><div><span style="color: #274e13; font-size: x-small;"><br /></span></div><div><span style="color: #274e13; font-size: x-small;"> self.net_worth = self.account_balance + self.shares_held * self.data.iloc[self.current_step].values[0] # 현가치</span></div><div><span style="color: #274e13; font-size: x-small;"><br /></span></div><div><span style="color: #274e13; font-size: x-small;"> if self.net_worth > self.max_net_worth:</span></div><div><span style="color: #274e13; font-size: x-small;"> self.max_net_worth = self.net_worth</span></div><div><span style="color: #274e13; font-size: x-small;"><br /></span></div><div><span style="color: #274e13; font-size: x-small;"> def _get_reward(self):</span></div><div><span style="color: #274e13; font-size: x-small;"> return self.net_worth - self.account_balance # 보상은 현가치와 잔고의 차</span></div><div><span style="color: #274e13; font-size: x-small;"><br /></span></div><div><span style="color: #274e13; font-size: x-small;"># 강화학습</span></div><div><span style="color: #274e13; font-size: x-small;">env = TradingEnvironment(train_data) # Create the trading environment</span></div><div><span style="color: #274e13; font-size: x-small;">model = PPO("MlpPolicy", env, verbose=1) # Initialize the PPO model</span></div><div><span style="color: #274e13; font-size: x-small;">model.learn(total_timesteps=50000) # Train the model </span></div><div><span style="color: #274e13; font-size: x-small;"><br /></span></div><div><span style="color: #274e13; font-size: x-small;"># 학습된 모델로 주식 거래 시뮬레이션</span></div><div><span style="color: #274e13; font-size: x-small;">def simulate_trading_strategy(model, data):</span></div><div><span style="color: #274e13; font-size: x-small;"> env = TradingEnvironment(data)</span></div><div><span style="color: #274e13; font-size: x-small;"> obs = env.reset()</span></div><div><span style="color: #274e13; font-size: x-small;"><br /></span></div><div><span style="color: #274e13; font-size: x-small;"> net_worths = []</span></div><div><span style="color: #274e13; font-size: x-small;"> for i in range(len(data)):</span></div><div><span style="color: #274e13; font-size: x-small;"> action, _ = model.predict(obs)</span></div><div><span style="color: #274e13; font-size: x-small;"> obs, _, _, _ = env.step(action)</span></div><div><span style="color: #274e13; font-size: x-small;"> net_worths.append(env.net_worth)</span></div><div><span style="color: #274e13; font-size: x-small;"> return net_worths</span></div><div><span style="color: #274e13; font-size: x-small;"><br /></span></div><div><span style="color: #274e13; font-size: x-small;">import matplotlib.pyplot as plt</span></div><div><span style="color: #274e13; font-size: x-small;">net_worth = simulate_trading_strategy(model, test_data)</span></div><div><span style="color: #274e13; font-size: x-small;"><br /></span></div><div><span style="color: #274e13; font-size: x-small;"># Plot the net worth over time</span></div><div><span style="color: #274e13; font-size: x-small;">plt.plot(net_worth, color='green') # color = 'green'</span></div><div><span style="color: #274e13; font-size: x-small;">plt.xlabel("Time")</span></div><div><span style="color: #274e13; font-size: x-small;">plt.ylabel("Net Worth")</span></div><div><span style="color: #274e13; font-size: x-small;">plt.title("Net Worth over Time")</span></div><div><span style="color: #274e13; font-size: x-small;">plt.show()</span></div><div><span style="color: #274e13; font-size: x-small;">input()</span></div></div><div><br /></div><div>결과는 다음 그림과 같다. 강화학습된 모델을 사용했을 때, 시간이 지나면서 순자산이 어떻게 증가하는 지를 확인할 수 있다.</div><div><br /></div><div><div>단, 이 결과는 패턴이 일관성있게 변화할 때 얻을 수 있는 결과이며, 펜데믹같이 예측하기 어려운 이벤트는 학습 데이터가 없었으므로 당연히 탐지하지 못한다. 다음은 주식 VWAP, RSI를 고려해, 강화학습한 모델로 주식 자동 트레이딩하였을때 자산가치를 보여준다. </div></div><div><br /></div><div><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhtoB_IBtDm9XkV30yTo72b-6rDUbhgyYu5GHbCoCZKXc4DkVpzD708YA2-l5PQfMQtaPHBbXVJiNqaPAEPVdE85YKelXHDCjdaO1VHIszjWJXvAWE2bBtLK_WhxAx63s1CpuePcyxjMyfHRRx8zLRdiIFhX-m6No_MJjYrVk-3xAFWCQmCQ2meOB010_nn" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="660" data-original-width="1195" height="253" src="https://blogger.googleusercontent.com/img/a/AVvXsEhtoB_IBtDm9XkV30yTo72b-6rDUbhgyYu5GHbCoCZKXc4DkVpzD708YA2-l5PQfMQtaPHBbXVJiNqaPAEPVdE85YKelXHDCjdaO1VHIszjWJXvAWE2bBtLK_WhxAx63s1CpuePcyxjMyfHRRx8zLRdiIFhX-m6No_MJjYrVk-3xAFWCQmCQ2meOB010_nn=w457-h253" width="457" /></a></div><div class="separator" style="clear: both; text-align: center;">Google stock trading net worth (timestemps=10000)</div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhTh8FvJjRq60DuRtH2kA7nlLmzG29F03aO3d_UIBNW6aKl6yAliHau_c5aTV-Vg5aF83KTOVmCOn89zUR82QvQuZzEk0VgM3HJGedCfDiLhqY2224xDmbcvxfip9zY8twYHF1I0wDcolZ547iBq1IaaiKIipl0bFQFFUUso3AMMZ75_392mW46yshWKe81/s1082/F2.JPG" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="803" data-original-width="1082" height="296" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhTh8FvJjRq60DuRtH2kA7nlLmzG29F03aO3d_UIBNW6aKl6yAliHau_c5aTV-Vg5aF83KTOVmCOn89zUR82QvQuZzEk0VgM3HJGedCfDiLhqY2224xDmbcvxfip9zY8twYHF1I0wDcolZ547iBq1IaaiKIipl0bFQFFUUso3AMMZ75_392mW46yshWKe81/w450-h296/F2.JPG" width="450" /></a></div><div class="separator" style="clear: both; text-align: center;">Apple stock trading net worth (timestemps=50000)</div><div class="separator" style="clear: both; text-align: center;"><br /></div></div></div></div><div><br /></div><div><div><b><span style="font-size: medium;">파이토치로 주식 트레이딩 강화학습 직접 구현하기</span></b></div></div><div>앞서 코딩된 강화학습은 stable baseline3 라이브러리를 사용한 것이다. 강화학습기술의 내부 메커니즘을 관찰하기 위해, 주식 자동 거래 강화학습 모델의 구현 코드를 확인해 본다. </div><div><br /></div><div>주식 거래 데이터는 앞서 언급한것과 동일한 형식인 엑셀파일로 저장되어 있다. 다음은 강화학습 시 사용될 거래 데이터 이력이다. </div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg6wPuiIjZniuNHYzeisHbx-Oig4ws7wpJFu2zGLZtFwSCuzLMjR6BH9GeBBDSYAFYfTHcb8i4b6_lSC8pp1ewUUDx0IV_XbLiD7nErOQq7Efuh6R0iOC0O-jfuUxE7xju6FBReAHAljvMzlXYZ82C6iV8-haa0_TWNWtobJmfps-mZ06VA3jliZXWNsCq0/s1844/product1.JPG" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="961" data-original-width="1844" height="209" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg6wPuiIjZniuNHYzeisHbx-Oig4ws7wpJFu2zGLZtFwSCuzLMjR6BH9GeBBDSYAFYfTHcb8i4b6_lSC8pp1ewUUDx0IV_XbLiD7nErOQq7Efuh6R0iOC0O-jfuUxE7xju6FBReAHAljvMzlXYZ82C6iV8-haa0_TWNWtobJmfps-mZ06VA3jliZXWNsCq0/w400-h209/product1.JPG" width="400" /></a></div><div class="separator" style="clear: both; text-align: center;">애플 주가 이력 데이터</div><div><br /></div><div>강화학습 코드 개발 순서는 다음과 같다. </div><ul style="text-align: left;"><li>보상 알고리즘이 포함된 환경 정책 클래스를 정의</li><li>Q-네트워크 모델 정의</li><li>에이전트 정의</li><li>관찰 상태 데이터 학습에 사용될 재생 메모리 버퍼 정의</li><li>DQN 기법으로 보상 최대화되도록 학습</li></ul><div>다음은 이와 관련된 상세 코드이다. </div><div><div><span style="color: #274e13; font-size: x-small;">import os, time, copy, random, logging</span></div><div><span style="color: #274e13; font-size: x-small;">import numpy as np, pandas as pd</span></div><div><span style="color: #274e13; font-size: x-small;">import torch</span></div><div><span style="color: #274e13; font-size: x-small;">import torch.nn as nn</span></div><div><span style="color: #274e13; font-size: x-small;">import torch.nn.functional as F</span></div><div><span style="color: #274e13; font-size: x-small;">import torch.optim as optim</span></div><div><span style="color: #274e13; font-size: x-small;">from plotly import tools</span></div><div><span style="color: #274e13; font-size: x-small;">from plotly.graph_objs import *</span></div><div><span style="color: #274e13; font-size: x-small;">from plotly.offline import init_notebook_mode, iplot, iplot_mpl</span></div><div><span style="color: #274e13; font-size: x-small;">from collections import namedtuple, deque</span></div><div><span style="color: #274e13; font-size: x-small;"><br /></span></div><div><span style="color: #274e13; font-size: x-small;"># 주식 데이터 로딩 </span></div><div><span style="color: #274e13; font-size: x-small;">data = pd.read_csv('./AAPl2.csv') </span></div><div><span style="color: #274e13; font-size: x-small;">data['Date'] = pd.to_datetime(data['Date'])</span></div><div><span style="color: #274e13; font-size: x-small;">data = data.set_index('Date')</span></div><div><span style="color: #274e13; font-size: x-small;">print(data.index.min(), data.index.max())</span></div><div><span style="color: #274e13; font-size: x-small;">data.head()</span></div><div><span style="color: #274e13; font-size: x-small;"><br /></span></div><div><span style="color: #274e13; font-size: x-small;"># 학습, 테스트 데이터 분할</span></div><div><span style="color: #274e13; font-size: x-small;">date_split = '2022-01-01'</span></div><div><span style="color: #274e13; font-size: x-small;">train = data[:date_split]</span></div><div><span style="color: #274e13; font-size: x-small;">test = data[date_split:]</span></div><div><span style="color: #274e13; font-size: x-small;">print(len(train), len(test))</span></div><div><span style="color: #274e13; font-size: x-small;"><br /></span></div><div><span style="color: #274e13; font-size: x-small;"># 학습 데이터 그래프 출력</span></div><div><span style="color: #274e13; font-size: x-small;">data = [</span></div><div><span style="color: #274e13; font-size: x-small;"> Candlestick(x=train.index, open=train['Open'], high=train['High'], low=train['Low'], close=train['Close'], name='train'),</span></div><div><span style="color: #274e13; font-size: x-small;"> Candlestick(x=test.index, open=test['Open'], high=test['High'], low=test['Low'], close=test['Close'], name='test')</span></div><div><span style="color: #274e13; font-size: x-small;">]</span></div><div><span style="color: #274e13; font-size: x-small;">layout = {</span></div><div><span style="color: #274e13; font-size: x-small;"> 'shapes': [</span></div><div><span style="color: #274e13; font-size: x-small;"> {'x0': date_split, 'x1': date_split, 'y0': 0, 'y1': 1, 'xref': 'x', 'yref': 'paper', 'line': {'color': 'rgb(0,0,0)', 'width': 1}}</span></div><div><span style="color: #274e13; font-size: x-small;"> ],</span></div><div><span style="color: #274e13; font-size: x-small;"> 'annotations': [</span></div><div><span style="color: #274e13; font-size: x-small;"> {'x': date_split, 'y': 1.0, 'xref': 'x', 'yref': 'paper', 'showarrow': False, 'xanchor': 'left', 'text': ' test data'},</span></div><div><span style="color: #274e13; font-size: x-small;"> {'x': date_split, 'y': 1.0, 'xref': 'x', 'yref': 'paper', 'showarrow': False, 'xanchor': 'right', 'text': 'train data '}</span></div><div><span style="color: #274e13; font-size: x-small;"> ]</span></div><div><span style="color: #274e13; font-size: x-small;">}</span></div><div><span style="color: #274e13; font-size: x-small;">figure = Figure(data=data, layout=layout)</span></div><div><span style="color: #274e13; font-size: x-small;">iplot(figure)</span></div><div><span style="color: #274e13; font-size: x-small;"><br /></span></div><div><span style="color: #274e13; font-size: x-small;"># 주식 마켓 환경 정책</span></div><div><span style="color: #274e13; font-size: x-small;">class StockMarketEnv: </span></div><div><span style="color: #274e13; font-size: x-small;"> def __init__(self, data, history_t=90):</span></div><div><span style="color: #274e13; font-size: x-small;"> self.data = data</span></div><div><span style="color: #274e13; font-size: x-small;"> self.history_t = history_t</span></div><div><span style="color: #274e13; font-size: x-small;"> self.reset()</span></div><div><span style="color: #274e13; font-size: x-small;"> </span></div><div><span style="color: #274e13; font-size: x-small;"> def reset(self):</span></div><div><span style="color: #274e13; font-size: x-small;"> self.t = 0</span></div><div><span style="color: #274e13; font-size: x-small;"> self.done = False</span></div><div><span style="color: #274e13; font-size: x-small;"><div> self.total_profits = 0</div></span></div><div><span style="color: #274e13; font-size: x-small;"> self.profits = 0</span></div><div><span style="color: #274e13; font-size: x-small;"> self.positions = []</span></div><div><span style="color: #274e13; font-size: x-small;"> self.position_value = 0</span></div><div><span style="color: #274e13; font-size: x-small;"> self.history = [0 for _ in range(self.history_t)]</span></div><div><span style="color: #274e13; font-size: x-small;"> reward = 0</span></div><div><span style="color: #274e13; font-size: x-small;"> return [self.position_value] + self.history, reward ,self.done# obs</span></div><div><span style="color: #274e13; font-size: x-small;"> </span></div><div><span style="color: #274e13; font-size: x-small;"> def step(self, act):</span></div><div><span style="color: #274e13; font-size: x-small;"> reward = 0</span></div><div><span style="color: #274e13; font-size: x-small;"> </span></div><div><span style="color: #274e13; font-size: x-small;"> # 액션. 0=보유, 1=buy, 2=sell</span></div><div><span style="color: #274e13; font-size: x-small;"> if act == 1: # 구매, 구매 시점 종가 기록</span></div><div><span style="color: #274e13; font-size: x-small;"> self.positions.append(self.data.iloc[self.t, :]['Close']) </span></div><div><span style="color: #274e13; font-size: x-small;"> elif act == 2: # 판매 </span></div><div><span style="color: #274e13; font-size: x-small;"> if len(self.positions) == 0: # 구매 기록이 없을 때</span></div><div><span style="color: #274e13; font-size: x-small;"> reward = -1</span></div><div><span style="color: #274e13; font-size: x-small;"> else: </span></div><div><span style="color: #274e13; font-size: x-small;"> profits = 0</span></div><div><span style="color: #274e13; font-size: x-small;"> for p in self.positions: # 구매 주식이 있을 때</span></div><div><span style="color: #274e13; font-size: x-small;"> profits += (self.data.iloc[self.t, :]['Close'] - p) # 이익 = 현재 종가 - 구매 주식 가격들</span></div><div><span style="color: #274e13; font-size: x-small;"> reward += profits # 리워드 최대치 계산(이익 총합)</span></div><div><span style="color: #274e13; font-size: x-small;"> self.profits += profits</span></div><div><span style="color: #274e13; font-size: x-small;"> self.positions = []</span></div><div><span style="color: #274e13; font-size: x-small;"> </span></div><div><span style="color: #274e13; font-size: x-small;"> self.total_profits += self.profits</span></div><div><span style="color: #274e13; font-size: x-small;"> self.t += 1 # 관찰 시점 다음 이동</span></div><div><span style="color: #274e13; font-size: x-small;"> self.position_value = 0</span></div><div><span style="color: #274e13; font-size: x-small;"> for p in self.positions:</span></div><div><span style="color: #274e13; font-size: x-small;"> self.position_value += (self.data.iloc[self.t, :]['Close'] - p)</span></div><div><span style="color: #274e13; font-size: x-small;"> self.history.pop(0)</span></div><div><span style="color: #274e13; font-size: x-small;"><br /></span></div><div><span style="color: #274e13; font-size: x-small;"> self.history.append(self.data.iloc[self.t, :]['Close'] - self.data.iloc[(self.t-1), :]['Close'])</span></div><div><span style="color: #274e13; font-size: x-small;"> </span></div><div><span style="color: #274e13; font-size: x-small;"> # 보상치 계산</span></div><div><span style="color: #274e13; font-size: x-small;"> if reward > 0:</span></div><div><span style="color: #274e13; font-size: x-small;"> reward = 1</span></div><div><span style="color: #274e13; font-size: x-small;"> elif reward < 0:</span></div><div><span style="color: #274e13; font-size: x-small;"> reward = -1</span></div><div><span style="color: #274e13; font-size: x-small;"> </span></div><div><span style="color: #274e13; font-size: x-small;"> return [self.position_value] + self.history, reward, self.done # 관찰, 보상, 에피소드 종료</span></div><div><span style="color: #274e13; font-size: x-small;"><br /></span></div><div><span style="color: #274e13; font-size: x-small;">import torch</span></div><div><span style="color: #274e13; font-size: x-small;">import torch.nn as nn</span></div><div><span style="color: #274e13; font-size: x-small;">import torch.nn.functional as F</span></div><div><span style="color: #274e13; font-size: x-small;"><br /></span></div><div><span style="color: #274e13; font-size: x-small;">class QNetwork(nn.Module):</span></div><div><span style="color: #274e13; font-size: x-small;"> def __init__(self, state_size, action_size, seed, fc1_units=64, fc2_units=64): # 상태 차원수, 액션 차원수, 램덤값, FC1 유닛수, FC2 유닛수</span></div><div><span style="color: #274e13; font-size: x-small;"> super(QNetwork, self).__init__()</span></div><div><span style="color: #274e13; font-size: x-small;"> self.seed = torch.manual_seed(seed)</span></div><div><span style="color: #274e13; font-size: x-small;"> self.fc1 = nn.Linear(state_size, fc1_units)</span></div><div><span style="color: #274e13; font-size: x-small;"> self.fc2 = nn.Linear(fc1_units, fc2_units)</span></div><div><span style="color: #274e13; font-size: x-small;"> self.fc3 = nn.Linear(fc2_units, action_size)</span></div><div><span style="color: #274e13; font-size: x-small;"><br /></span></div><div><span style="color: #274e13; font-size: x-small;"> def forward(self, state):</span></div><div><span style="color: #274e13; font-size: x-small;"> x = F.relu(self.fc1(state)) # 관찰 상태 벡터 입력</span></div><div><span style="color: #274e13; font-size: x-small;"> x = F.relu(self.fc2(x))</span></div><div><span style="color: #274e13; font-size: x-small;"> return self.fc3(x) # 액션 출력</span></div><div><span style="color: #274e13; font-size: x-small;"><br /></span></div><div><span style="color: #274e13; font-size: x-small;"># 에이전트 학습 준비</span></div><div><span style="color: #274e13; font-size: x-small;">BUFFER_SIZE = int(1e5) # 재생 메모리 버퍼 크기</span></div><div><span style="color: #274e13; font-size: x-small;">MINI_BATCH_SIZE = 64 # 미니 배치 크기</span></div><div><span style="color: #274e13; font-size: x-small;">GAMMA = 0.99 # 할인율 감마값</span></div><div><span style="color: #274e13; font-size: x-small;">TAU = 1e-3 # 타겟 네트워크 파라메터 소프트 업데이트를 위한 타오 계수</span></div><div><span style="color: #274e13; font-size: x-small;">LR = 5e-4 # 학습율</span></div><div><span style="color: #274e13; font-size: x-small;">UPDATE_EVERY = 4 # 네트워크 갱신 빈도수 </span></div><div><span style="color: #274e13; font-size: x-small;"><br /></span></div><div><span style="color: #274e13; font-size: x-small;">device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")</span></div><div><span style="color: #274e13; font-size: x-small;"><br /></span></div><div><span style="color: #274e13; font-size: x-small;">class Agent():</span></div><div><span style="color: #274e13; font-size: x-small;"> def __init__(self, state_size, action_size, seed, DDQN=False): # 상태 차원 크기, 액션 차원 크기, 램덤값, DDQN 학습 플래그</span></div><div><span style="color: #274e13; font-size: x-small;"> self.state_size = state_size</span></div><div><span style="color: #274e13; font-size: x-small;"> self.action_size = action_size</span></div><div><span style="color: #274e13; font-size: x-small;"> self.seed = random.seed(seed)</span></div><div><span style="color: #274e13; font-size: x-small;"><br /></span></div><div><span style="color: #274e13; font-size: x-small;"> # Q-Network 지역, 타겟 네트워크 준비</span></div><div><span style="color: #274e13; font-size: x-small;"> print("state size :",self.state_size)</span></div><div><span style="color: #274e13; font-size: x-small;"> print("action size :",self.action_size)</span></div><div><span style="color: #274e13; font-size: x-small;"> self.qnetwork_local = QNetwork(state_size, action_size, seed).to(device)</span></div><div><span style="color: #274e13; font-size: x-small;"> self.qnetwork_target = QNetwork(state_size, action_size, seed).to(device)</span></div><div><span style="color: #274e13; font-size: x-small;"> self.optimizer = optim.Adam(self.qnetwork_local.parameters(), lr=LR)</span></div><div><span style="color: #274e13; font-size: x-small;"><br /></span></div><div><span style="color: #274e13; font-size: x-small;"> # 재생 메모리 버퍼 준비</span></div><div><span style="color: #274e13; font-size: x-small;"> self.memory = ReplayBuffer(action_size, BUFFER_SIZE, MINI_BATCH_SIZE, seed)</span></div><div><span style="color: #274e13; font-size: x-small;"><br /></span></div><div><span style="color: #274e13; font-size: x-small;"> # 타임 스템프 인덱스 설정</span></div><div><span style="color: #274e13; font-size: x-small;"> self.t_step = 0</span></div><div><span style="color: #274e13; font-size: x-small;"> self.DDQN = DDQN</span></div><div><span style="color: #274e13; font-size: x-small;"> print("DDQN is now :",self.DDQN)</span></div><div><span style="color: #274e13; font-size: x-small;"><br /></span></div><div><span style="color: #274e13; font-size: x-small;"> def step(self, state, action, reward, next_state, done):</span></div><div><span style="color: #274e13; font-size: x-small;"> # 재생 메모리 버퍼에 상태, 액션, 보상, 다음 상태, 종료 플래그 저장</span></div><div><span style="color: #274e13; font-size: x-small;"> self.memory.add(state, action, reward, next_state, done)</span></div><div><span style="color: #274e13; font-size: x-small;"><br /></span></div><div><span style="color: #274e13; font-size: x-small;"> # 매 시점별 타임스템프 업데이트</span></div><div><span style="color: #274e13; font-size: x-small;"> self.t_step = (self.t_step + 1) % UPDATE_EVERY</span></div><div><span style="color: #274e13; font-size: x-small;"> if self.t_step == 0: # 샘플수가 충분히 모이면, 재생버퍼에서 저장된 데이터 샘플링하여 학습</span></div><div><span style="color: #274e13; font-size: x-small;"> if len(self.memory) > MINI_BATCH_SIZE:</span></div><div><span style="color: #274e13; font-size: x-small;"> experiences = self.memory.sample()</span></div><div><span style="color: #274e13; font-size: x-small;"> self.learn(experiences, GAMMA)</span></div><div><span style="color: #274e13; font-size: x-small;"><br /></span></div><div><span style="color: #274e13; font-size: x-small;"> def act(self, state, eps=0.): # 현재 상태, epsilon-greedy 액션 선택 엡실론값</span></div><div><span style="color: #274e13; font-size: x-small;"> state = torch.from_numpy(state).float().unsqueeze(0).to(device)</span></div><div><span style="color: #274e13; font-size: x-small;"> self.qnetwork_local.eval()</span></div><div><span style="color: #274e13; font-size: x-small;"> with torch.no_grad():</span></div><div><span style="color: #274e13; font-size: x-small;"> action_values = self.qnetwork_local(state) # 상태값에 대한 로컬 모델 예측 액션값 획득</span></div><div><span style="color: #274e13; font-size: x-small;"> self.qnetwork_local.train() # 로컬 모델 네트워크 학습</span></div><div><span style="color: #274e13; font-size: x-small;"><br /></span></div><div><span style="color: #274e13; font-size: x-small;"> # Epsilon-greedy 액션 선택</span></div><div><span style="color: #274e13; font-size: x-small;"> if random.random() > eps:</span></div><div><span style="color: #274e13; font-size: x-small;"> return np.argmax(action_values.cpu().data.numpy())</span></div><div><span style="color: #274e13; font-size: x-small;"> else:</span></div><div><span style="color: #274e13; font-size: x-small;"> return random.choice(np.arange(self.action_size))</span></div><div><span style="color: #274e13; font-size: x-small;"><br /></span></div><div><span style="color: #274e13; font-size: x-small;"> def learn(self, experiences, gamma): # 관찰 데이터(s, a, r, s', done), 할인율 감마값</span></div><div><span style="color: #274e13; font-size: x-small;"> states, actions, rewards, next_states, dones = experiences</span></div><div><span style="color: #274e13; font-size: x-small;"><br /></span></div><div><span style="color: #274e13; font-size: x-small;"> Q_expected = self.qnetwork_local(states).gather(1, actions) # 현재 상태에 대한 지역 네트워크 모델 현재 보상 획득</span></div><div><span style="color: #274e13; font-size: x-small;"><br /></span></div><div><span style="color: #274e13; font-size: x-small;"> # 다음 상태에 대한 타겟 네트워크 모델 미래 보상 획득</span></div><div><span style="color: #274e13; font-size: x-small;"> if self.DDQN: # DDQN 알고리즘</span></div><div><span style="color: #274e13; font-size: x-small;"> next_actions = torch.max(self.qnetwork_local(next_states), dim=-1)[1]</span></div><div><span style="color: #274e13; font-size: x-small;"> next_actions_t = torch.LongTensor(next_actions).reshape(-1,1).to(</span></div><div><span style="color: #274e13; font-size: x-small;"> device=device)</span></div><div><span style="color: #274e13; font-size: x-small;"> target_qvals = self.qnetwork_target(next_states)</span></div><div><span style="color: #274e13; font-size: x-small;"> Q_targets_next = torch.gather(target_qvals, 1, next_actions_t).detach()</span></div><div><span style="color: #274e13; font-size: x-small;"> else: # DQN 알고리즘</span></div><div><span style="color: #274e13; font-size: x-small;"> Q_targets_next = self.qnetwork_target(next_states).detach().max(1)[0].unsqueeze(1) </span></div><div><span style="color: #274e13; font-size: x-small;"><br /></span></div><div><span style="color: #274e13; font-size: x-small;"> Q_targets = rewards + (gamma * Q_targets_next * (1 - dones))</span></div><div><span style="color: #274e13; font-size: x-small;"> loss = F.mse_loss(Q_expected, Q_targets) # loss = 현재 보상 - 미래 보상</span></div><div><span style="color: #274e13; font-size: x-small;"><br /></span></div><div><span style="color: #274e13; font-size: x-small;"> self.optimizer.zero_grad() # loss 최소화</span></div><div><span style="color: #274e13; font-size: x-small;"> loss.backward() # 네트워크 역전파</span></div><div><span style="color: #274e13; font-size: x-small;"> self.optimizer.step() # 한단계 실행</span></div><div><span style="color: #274e13; font-size: x-small;"><br /></span></div><div><span style="color: #274e13; font-size: x-small;"> self.soft_update(self.qnetwork_local, self.qnetwork_target, TAU) # 목표 학습 모델 파라메터 갱신</span></div><div><span style="color: #274e13; font-size: x-small;"><br /></span></div><div><span style="color: #274e13; font-size: x-small;"> def soft_update(self, local_model, target_model, tau): # 지역 모델, 목표 모델, 인터폴레이션 비율 타오값</span></div><div><span style="color: #274e13; font-size: x-small;"> # Soft update model. θ_target = τ*θ_local + (1 - τ)*θ_target</span></div><div><span style="color: #274e13; font-size: x-small;"> for target_param, local_param in zip(target_model.parameters(), local_model.parameters()):</span></div><div><span style="color: #274e13; font-size: x-small;"> target_param.data.copy_(tau*local_param.data + (1.0-tau)*target_param.data)</span></div><div><span style="color: #274e13; font-size: x-small;"><br /></span></div><div><span style="color: #274e13; font-size: x-small;">class ReplayBuffer:</span></div><div><span style="color: #274e13; font-size: x-small;"> def __init__(self, action_size, buffer_size, MINI_BATCH_SIZE, seed): # 액션 차원 크기, 재생 메모리 버퍼 크기, 미니배치 크기, 랜덤값</span></div><div><span style="color: #274e13; font-size: x-small;"> self.action_size = action_size</span></div><div><span style="color: #274e13; font-size: x-small;"> self.memory = deque(maxlen=buffer_size)</span></div><div><span style="color: #274e13; font-size: x-small;"> self.MINI_BATCH_SIZE = MINI_BATCH_SIZE</span></div><div><span style="color: #274e13; font-size: x-small;"> self.experience = namedtuple("Experience", field_names=["state", "action", "reward", "next_state", "done"])</span></div><div><span style="color: #274e13; font-size: x-small;"> self.seed = random.seed(seed)</span></div><div><span style="color: #274e13; font-size: x-small;"><br /></span></div><div><span style="color: #274e13; font-size: x-small;"> def add(self, state, action, reward, next_state, done): # 경험 및 재생 메모리에 현재 상태 데이터 추가</span></div><div><span style="color: #274e13; font-size: x-small;"> e = self.experience(state, action, reward, next_state, done)</span></div><div><span style="color: #274e13; font-size: x-small;"> self.memory.append(e)</span></div><div><span style="color: #274e13; font-size: x-small;"><br /></span></div><div><span style="color: #274e13; font-size: x-small;"> def sample(self): # 미니배치 학습 데이터 샘플링하기</span></div><div><span style="color: #274e13; font-size: x-small;"> experiences = random.sample(self.memory, k=self.MINI_BATCH_SIZE)</span></div><div><span style="color: #274e13; font-size: x-small;"><br /></span></div><div><span style="color: #274e13; font-size: x-small;"> states = torch.from_numpy(np.vstack([e.state for e in experiences if e is not None])).float().to(device)</span></div><div><span style="color: #274e13; font-size: x-small;"> actions = torch.from_numpy(np.vstack([e.action for e in experiences if e is not None])).long().to(device)</span></div><div><span style="color: #274e13; font-size: x-small;"> rewards = torch.from_numpy(np.vstack([e.reward for e in experiences if e is not None])).float().to(device)</span></div><div><span style="color: #274e13; font-size: x-small;"> next_states = torch.from_numpy(np.vstack([e.next_state for e in experiences if e is not None])).float().to(device)</span></div><div><span style="color: #274e13; font-size: x-small;"> dones = torch.from_numpy(np.vstack([e.done for e in experiences if e is not None]).astype(np.uint8)).float().to(device)</span></div><div><span style="color: #274e13; font-size: x-small;"><br /></span></div><div><span style="color: #274e13; font-size: x-small;"> return (states, actions, rewards, next_states, dones)</span></div><div><span style="color: #274e13; font-size: x-small;"><br /></span></div><div><span style="color: #274e13; font-size: x-small;"> def __len__(self):</span></div><div><span style="color: #274e13; font-size: x-small;"> return len(self.memory)</span></div><div><span style="color: #274e13; font-size: x-small;"><br /></span></div><div><span style="color: #274e13; font-size: x-small;">def train_DQN(n_episodes=1500, max_t=300, eps_start=1.0, eps_end=0.01, eps_decay=0.995,pth_file = 'checkpoint.pth'):</span></div><div><span style="color: #274e13; font-size: x-small;"> eps = eps_start # initialize the score</span></div><div><span style="color: #274e13; font-size: x-small;"> scores_window = deque(maxlen=100) # 스코어 저장 큐</span></div><div><span style="color: #274e13; font-size: x-small;"> scores = []</span></div><div><span style="color: #274e13; font-size: x-small;"><div> total_profits_history = []</div></span></div><div><span style="color: #274e13; font-size: x-small;"> logging.info('Starting of agent training ......')</span></div><div><span style="color: #274e13; font-size: x-small;"> for episode in range(1,n_episodes+1):</span></div><div><span style="color: #274e13; font-size: x-small;"> next_state, reward, done = env.reset() # 환경 초기화</span></div><div><span style="color: #274e13; font-size: x-small;"> state = next_state # 현재 상태 획득</span></div><div><span style="color: #274e13; font-size: x-small;"> score = 0</span></div><div><span style="color: #274e13; font-size: x-small;"> for time_step in range(max_t):</span></div><div><span style="color: #274e13; font-size: x-small;"> action = agent.act(np.array(state), eps) # 액션 선택</span></div><div><span style="color: #274e13; font-size: x-small;"> next_state, reward, done = env.step(action) # 액션에 대한 보상 획득</span></div><div><span style="color: #274e13; font-size: x-small;"> agent.step(state, action, reward, next_state, done) # 에이전트 액션 실행</span></div><div><span style="color: #274e13; font-size: x-small;"><br /></span></div><div><span style="color: #274e13; font-size: x-small;"> score += reward # 보상 스코어값 누적 </span></div><div><span style="color: #274e13; font-size: x-small;"> state = next_state # 다음 상태를 현재 상태로 설정</span></div><div><span style="color: #274e13; font-size: x-small;"> eps = max(eps_end, eps_decay * eps) # epsilon-greedy 액션 선택 엡실론값</span></div><div><span style="color: #274e13; font-size: x-small;"> if done:</span></div><div><span style="color: #274e13; font-size: x-small;"> break</span></div><div><span style="color: #274e13; font-size: x-small;"> scores.append(score)</span></div><div><span style="color: #274e13; font-size: x-small;"> scores_window.append(score) # 현재 스코어 저장</span></div><div><span style="color: #274e13; font-size: x-small;"> total_profits_history.append(env.total_profits)</span></div><div><span style="color: #274e13; font-size: x-small;"> </span></div><div><span style="color: #274e13; font-size: x-small;"> print('\rEpisode {}\tAverage Score: {:.2f}'.format(episode, np.mean(scores_window)), end="")</span></div><div><span style="color: #274e13; font-size: x-small;"> if episode % 100 == 0:</span></div><div><span style="color: #274e13; font-size: x-small;"> print('\rEpisode {}\tAverage Score: {:.2f}'.format(episode, np.mean(scores_window)))</span></div><div><span style="color: #274e13; font-size: x-small;"> if np.mean(scores_window)>=5000:</span></div><div><span style="color: #274e13; font-size: x-small;"> print('\nEnvironment solved in {:d} episodes!\tAverage Score: {:.2f}'.format(episode-100, np.mean(scores_window)))</span></div><div><span style="color: #274e13; font-size: x-small;"> torch.save(agent.qnetwork_local.state_dict(), pth_file)</span></div><div><span style="color: #274e13; font-size: x-small;"> break</span></div><div><span style="color: #274e13; font-size: x-small;"> return scores, reward</span></div><div><span style="color: #274e13; font-size: x-small;"><br /></span></div><div><span style="color: #274e13; font-size: x-small;">state_size = 91 # 90일 이전 가격 + 현재 가격 상태 데이터 획득을 위한 크기 변수</span></div><div><span style="color: #274e13; font-size: x-small;">action_size = 3 # 보유 = 0 , 구매 = 1 , 판매 = 2</span></div><div><span style="color: #274e13; font-size: x-small;">agent = Agent(state_size, action_size, 99, False)</span></div><div><span style="color: #274e13; font-size: x-small;">env = StockMarketEnv(train)</span></div><div><span style="color: #274e13; font-size: x-small;"><br /></span></div><div><span style="color: #274e13; font-size: x-small;">start_time = time.time()</span></div><div><span style="color: #274e13; font-size: x-small;">episode_count = 50 # </span></div><div><span style="color: #274e13; font-size: x-small;">scores_dqn_base, reward = train_DQN(n_episodes=episode_count, pth_file='checkpoint_dqn.pth')</span></div><div><span style="color: #274e13; font-size: x-small;">print("Total run time to achieve average score : %s seconds " % (time.time() - start_time))</span></div><div><span style="color: #274e13; font-size: x-small;"><br /></span></div><div><span style="color: #274e13; font-size: x-small;"># plot results</span></div><div><div><span style="color: #274e13; font-size: x-small;">import matplotlib.pyplot as plt</span></div><div><span style="color: #274e13; font-size: x-small;">fig = plt.figure()</span></div><div><span style="color: #274e13; font-size: x-small;">ax = fig.add_subplot(111)</span></div><div><span style="color: #274e13; font-size: x-small;">plt.plot(np.arange(len(total_profits_history)), total_profits_history) # 이익</span></div><div><span style="color: #274e13; font-size: x-small;">plt.ylabel('Profit')</span></div><div><span style="color: #274e13; font-size: x-small;">plt.xlabel('Episode #')</span></div><div><span style="color: #274e13; font-size: x-small;">plt.title('DQN Reward Graph over Time for Profit')</span></div><div><span style="color: #274e13; font-size: x-small;">plt.show()</span></div><div><span style="color: #274e13; font-size: x-small;"><br /></span></div><div><span style="color: #274e13; font-size: x-small;">fig = plt.figure()</span></div><div><span style="color: #274e13; font-size: x-small;">ax = fig.add_subplot(111)</span></div><div><span style="color: #274e13; font-size: x-small;">plt.plot(np.arange(len(scores_dqn_base)), scores_dqn_base) # 개별 보상 스코어</span></div><div><span style="color: #274e13; font-size: x-small;">plt.ylabel('Score')</span></div><div><span style="color: #274e13; font-size: x-small;">plt.xlabel('Episode #')</span></div><div><span style="color: #274e13; font-size: x-small;">plt.title('DQN Reward Graph over Time for Stock Price')</span></div><div><span style="color: #274e13; font-size: x-small;">plt.show()</span></div><div><span style="color: #274e13; font-size: x-small;"><br /></span></div><div><span style="color: #274e13; font-size: x-small;">fig = plt.figure()</span></div><div><span style="color: #274e13; font-size: x-small;">ax = fig.add_subplot(111)</span></div><div><span style="color: #274e13; font-size: x-small;">plt.plot(np.arange(len(pd.Series(scores_dqn_base).rolling(10).mean())), pd.Series(scores_dqn_base).rolling(10).mean()) # 평균 보상 스코어</span></div><div><span style="color: #274e13; font-size: x-small;">plt.ylabel('Score')</span></div><div><span style="color: #274e13; font-size: x-small;">plt.xlabel('Episode #')</span></div><div><span style="color: #274e13; font-size: x-small;">plt.title('DQN Reward Graph over Time for Stock Price')</span></div><div><span style="color: #274e13; font-size: x-small;">plt.show()</span></div><div><span style="color: #274e13; font-size: x-small;">input()</span></div></div><div><br /></div></div><div>실행 결과는 다음과 같다. 보상이 최대값으로 해가 수렴하는 것을 볼 수 있다. </div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgZZiVekKU018UxP6t4besCoqblE0MuBs99h5v5WWf38y81blLGnYx1zTqWKVHMHDIyQXzRRgtbeSJqLiWJVX6j_3Xjc8imAvF9f5xcB_hE3PUsyU7QD98cDxV04lllr1fvqLW0s3Sutl__lKxW8sate-u5kFQDXyLg-YrplwVnv0JxtgaVmd7SksiPvwqO/s745/product5.JPG" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="588" data-original-width="745" height="180" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgZZiVekKU018UxP6t4besCoqblE0MuBs99h5v5WWf38y81blLGnYx1zTqWKVHMHDIyQXzRRgtbeSJqLiWJVX6j_3Xjc8imAvF9f5xcB_hE3PUsyU7QD98cDxV04lllr1fvqLW0s3Sutl__lKxW8sate-u5kFQDXyLg-YrplwVnv0JxtgaVmd7SksiPvwqO/w227-h180/product5.JPG" width="227" /></a><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiSU8isDz43myi7X39K2LBzVYvIXEVws0r1TfzX0OxsFjOneSVfMzAW4HySNJUIOe6TcH-o4uw52v9FvNgdqwwlwcSS-dVP55Oxzt4eewTGTxlm_6DxupOMfzAOPKpahdokFeZBW5AsZIzc7ESeUL3ZPMAipJNrY-GO89nLdpz5QEtg-5BVWfcx_CcuLsOQ/s807/profit2.JPG" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="565" data-original-width="807" height="181" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiSU8isDz43myi7X39K2LBzVYvIXEVws0r1TfzX0OxsFjOneSVfMzAW4HySNJUIOe6TcH-o4uw52v9FvNgdqwwlwcSS-dVP55Oxzt4eewTGTxlm_6DxupOMfzAOPKpahdokFeZBW5AsZIzc7ESeUL3ZPMAipJNrY-GO89nLdpz5QEtg-5BVWfcx_CcuLsOQ/w257-h181/profit2.JPG" width="257" /></a></div><div class="separator" style="clear: both; text-align: center;">보상 스코어 및 마켓 거래 총 이익 그래프</div><div><br /></div><div>사실, 대부분 시장 제품 거래(에너지, 상품, 무역, 물류 등)는 거의 구현방식이 유사하다. 주식 마켓 트레이딩 강화학습과 관련된 좀 더 상세한 내용은 다음을 참고한다. </div></div><div><ul style="text-align: left;"><li><a href="https://www.analyticsvidhya.com/blog/2020/10/reinforcement-learning-stock-price-prediction/">Predicting Stock Prices using Reinforcement Learning</a></li><li><a href="https://medium.com/@sthanikamsanthosh1994/custom-gym-environment-stock-trading-for-reinforcement-learning-stable-baseline3-629a489d462d">Custom Gym environment(Stock trading) for Reinforcement Learning</a></li><li><a href="https://tradingtechai.medium.com/building-an-algorithmic-trading-strategy-using-reinforcement-learning-4ab12488d190">Building an Algorithmic Trading Strategy using Reinforcement Learning</a></li><li><a href="https://medium.com/mlearning-ai/mastering-reinforcement-learning-with-stable-baselines-3-a-comprehensive-guide-fdd5de945b32">Mastering Reinforcement Learning with Stable Baselines 3: A Comprehensive Guide</a></li></ul></div><div><div><div>참고로, 주식 거래 이력 데이터는 다음 링크에서 다운로드할 수 있다.</div><div><ul style="text-align: left;"><li><a href="https://www.kaggle.com/datasets/paultimothymooney/stock-market-supplementary-data?resource=download">Stock Market Supplementary Data</a></li><li><a href="https://www.nasdaq.com/market-activity/stocks/tsla/historical">Tesla, Inc. (TSLA) Historical Data | Nasdaq</a></li></ul></div></div><div><br /></div></div><div><b><span style="font-size: medium;">마무리</span></b></div><div>지금까지 전체적으로 강화학습 개념, 발전역사, OpenAI의 gym을 사용한 강화학습 실험 및 직접 DQN의 딥러닝 에이전트를 개발하는 과정을 확인해 보았다. 이를 통해, 특정 게임과 같이 정책이 있는 환경은 보상 알고리즘을 설계한 후, 강화학습을 적용해, 용이하게 학습모델을 개발해 사용할 수 있다.</div><div><br /></div><div>좀 더 많은 강화학습 응용 예시는 부록을 참고하길 바란다.</div><div><br /></div><div><b><span style="font-size: medium;">레퍼런스</span></b></div></div></div></div><div style="text-align: left;"><ul style="text-align: left;"><li><a href="https://spinningup.openai.com/en/latest/">Welcome to Spinning Up in Deep RL! — Spinning Up documentation (openai.com)</a></li><li><a href="https://www.youtube.com/watch?v=D9sU1hLT0QY">Reinforcement Learning for Trading Tutorial | $GME RL Python Trading</a></li><li><a href="https://www.youtube.com/watch?v=bD6V3rcr_54">Building a Custom Environment for Deep Reinforcement Learning with OpenAI Gym and Python</a></li><li><a href="https://www.youtube.com/watch?v=cO5g5qLrLSo">Deep Reinforcement Learning Tutorial for Python in 20 Minutes</a></li><li><a href="https://www.youtube.com/watch?v=YLa_KkehvGw">Deep Reinforcement Learning with OpenAI Gym in Python</a></li><li><a href="https://www.youtube.com/watch?v=pYC2kpCTLK4">Deep Reinforcement Learning TO Predict Stock</a></li><li><a href="https://medium.com/analytics-vidhya/reinforcement-learning-to-reduce-building-energy-consumption-bb7e7ffa13b2">Reinforcement Learning to Reduce Building Energy Consumption | by Enrico Busto - EN | Analytics Vidhya | Medium</a></li><li><a href="https://courses.dibya.online/p/realdeeprl">Deep RL in the Real World | Dibya's School</a></li><li><a href="https://ml-jku.github.io/research/reinforcement_learning/">Institute for Machine Learning @ JKU | Reinforcement Learning (ml-jku.github.io)</a></li><li><a href="https://jerrickliu.com/2020-07-13-FourthPost/#:~:text=In%20PPO%2C%20recall%20that%20the,that%20maximizes%20the%20Q%20value.">DQN vs PPO. Discussion with my mentors (jerrickliu.com)</a></li><li><a href="https://www.pycodemates.com/2023/08/stock-trading-ai-using-deep-reinforcement-learning-python.html">Stock Trading AI Bot Using Python | ML Trading - PyCodeMates</a></li></ul><div><div><div>강화학습을 이용해 규칙과 보상이 있는 주식 트레이딩 모델을 개발할 수 있다. 상세 내용은 다음 자료를 참고 한다.</div><div><ul><li><a href="https://lixiaoguang.medium.com/training-dqn-models-for-trading-using-pytorch-and-stable-baselines3-ml4t-005-2c256373db7b">Training DQN models for trading using PyTorch and Stable-Baselines3 (drl4t-05) | by Xiaoguang Li | Medium</a></li><li><a href="https://towardsdatascience.com/creating-a-custom-openai-gym-environment-for-stock-trading-be532be3910e">Create custom gym environments from scratch — A stock market example | by Adam King | Towards Data Science</a></li></ul></div><div>도시 건물 에너지 관련 딥러닝 모델 개발 시 참고할만한 예시는 다음과 같다. </div><div><ul style="text-align: left;"><li><a href="https://www.sciencedirect.com/science/article/pii/S0378778823009982">Urban building energy performance prediction and retrofit analysis using data-driven machine learning approach - ScienceDirect</a></li><li><a href="https://link.springer.com/article/10.1007/s42452-023-05424-6">Energy consumption forecast in peer to peer energy trading | Discover Applied Sciences</a></li><li><a href="https://www.tandfonline.com/doi/full/10.1080/13467581.2023.2223587">Full article: Implementing a web-based optimized artificial intelligence system with metaheuristic optimization for improving building energy performance</a></li><li><a href="https://williamkoehrsen.medium.com/building-energy-data-analysis-part-one-8d7ccc601645">Building Energy Data Analysis Part One | by Will Koehrsen | Medium</a></li><li><a href="https://www.nature.com/articles/s41597-022-01257-x">A three-year dataset supporting research on building energy management and occupancy analytics | Scientific Data</a></li><li><a href="https://datadryad.org/stash/dataset/doi:10.7941/D1N33Q">A three-year building operational performance dataset for informing energy efficiency</a></li></ul><div>도시 건물 에너지 및 운영 데이터셋은 다음을 참고한다.</div><ul style="text-align: left;"><li><a href="https://github.com/tohid-yousefi/Predicting_the_Energy_Efficiency_of_Buildings/tree/main">Predicting_the_Energy_Efficiency_of_Buildings: In this section, predicting the energy efficiency of buildings with machine learning algorithms</a></li><li><a href="https://bpd.lbl.gov/explore">Building Performance Database (lbl.gov)</a></li><li><a href="https://buildings.lbl.gov/tools-guides">Tools & Guides | Building Technology and Urban Systems (lbl.gov)</a></li><li><a href="http://trynthink.github.io/buildingsdatasets/">Buildings Datasets (trynthink.github.io)</a></li><li><a href="https://data.openei.org/submissions/2977">OEDI: AlphaBuilding - Synthetic Buildings Operation Dataset (openei.org)</a></li><li><a href="https://www.nature.com/sdata/">Scientific Data (nature.com)</a></li><li><a href="https://lbnl-eta.github.io/AlphaBuilding-SyntheticDataset/">A Synthetic Building Operation Dataset | AlphaBuilding-SyntheticDataset (lbnl-eta.github.io)</a></li><li><a href="https://www.sciencedirect.com/science/article/pii/S2352340923003360">Performance dataset on a nearly zero-energy office building in temperate oceanic climate based on field measurements - ScienceDirect</a></li><li><a href="https://dataverse.harvard.edu/dataset.xhtml?persistentId=doi%3A10.7910%2FDVN%2FNLEAKA">Field measurement dataset of a nearly zero-energy office building in temperate oceanic climate - Harvard Dataverse</a></li><li><a href="https://github.com/NREL/BuildingsBench">NREL/BuildingsBench: Large-scale pretraining and benchmarking for short-term load forecasting. (github.com)</a></li><li><a href="https://data.mendeley.com/">Mendeley Data</a></li><li><a href="https://www.osti.gov/biblio/1775065">Building Performance Database API (BPD API) v2.1 (Software) | OSTI.GOV</a></li><li><a href="https://link.springer.com/article/10.1007/s12273-022-0925-9">ROBOD, room-level occupancy and building operation dataset | Building Simulation (springer.com)</a></li><li><a href="https://www.kaggle.com/datasets/elikplim/eergy-efficiency-dataset">Energy Efficiency Dataset (kaggle.com)</a></li><li><a href="https://nicholasinstitute.duke.edu/project/energy-data-resources">Energy Data Resources | The Nicholas Institute for Energy, Environment & Sustainability (duke.edu)</a></li><li><a href="https://opendata.dc.gov/datasets/DCGIS::building-energy-performance/about">Building Energy Performance | Open Data DC</a></li><li><a href="https://www.buildingsiot.com/blog/what-big-data-can-do-for-building-energy-management-bd">Big Data for Building Energy Management - What Can it Do? (buildingsiot.com)</a></li><li><a href="https://opendata.dc.gov/datasets/DCGIS::building-energy-performance/about">Building Energy Performance | Open Data DC</a></li><li><a href="https://github.com/samy101/lead-dataset/tree/main">samy101/lead-dataset: A Large-scale annotated dataset for Energy Anomaly Detection in commercial buildings (github.com)</a></li><li><a href="https://energy.acm.org/resources/">Resources - SIGENERGY (acm.org)</a></li><li><a href="https://www.ioes.ucla.edu/project/ucla-engage-building-performance-dataset/">UCLA Engage Building Performance Dataset — Institute of the Environment and Sustainability at UCLA</a></li></ul></div><div><b>부록: 건물 도시 에너지 활용 최적화를 위한 강화학습 도구</b></div><div>건물 도시 에너지 활용 최적화를 위한 강화학습 도구도 개발되어 있다. 이 경우, 정책에 대한 보상과 관찰 데이터는 보통 다음과 같다. </div><div><br /></div><div>Observation data</div></div></div></div><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><div><div><div><div>energy_per_price(kW)</div></div></div></div></div></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px;"><div style="text-align: left;"><div><div><div style="text-align: left;">outdoor_temp</div></div></div></div></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px;"><div style="text-align: left;"><div><div><div style="text-align: left;">comfort_temp_max</div></div></div></div></blockquote><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px;"><div style="text-align: left;"><div><div><div style="text-align: left;">comfort_temp_min</div></div></div></div></blockquote><div style="text-align: left;"><div><div><div><br /></div></div><div>Policy</div></div></div><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px;"><div style="text-align: left;"><div><div><div style="text-align: left;">reward = comfort + 1 / device_switching_cost - energy_cost</div></div></div></div></blockquote><div style="text-align: left;"><div><div><div><br /></div></div><div>이에 관심이 있다면, 다음 링크를 참고한다. </div><div><ul><li><a href="https://github.com/intelligent-environments-lab/CityLearn">CityLearn: Official reinforcement learning environment for demand response and load shaping</a></li><li><a href="https://github.com/tobirohrer/building-energy-storage-simulation">building-energy-storage-simulation: An open source playground energy storage environment to explore reinforcement learning and model predictive control</a></li><li><a href="https://github.com/bsl546/energym">Energym is an open source building simulation library designed to test climate control and energy management strategies on buildings in a systematic and reproducible way</a></li><li><a href="https://github.com/erickCantu/TheGreenCitySolutionsGroup">TheGreenCitySolutionsGroup: Forecasting building energy demand through time series analysis and machine learning. (github.com)</a></li><li><a href="https://github.com/tkcoding/Stock_DRL">Stock_DRL: Using Deep reinforcement learning (DRL) agent to learn from historical data</a></li><li><a href="https://ember-climate.org/data-catalogue/european-wholesale-electricity-price-data/">European wholesale electricity price data | Ember</a></li><li><a href="https://data.open-power-system-data.org/time_series/">Data Platform – Open Power System Data</a></li></ul><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEheX3YdV3dGarBYZufUu0jHy6n94pgE6DUEUWz08IDMLQFPE5_rxc1ECtxjJ_8KKXxPm8HcIqo1Ji7ZAWe7jmsKUiUVI8ryOnGd7skrgOivYFOGqQxLryXWkeEjfszqpEnULvXrmm-6w6lF7infRhbMjnh0r4zscabbsSHBlkLi8cuHnsnLGXeC8i8sXbwt" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="873" data-original-width="1777" height="196" src="https://blogger.googleusercontent.com/img/a/AVvXsEheX3YdV3dGarBYZufUu0jHy6n94pgE6DUEUWz08IDMLQFPE5_rxc1ECtxjJ_8KKXxPm8HcIqo1Ji7ZAWe7jmsKUiUVI8ryOnGd7skrgOivYFOGqQxLryXWkeEjfszqpEnULvXrmm-6w6lF7infRhbMjnh0r4zscabbsSHBlkLi8cuHnsnLGXeC8i8sXbwt=w400-h196" width="400" /></a></div><div class="separator" style="clear: both; text-align: center;"><a href="https://github.com/intelligent-environments-lab/CityLearn">CityLearn</a></div><div><br /></div></div><div><b>부록: 거주환경 쾌적성 지속을 위한 강화학습 예시</b></div><ul><li><a href="https://github.com/arunajit/drl">Implementation of Research paper, Deep reinforcement learning based framework for energy optimization and thermal comfort control in smart buildings</a></li><li><a href="https://github.com/dnlmrtn/hvac-rl">hvac-rl: Testing environment for Reinforcement Learning Solutions in HVAC deployment</a></li></ul><div><div><b>부록: 배터리 운영 최적화를 위한 강화학습 예시</b></div><div>BEMS(Building Energy Management System), HEMS (Home EMS), CEMS(Community EMS)와 같은 배터리 충방전을 효과적으로 사용하는 운영모델을 개발할 수 있다. 이 경우, 관찰 데이터는 보통 다음과 같다. </div><div><br /></div></div></div></div></div><blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;"><div style="text-align: left;"><div><div><div><div>전력비용</div></div></div></div></div><div style="text-align: left;"><div><div><div><div>SOC 충전상태</div></div></div></div></div><div style="text-align: left;"><div><div><div><div>부하에 따른 장비 전력 소모</div></div></div></div></div></blockquote><p>관련 자료는 다음과 같다. </p><div style="text-align: left;"><div><div><div><ul></ul></div><ul><li><a href="https://github.com/danbolinson/BatteryAgent">BatteryAgent: Reinforcement learning approach to battery dispatch</a></li><li><a href="https://github.com/Carterbouley/Batteries-RL-optimization">Batteries-RL-optimization: Operation optimization of electric batteries via reinforcement learning</a></li><li><a href="https://github.com/Anon75014/ReinforcementLearning/tree/master">RL project for battery trading on the day-ahead market</a></li></ul></div><div><div class="separator" style="clear: both; text-align: center;"><br /></div><b>부록: DQN기반 주식 트레이딩 최적화를 위한 강화학습 예시<br /></b><div><ul><li><a href="https://lixiaoguang.medium.com/training-dqn-models-for-trading-using-pytorch-and-stable-baselines3-ml4t-005-2c256373db7b">Training DQN models for trading using PyTorch and Stable-Baselines3 (drl4t-05) by Xiaoguang Li</a></li><li><a href="https://medium.com/@abhishek-maheshwarappa/reinforcement-learning-for-stock-trading-d4e487b1b280">Reinforcement Learning for Stock Trading</a></li><li><a href="https://www.ijcai.org/proceedings/2020/640">Hierarchical Multi-Scale Gaussian Transformer for Stock Movement Prediction | IJCAI</a></li></ul></div><div></div></div></div><div><b><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEiS4JpFZZVp_0M_cr2pyxi3-9yZET5_iq0yboXIfFCrTUJCoTETVS_cO8gSoKufZdoidtAAYtUavcOGhFzlGZRs-dIbYIzka_nkVw43tpV0veqyRFgKkJMbv2yTAYsGp4TVes2NUV0Ie6Mn4FYpAFglszOY7lBKZFQQlKH0vr-AKYA5dGebze-8Dui_fA6C" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="285" data-original-width="392" height="233" src="https://blogger.googleusercontent.com/img/a/AVvXsEiS4JpFZZVp_0M_cr2pyxi3-9yZET5_iq0yboXIfFCrTUJCoTETVS_cO8gSoKufZdoidtAAYtUavcOGhFzlGZRs-dIbYIzka_nkVw43tpV0veqyRFgKkJMbv2yTAYsGp4TVes2NUV0Ie6Mn4FYpAFglszOY7lBKZFQQlKH0vr-AKYA5dGebze-8Dui_fA6C" width="320" /></a></div><br /></b></div><div><div><b>부록. 파이토치 기반 딥러닝 강화학습 개발</b></div><div>파이토치 기반으로 개발하고자 하면, 다음 링크를 참고한다.</div><div><ul><li><a href="https://pytorch.org/tutorials/intermediate/reinforcement_q_learning.html">Reinforcement Learning (DQN) Tutorial — PyTorch Tutorials 2.2.0+cu121 documentation</a></li><li><a href="https://blog.gofynd.com/building-a-deep-q-network-in-pytorch-fa1086aa5435">Building a DQN in PyTorch: Balancing Cart Pole with Deep RL | by Mohit Pilkhan | Building Fynd (gofynd.com)</a></li></ul></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgZnZhwlODZqvmI82q-KlfG6hJbIzEA7MAbN4cFAz7sPxvS1jX47kbjfC028LXEx_fXyXZlP2oVxAk5-8NdWE2cA4EF7ECoFAxK_nhrCPXBbTqwMxUQul624uy7TNp5QRspsCHB_U9YmMnwuHOOAJo8Jz6Uiq749zTuBVSlg6c8PBXG3tqGiVa2jfKszRK4" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="691" data-original-width="879" height="240" src="https://blogger.googleusercontent.com/img/a/AVvXsEgZnZhwlODZqvmI82q-KlfG6hJbIzEA7MAbN4cFAz7sPxvS1jX47kbjfC028LXEx_fXyXZlP2oVxAk5-8NdWE2cA4EF7ECoFAxK_nhrCPXBbTqwMxUQul624uy7TNp5QRspsCHB_U9YmMnwuHOOAJo8Jz6Uiq749zTuBVSlg6c8PBXG3tqGiVa2jfKszRK4" width="305" /></a></div></div><div class="separator" style="clear: both; text-align: center;">각 페이소드 당 최대 보상을 얻을 때 타임스텝 수(시간이 지날수록 타임스텝이 적더라도 최대보상을 얻는 경우가 많아짐)</div><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: left;"><b>부록. 기타 응용 별 딥러닝 강화학습 예제</b></div><div class="separator" style="clear: both; text-align: left;">기타 다른 응용 별 딥러닝 강화학습 예제는 다음 링크를 참고한다.</div><div class="separator" style="clear: both; text-align: left;"><ul style="text-align: left;"><li><a href="https://www.kaggle.com/code/franoisgeorgesjulien/deep-reinforcement-learning-for-trading/input">Deep Reinforcement Learning for Trading</a></li><li><a href="https://blog.griddynamics.com/building-a-next-best-action-model-using-reinforcement-learning/">Next Best Action Model And Reinforcement Learning</a></li><li><a href="https://www.kaggle.com/datasets/alincijov/trading/code">Reinforcement Learning Stock Trading</a></li></ul></div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;"><b>부록. 사용자 Custom Enviroment 개발 방법</b></div><div class="separator" style="clear: both; text-align: left;"><ul style="text-align: left;"><li><a href="https://blog.paperspace.com/creating-custom-environments-openai-gym/">Creating Custom Environments in OpenAI Gym | Paperspace Blog</a></li><li><a href="https://docs.omniverse.nvidia.com/isaacsim/latest/isaac_gym_tutorials/tutorial_advanced_rl_stable_baselines.html">Reinforcement Learning using Stable Baselines — Omniverse IsaacSim latest documentation (nvidia.com)</a></li><li><a href="https://gym-trading-env.readthedocs.io/en/latest/">Gym Trading Environment (gym-trading-env.readthedocs.io)</a></li><li><a href="https://www.youtube.com/watch?v=re9zxrJ4Y-M">How to create a custom Open-AI Gym environment? with codes and example</a></li><li><a href="https://medium.com/@samuelerickson977/creating-an-automated-stock-trading-system-using-reinforcement-learning-e1dbe1a305fd">Creating an Automated Stock Trading System using Reinforcement Learning | by Sam Erickson | Dec, 2023 | Medium</a></li></ul></div><div class="separator" style="clear: both; text-align: left;"><br /></div><div><br /></div></div></div>Daddy Makerhttp://www.blogger.com/profile/02275758703365576780noreply@blogger.com0tag:blogger.com,1999:blog-5201956450461596914.post-83866173611910871102023-12-25T20:28:00.000-08:002024-02-05T23:50:16.132-08:00딥러닝 모델 트랜스포머 인코더 핵심 코드 구현을 통한 동작 메커니즘 이해하기<div style="text-align: left;"><div>이 글은 생성AI의 핵심인 딥러닝 모델 트랜스포머(Transformer) 구현 메커니즘을 코드 수준에서 이해하기 위해, 트랜스포머의 작동 과정을 상세히 설명합니다. 그리고, 트랜스포머를 실제로 동작하는 코드를 예시하고 실행해 봅니다.</div><div><br /></div><div>이 과정을 통해, 텍스트 번역기 등 앞뒤 문맥 관계가 있는 딥러닝 모델을 직접 개발하거나, 이와 유사한 멀티모델 데이터 스트림을 다른 형식으로 변환(트랜스폼)시킬 수 있는 스테이블 디퓨전(Stable Diffusion)같은 생성AI 모델을 개발할 수 있습니다. 실제, 트랜스포머는 텍스트에서 비전, 음성, 비디오 데이터로 맵핑하는 주요 컴포넌트 중 하나로 사용된다. </div><div><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEiHDWtufVWWSfVDd3xutxQSXDhWX-dUflFU3cDTxnQqwdJV2Mh8LxHDkN0d-zfSeKUgjjXaexc4cBfnCn66eCBx19cw-SSWq12ID2N1EE5G63t62W1IzxWcvQ13_YANrvzRVmP9iNUomlIDXgUD48dkaxuR3-zKUivhWSuqmU-hjv_KdVtHerhIBa67bRUl" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="978" data-original-width="1458" height="269" src="https://blogger.googleusercontent.com/img/a/AVvXsEiHDWtufVWWSfVDd3xutxQSXDhWX-dUflFU3cDTxnQqwdJV2Mh8LxHDkN0d-zfSeKUgjjXaexc4cBfnCn66eCBx19cw-SSWq12ID2N1EE5G63t62W1IzxWcvQ13_YANrvzRVmP9iNUomlIDXgUD48dkaxuR3-zKUivhWSuqmU-hjv_KdVtHerhIBa67bRUl=w400-h269" width="400" /></a></div></div><div class="separator" style="clear: both; text-align: center;">트랜스포머 인코더-디코더 모델 적용된 생성 AI 멀티모달 Stable Diffusion 모델(<a href="https://synthesis.ai/2023/01/05/modern-generative-ai-an-overview/">Generative AI Models in Image Generation: Overview - Synthesis AI</a>)</div></div><br /></div><div>관련 내용이 좀 많아, 글을 인코더 부분과 <a href="https://daddynkidsmakers.blogspot.com/2024/02/blog-post.html">디코더 부분</a>(<a href="https://daddynkidsmakers.blogspot.com/2024/02/blog-post.html">여기 참고</a>) 크게 두 개로 나누어 진행하도록 한다. 이 글은 트랜스포머 인코더 구현 방법에 대한 글이다. </div><div><br /></div><div>이 글에서 표시된 트랜스포머 내부 실행 소스 코드는 다음 링크에서 다운로드할 수 있다. </div><div><ul><li>Github - <a href="https://github.com/mac999/transformer">transformer (github.com)</a></li></ul></div><div>딥러닝 및 컴퓨터 비전에 대한 개념은 다음 링크를 참고한다. </div><div><ul style="text-align: left;"><li><a href="https://daddynkidsmakers.blogspot.com/2017/02/blog-post_24.html">머신러닝 딥러닝 신경망 개념, 종류 및 개발</a></li><li><a href="https://daddynkidsmakers.blogspot.com/2022/11/3.html">딥러닝 기반 3차원 스캔 데이터, 포인트 클라우드 학습</a></li><li><a href="https://daddynkidsmakers.blogspot.com/2023/03/generative-ai.html">생성(Generative) AI 오픈소스 딥러닝 모델 Stable Diffusion, ControlNet 개념 및 ComfyUI 사용법</a></li><li><a href="https://github.com/mac999/computer_vision_deeplearning/tree/main">Computer_vision_deeplearning: computer vision based on deep learning lecture materials, github</a></li></ul><div>이 글은 많은 레퍼런스들을 참고해, 가능한 트랜스포머 동작방식을 이해하기 쉽도록 정리한 것이다. 관련해 궁금하다면, 이 글 마지막에 있는 레퍼런스들을 살펴보길 바란다. </div></div><div><br /></div><div><b><span style="font-size: medium;">머리말</span></b></div><div>트랜스포머는 구글 연구팀이 구글 번역기 성능 개선을 위해 개발한 딥러닝 기술로써 <a href="https://arxiv.org/abs/1706.03762">Attention Is All You Need</a> 논문을 통해 널리 알려진 딥러닝 모델이다. 트랜스포머는 입력 데이터 토큰들과 출력 데이터 토큰들 간의 순서 관계를 통해, 서로의 유사성을 계산한다(예. 영어 문장 > 독일어 문장). </div><div style="text-align: center;"><br /></div><div>이 모델은 특정 토큰 다음에 어떤 토큰이 올 지에 확률적으로 초점을 맞추는 어텐션 개념을 수학적 모델로 제안하였다. 어텐션은 우리가 말을 하면서, 특정 정보에만 집중하는 것을 흉내낸다. </div><div><br /></div><div>트랜스포머는 순서가 있는 데이터 토큰의 예측, 생성, 비교 과정을 수학적으로 일반화한 확률 통계 모델이므로, 데이터 종류에 구애받지 않는다. 이 특징을 활용해, 트랜스포머는 멀티모델 생성AI 기술로 사용될 수 있었다.</div><div><br /></div><div>트랜스포머는 다음과 같은 기존 RNN(Recurrent Neural Network), LSTM(Long Short Term Memory)와 같은 Seq2Seq 모델들의 단점을 해결하고자 했다. Seq2Seq 모델의 구조는 일반적으로 다음과 같다.</div><div><ul><li>인코더 인닉상태 가중치 초기화</li><li>모델에 매 시점마다 단어 토큰과 다음 토큰이 라벨링으로 입력 학습, 은닉 상태 갱신</li><li>앞의 학습 단계 계속 반복</li><li>최종 학습 시점의 인코더 은닉 상태인 문맥 벡터(Context vector)를 얻어, 디코더에 설정</li><li>디코더가 새로운 단어 토큰들을 입력받아, 문맥 벡터로 계산해, 출력을 생성함</li></ul><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhJQsB0Kn54PoAymfhV8pvMj3EdO-0AlT02nXUxbWG_Psj54JPv1JeHf58i4ebvw7U2LzJpWzQvbseh0T9ysgxBWpJ5HxMAizd2J0omVxbA5o6tQrhn6KBWnZYltAPPWzekno3b4Rb9NAT3AC3qGx8cOmsS3NsuDqtBE5EcbKfOsYMwHIMNE-xhIO5jkJ01" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="356" data-original-width="873" height="163" src="https://blogger.googleusercontent.com/img/a/AVvXsEhJQsB0Kn54PoAymfhV8pvMj3EdO-0AlT02nXUxbWG_Psj54JPv1JeHf58i4ebvw7U2LzJpWzQvbseh0T9ysgxBWpJ5HxMAizd2J0omVxbA5o6tQrhn6KBWnZYltAPPWzekno3b4Rb9NAT3AC3qGx8cOmsS3NsuDqtBE5EcbKfOsYMwHIMNE-xhIO5jkJ01=w400-h163" width="400" /></a></div><div class="separator" style="clear: both; text-align: center;">RNN(순환신경망) 학습 계산 개념도 (<a href="https://www.analyticsvidhya.com/blog/2019/01/fundamentals-deep-learning-recurrent-neural-networks-scratch-python/" style="text-align: left;">Build RNN model</a>)</div><br /></div><div>Seq2Seq 모델은 태생적으로 다음과 같은 한계가 있다.</div></div><div><ul style="text-align: left;"><li>재귀적으로 학습하므로 계산 속도가 느리다. 각 시점 별로 계산된 은닉 상태를 메모리로 가지고 있어, 역전파 시 메모리를 읽어 Loss를 감소시키는 방향으로 가중치를 수정해야 한다. 입력된 데이터를 병렬로 학습할 수 없을까?</li><li>재귀적으로 학습하므로, 현재보다 먼 앞의 시점에서 학습된 가중치가 뒤에서 학습된 결과에 의해 희미해질 수 있다. 이런 이유로, 데이터 토큰의 전체 관계를 학습하기 어렵다. 학습 시 전체 데이터를 볼 수 없을까?</li></ul></div><div>트랜스포머는 이 문제를 포지션 인코딩, 멀티헤드 어텐션으로 해결한다. 다음은 Google 연구팀이 펴낸 논문에 기술된 트랜스포머 개념도를 보여준다. </div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEivaZUSqlUqCnUlA9jr5VsIiUDz0-Kl8fG_X4F8DTHeH7zbZ64HOqr3ZRPRSka0wi8zf7ixpdN3YXiw1PAk85H5xBKDxqJZE1vhOX4mEYo5LQuuqVqTG2cALwQziGfk7fGADFN8r-K6Q0SYFMmfU400sV8X52yenRsiYMdL5KyDgMWMxiZyKDkWVRQxJMR4" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="702" data-original-width="1240" height="309" src="https://blogger.googleusercontent.com/img/a/AVvXsEivaZUSqlUqCnUlA9jr5VsIiUDz0-Kl8fG_X4F8DTHeH7zbZ64HOqr3ZRPRSka0wi8zf7ixpdN3YXiw1PAk85H5xBKDxqJZE1vhOX4mEYo5LQuuqVqTG2cALwQziGfk7fGADFN8r-K6Q0SYFMmfU400sV8X52yenRsiYMdL5KyDgMWMxiZyKDkWVRQxJMR4=w546-h309" width="546" /></a></div><div class="separator" style="clear: both; text-align: center;">트랜스포머 구성요소(<a href="https://deepfrench.gitlab.io/deep-learning-project/" style="text-align: left;">The Attention Mechanism and the Transformer Model</a>)</div><div style="text-align: center;"><br /></div></div><div>이 그림을 보면, 트랜스포머 모델은 Seq2Seq와 유사하게, 인코딩과 디코딩 네트웍으로 나눠진다. 영어 > 프랑스어 다국어 번역의 경우, 트랜스포머의 인코더는 영어의 문맥을 학습하고, 디코더는 번역될 프랑스어 텍스트를 입력받아 인코더에서 학습된 문맥을 바탕으로 프랑스어를 생성하는 역할을 한다. </div><div><div style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjyKZFt01N66hg_5CO0W625awCI0w9h-zv_FZg4wPFnzcOlZ84svJ1JXHFfIyrhWWU9P7gCXt12y_nrlGWUjGM4mSatY8qVTjKbKSzo8cu1iqsBjF4Bsu8W5xEiKOtKHxCpBPiJYK1xDKeykaCLb1U1BXuWP-LvH43iuIi9m4bo4fQCY7AYdFgRGsptn6n4" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="278" data-original-width="688" height="129" src="https://blogger.googleusercontent.com/img/a/AVvXsEjyKZFt01N66hg_5CO0W625awCI0w9h-zv_FZg4wPFnzcOlZ84svJ1JXHFfIyrhWWU9P7gCXt12y_nrlGWUjGM4mSatY8qVTjKbKSzo8cu1iqsBjF4Bsu8W5xEiKOtKHxCpBPiJYK1xDKeykaCLb1U1BXuWP-LvH43iuIi9m4bo4fQCY7AYdFgRGsptn6n4" width="320" /></a></div><div style="text-align: center;">자연어 번역 시 인코딩 디코딩 과정 예시 (영어 > 독일어 번역)</div><div style="text-align: center;"><br /></div></div><div>인코딩의 각 단계를 간략히 확인해 보자. </div><div><ol style="text-align: left;"><li>Input Embedding: 입력 데이터를 토큰으로 구분하고 임베딩한다. 이를 통해, 비정형적인 텍스트를 디지털 계산이 가능한 숫자로 표현된 고유한 임베딩 벡터값으로 표현한다.</li><li>Positional Encoding: 변환된 임베딩 벡터는 앞뒤 순서가 포함되어 있지 않다. 하지만, 문장을 구성하는 단어는 순서가 있으며, 이 순서에 따라, 따라올 다음 단어가 결정된다. 그러므로, 단어의 순서를 표현하는 위치 벡터를 계산해, 임베딩 벡터에 더해준다.</li><li>Multi-Head Attention: 앞에서 특정 단어 A가 존재할 때, 그 다음 특정 단어 B가 존재할 확률을 계산한다. 이 확률은 트랜스포머 모델이 A가 여러 단어 중 B를 선택(어텐션. Attention)하는 방법을 알려준다. 이를 위해, 앞에서 입력된 인코딩 벡터를 8개(구글 논문에서 사용한 숫자)의 Multi-Head 벡터로 나눈다. 이를 다시 Query, Key, Value (QKV)벡터로 구분해 사용한다. 여기서, Query는 주의를 기울이고 싶은 것(검색어. 디코더의 출력값), 검색 결과는 Value, Key는 Query와 함께 생성될 토큰의 확률을 계산할 때 제공되는 벡터값이다. Query와 Key는 Value값을 얻도록 벡터 간 유사도를 계산해야한다. 이는 코사인(cosine) 벡터 유사도 함수 계산으로 해결한다. 이는 어텐션 수식으로 표현되며, 이후에 자세히 설명할 것이다.</li><li>Add & Normal: 신경망 역전파 시 그레디언트 감쇄 문제를 해결하기 위해, 잔차 연결과 정규화를 수행한다. </li><li>Forward Feedback: 단순히 포워드 신경망을 연결해, 가중치를 계산한다. </li><li>Add & Normal: 4번 단계와 동일하다.</li><li>최종 출력은 Key, Value로써 디코더의 Multi-Head Attention에 입력한다. Q는 디코더 결과값으로 입력된다. </li></ol></div><div><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEj2EkZnKOlXLNGysxW2f_NWj3kN8CF0bWuGRKASJKdPWZ3VgbogU58rvnok8Xcc0vJUe6QhxWAHjkhl01f0UkkcjzfWxmQwd1R0GpU0T9XtmCRTMrH3aFuyrL62HhnahEPxJyD6X64t1iLl3t1Vmo0juK7l3ZRqnkTOW7qdONltWYg52fRI6gjzv-f-XPaJ" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="450" data-original-width="800" height="315" src="https://blogger.googleusercontent.com/img/a/AVvXsEj2EkZnKOlXLNGysxW2f_NWj3kN8CF0bWuGRKASJKdPWZ3VgbogU58rvnok8Xcc0vJUe6QhxWAHjkhl01f0UkkcjzfWxmQwd1R0GpU0T9XtmCRTMrH3aFuyrL62HhnahEPxJyD6X64t1iLl3t1Vmo0juK7l3ZRqnkTOW7qdONltWYg52fRI6gjzv-f-XPaJ=w559-h315" width="559" /></a></div><div class="separator" style="clear: both;">트랜스포머 QKV 벡터 계산 개념도(<a href="https://theaisummer.com/transformer/">AI Summer</a>)</div></div><div><br /></div></div><div>디코딩 처리 단계는 번역할 문장(예. 독일어)의 단어 토큰을 오른쪽으로 이동(shifted right)한 후, 임베딩과 유사하게 수행된다.</div><div><ol style="text-align: left;"><li>Output Embedding: 입력 데이터를 토큰으로 구분하고 임베딩한다. </li><li>Positional Encoding: 단어의 순서를 표현하는 위치를 인코딩해, 임베딩 벡터에 포함해준다.</li><li>Masked Multi-Head Attention: 디코딩에서는 다음 단어가 예측되도록, 앞의 단어 임베딩 벡터에 해당하는 계산은 Mask 처리해야 한다. 입력된 인코딩 벡터를 8개의 Multi-Head 벡터로 나누고, 다시 Query, Key, Value 벡터로 나누어, 어텐션을 코사인 벡터 유사도 함수 계산으로 해결한다. </li><li>Add & Normal: 잔차 연결과 정규화를 수행한다. </li><li>Multi-Head Attention: 인코더 출력은 Key, Value 값으로 디코더 Multi-Head Attention에 입력한다. Q는 앞에서 출력을 입력한다. </li><li>Add & Normal: 잔차 연결과 정규화를 수행한다. </li><li>Forward Feedback: 포워드 신경망을 연결해, 가중치를 계산한다. </li><li>Add & Normal: 6번 단계와 동일하다.</li><li>Linear: 선형 레이어로 가중치를 계산한다. </li><li>Softmax: 과거 토큰 A에 대한 미래 토큰 B의 확률을 얻기 위해, softmax를 적용한다.</li><li>단어 예측 결과를 출력한다.</li></ol></div></div><div style="text-align: left;"><div style="text-align: left;"><div>다음은 이 과정을 좀 더 자세히 살펴보기 위해 코드 개발 수준에서 살펴본다. </div></div><div style="text-align: left;"><br /></div><div style="text-align: left;"><span style="font-size: medium;"><b>데이터 </b></span><b style="font-size: large;">인코더 </b><b style="font-size: large;">처리 메커니즘</b></div><div style="text-align: left;"><b>Input Embedding</b></div><div style="text-align: left;">트랜스포머는 주어진 데이터를 토큰으로 분할해, 다음 토큰이 출현하는 확률을 계산한다. 예를 들어, 'I like my cats'라는 문장이 있다면 다음과 같이 토큰으로 이를 분할한다.</div><div style="text-align: left;"><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEiOD0rRvG0cCOPTO9i1mzmxf5r_ozspUKWwabLKRcP0FmN026Sdt98IkA-lNrXpgYtVEj3WHJfNUZYoIQzurRAR_LJ9TAwAJgD4OJvMh0ItghEDHgg3076yfrL4bYZnNOfeNr80JN6hP_b9YRl0JCysa2InrjADYL3uaL9zqY5bZifddwHOzakJcbLUldJW" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="151" data-original-width="604" height="70" src="https://blogger.googleusercontent.com/img/a/AVvXsEiOD0rRvG0cCOPTO9i1mzmxf5r_ozspUKWwabLKRcP0FmN026Sdt98IkA-lNrXpgYtVEj3WHJfNUZYoIQzurRAR_LJ9TAwAJgD4OJvMh0ItghEDHgg3076yfrL4bYZnNOfeNr80JN6hP_b9YRl0JCysa2InrjADYL3uaL9zqY5bZifddwHOzakJcbLUldJW=w281-h70" width="281" /></a></div></div></div></div><div style="text-align: left;">토큰화된 후 다차원 기하학적 공간에 각 토큰을 투영한다. 이를 임베딩(<a href="https://medium.com/deeper-learning/glossary-of-deep-learning-word-embedding-f90c3cec34ca">embedding</a>)이라 한다. 임베딩은 텍스트를 숫자 벡터로 변환하는 방법이다. </div><div style="text-align: left;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhHb38TZ5s5_nxoWRH5ZolSH0GTgH9VjYUem03fFoFxcCRe7jheAFKtlUDeaJ1bL3SbyeTYa71HFXqqp0lzWVTyz5n0g6mbpbzI1sCc5EUjH2okhvGnI6U8VeRE0xXRDYfA-wlUnCkY73Sn1L_qOrgqc9d4h1rVuxh2R3Qrf0ej3uArPXQ4adLifgB8ZSHO" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="111" data-original-width="447" height="52" src="https://blogger.googleusercontent.com/img/a/AVvXsEhHb38TZ5s5_nxoWRH5ZolSH0GTgH9VjYUem03fFoFxcCRe7jheAFKtlUDeaJ1bL3SbyeTYa71HFXqqp0lzWVTyz5n0g6mbpbzI1sCc5EUjH2okhvGnI6U8VeRE0xXRDYfA-wlUnCkY73Sn1L_qOrgqc9d4h1rVuxh2R3Qrf0ej3uArPXQ4adLifgB8ZSHO=w207-h52" width="207" /></a></div></div><div style="text-align: left;">임베딩은 주어진 문자열을 큰 벡터 크기로 표현하는 one-hot 인코딩 벡터 방식보다 작고 효율적인 벡터를 만드는 워드 임베딩(Word Embedding)을 사용한다. 이 임베딩 방식은 주어진 문장을 계산, 비교하기 편리하다(입력 데이터 특성에 따라 임베딩 방식은 달라질 수 있음). 워드 임베딩은 word2vec로 알려져 있다. 이는 문장을 구성하는 각 단어를 one-hot 인코딩 벡터로 입력하고, 출력을 각 단어의 위치로 라벨링한 간단한 신경망을 사용한다. 이 신경망의 결과값이 임베딩 벡터가 된다. </div><div style="text-align: left;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEiCi4snz8aoj2HHF9neDwgKi2uKdg7B9qzObw4Qj4TynB7iIp4_4s0e0JbGBvFKqdl1qmnhFOCBv_A5Cqhf8Bt7lodhGjIluPnvqV0xLR64eCvC7QTxf4yK8qmtQIY75iRlJPzm87OdPuXZ2beVHWlIPABUUnQYeTG2P8krhHUuWan6J6lCaTBxdc17feW5" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="450" data-original-width="720" height="245" src="https://blogger.googleusercontent.com/img/a/AVvXsEiCi4snz8aoj2HHF9neDwgKi2uKdg7B9qzObw4Qj4TynB7iIp4_4s0e0JbGBvFKqdl1qmnhFOCBv_A5Cqhf8Bt7lodhGjIluPnvqV0xLR64eCvC7QTxf4yK8qmtQIY75iRlJPzm87OdPuXZ2beVHWlIPABUUnQYeTG2P8krhHUuWan6J6lCaTBxdc17feW5=w391-h245" width="391" /></a></div><div class="separator" style="clear: both; text-align: center;">word2vec 단어 임베딩 네트워크(<a href="http://mccormickml.com/2016/04/19/word2vec-tutorial-the-skip-gram-model/">Chris McCormick</a>, 2016)</div><br /></div><div style="text-align: left;">word2vec를 이용한 임베딩 벡터 계산 과정은 다음과 같다.</div><div style="text-align: left;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjwpE0gMjEJlVasxcVt42xE-8E5Gl0J29dUCR6rkYtGS9WwY4VTT3PN6UUEbVSd-CldXdZ5uDNWfH9qgQUZdbhN1qNYh8AVnwY6duz5vFgtDjYU5UfJNQF7YpQrtMhVB3yMQ78uxuvdqv7VBugs3VufLfhnKZRMAG65Whff7fRaQgBZJntx6lcZpaawk0GD" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="426" data-original-width="882" height="169" src="https://blogger.googleusercontent.com/img/a/AVvXsEjwpE0gMjEJlVasxcVt42xE-8E5Gl0J29dUCR6rkYtGS9WwY4VTT3PN6UUEbVSd-CldXdZ5uDNWfH9qgQUZdbhN1qNYh8AVnwY6duz5vFgtDjYU5UfJNQF7YpQrtMhVB3yMQ78uxuvdqv7VBugs3VufLfhnKZRMAG65Whff7fRaQgBZJntx6lcZpaawk0GD=w349-h169" width="349" /></a></div><br /></div><div style="text-align: left;">이를 통해, 각 토큰은 다차원 공간에서 고유의 위치를 갖게 되므로, 이 위치 특징을 사용해, 다른 토큰 간의 거리 등을 수학적으로 계산할 수 있게 된다. 참고로, 이런 이유로 멀티모달 데이터(텍스트, 이미지, 음성 등) 간 비교, 거리 계산, 생성 시 임베딩은 필수적으로 사용된다.</div><div style="text-align: left;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjmkfEo7Ul-4va2olLvlA-aEpFQvp3ua-7KdAcoqXO5Iu_3jHcfD3KZXrPNgAT2tMh9O7bH9JhUIVqQFjYLI0E6GTVSgwF6iit7inqWr3_ZMU9tg3gwiRuz8Hje3qUZPNn-oiIITiRT46DpOSylemcrgZQFdBjUZS9-h5ZKLYQezyx2Yo2HguQrjYFkWKaP" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="356" data-original-width="720" height="198" src="https://blogger.googleusercontent.com/img/a/AVvXsEjmkfEo7Ul-4va2olLvlA-aEpFQvp3ua-7KdAcoqXO5Iu_3jHcfD3KZXrPNgAT2tMh9O7bH9JhUIVqQFjYLI0E6GTVSgwF6iit7inqWr3_ZMU9tg3gwiRuz8Hje3qUZPNn-oiIITiRT46DpOSylemcrgZQFdBjUZS9-h5ZKLYQezyx2Yo2HguQrjYFkWKaP=w400-h198" width="400" /></a></div><div class="separator" style="clear: both; text-align: center;">영어와 독일어 임베딩 벡터 차원 공간 표현(위치와 거리 비교 계산이 가능함에 주목. <a href="https://medium.com/deeper-learning/glossary-of-deep-learning-word-embedding-f90c3cec34ca">Jaron Collis</a>, 2017)</div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;">이 글에서 임베딩 처리는 다음과 같이 파이토치에 내장된 Embedding 모듈을 사용한다. </div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both;"> self.embedding = nn.Embedding(self.vocab_size, d_model)</div><div><br /></div></div><div class="separator" style="clear: both; text-align: left;">구글 논문에서 임베딩 벡터 크기는 텍스트에서 제일 큰 길이의 문장을 고려해 512로 설정되어 있다. 이는 하이퍼파라메터이다. 임베딩 벡터는 다음 그림과 같이, Self-Attention에 입력되어, Feed Forward 로 전달된다. </div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEj99AJBTeIBKIl5GSNqsDAG_Bo50W_fmMvfNq9JXgHw7Yo0FJhlfMY0v6dzMJKLOesAP48Y5kpnPiZ8Vh_LcDJd5cKOQtAYtsfAunjqRr2U9t8kg3zeCw9v0967Zxonsp2cfgUqjMUn628F0Ur7uS_PF64tpHfLKOrdmkdnM0Cd3AyFiw9eY9b9PMjnE5EG" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="466" data-original-width="823" height="226" src="https://blogger.googleusercontent.com/img/a/AVvXsEj99AJBTeIBKIl5GSNqsDAG_Bo50W_fmMvfNq9JXgHw7Yo0FJhlfMY0v6dzMJKLOesAP48Y5kpnPiZ8Vh_LcDJd5cKOQtAYtsfAunjqRr2U9t8kg3zeCw9v0967Zxonsp2cfgUqjMUn628F0Ur7uS_PF64tpHfLKOrdmkdnM0Cd3AyFiw9eY9b9PMjnE5EG=w400-h226" width="400" /></a></div><div class="separator" style="clear: both; text-align: center;">임베딩 벡터 입력 흐름</div></div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;">여기서 주목할 점은 각 단어 임베딩 벡터가 병렬로 처리될 수 있다는 점이다. 그럼, 각 단어 간 문맥 파악을 위한 위치 순서는 어떻게 입력해야 하는 지 문제가 발생한다. 이는 다음 설명될 Positional Encoding 에서 처리된다. </div><div class="separator" style="clear: both; text-align: left;"><br /></div></div><div style="text-align: left;"><div><div><b>Positional Encoding</b></div><div>입력된 토큰에서 계산된 임베딩 벡터는 각 토큰의 전후 관계와 맥락을 학습할 수 없다. 그러므로, 다음 식과 같은 포지션 인코딩을 통해, 각 토큰의 위치를 앞뒤 관계의 특징을 추가하는 위치 벡터를 얻는다. 이 위치 벡터는 입력되는 토큰의 순서를 유일하게 보장할 수 있다.</div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjtmdBsFhVG9llowbmp6ovFX2pdIMOs3CiUWETamjyNmoL1aKIatcskZIyDtDIvLKBiI7FI4ZshO_d0YRsXvS5UPms96I2eMJK5ocatrEmwfqapzlOqwHZH4mLu8ikAXuAzyB0ekivGX6HjL62UVfoR_X4-eB6a40fsaXbTvBKDYZJFOgw_-Fc6QWXhkvsN" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="121" data-original-width="592" height="50" src="https://blogger.googleusercontent.com/img/a/AVvXsEjtmdBsFhVG9llowbmp6ovFX2pdIMOs3CiUWETamjyNmoL1aKIatcskZIyDtDIvLKBiI7FI4ZshO_d0YRsXvS5UPms96I2eMJK5ocatrEmwfqapzlOqwHZH4mLu8ikAXuAzyB0ekivGX6HjL62UVfoR_X4-eB6a40fsaXbTvBKDYZJFOgw_-Fc6QWXhkvsN=w242-h50" width="242" /></a></div><div class="separator" style="clear: both; text-align: center;">여기서, i = 차원 인덱스</div><div class="separator" style="clear: both; text-align: center;">d_model = 임베딩 길이</div><div class="separator" style="clear: both; text-align: center;">pos = 시퀀스 내에서 단어의 위치</div><br /></div><div><div>참고로, 트랜스포머 아키텍처는 기존 텍스트 번역에 사용되었던 RNN 재귀 학습 기법을 사용하지 않고, Positional Encoding와 다음에 설명할 Multi-Head attention을 사용해 병렬처리를 가능하게 함으로써 학습 시간을 크게 개선했다. </div><div><br /></div><div>우선, 입력되는 데이터 스트림에 순서를 부여하기 위해서는 문장에서 해당 단어의 위치에 대한 정보를 각 단어에 추가해야 한다. 예를 들어, [0, 1] 범위 내에서 각 시간 순서에 맞게 숫자를 할당하는 것인데, 여기서 0은 첫 번째 단어를 의미하고 1은 마지막 시간 단계를 의미한다. 매우 긴 문장에서도 이 문제를 해결할 수 있도록, sin(), cos() 함수를 이용해 주파수 값을 각 입력 토큰의 고유 순서로 할당한다. 이에 대한 자세한 사항은 <a href="https://kazemnejad.com/blog/transformer_architecture_positional_encoding/">여기</a>를 참고한다.</div></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhKz7bbB4qTFhXXOwNuczx8mUm-i5kjhbub-c2JL5h080RK32_pyZ3VctdGsPKOGg4JR_-qFyr0IZD1w1dLuG0udAS_jk3jk3GmT-ZkmyJ-z6saK4n3nhAHtzvBppvjGXTa_x0H5x1wUH7HcCERrSTRrVkf_rxLn9F49OtPeeu4pDBGYI2PBnFaHaCOWKtq" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="517" data-original-width="1070" height="207" src="https://blogger.googleusercontent.com/img/a/AVvXsEhKz7bbB4qTFhXXOwNuczx8mUm-i5kjhbub-c2JL5h080RK32_pyZ3VctdGsPKOGg4JR_-qFyr0IZD1w1dLuG0udAS_jk3jk3GmT-ZkmyJ-z6saK4n3nhAHtzvBppvjGXTa_x0H5x1wUH7HcCERrSTRrVkf_rxLn9F49OtPeeu4pDBGYI2PBnFaHaCOWKtq=w428-h207" width="428" /></a></div><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both;">최대 길이 50인 문장에 대한 128차원 위치 인코딩 결과(각 행은 임베딩 벡터를 나타냄. <a href="https://kazemnejad.com/blog/transformer_architecture_positional_encoding/">Amirhossein Kazemnejad</a>)</div><div class="separator" style="clear: both;"><br /></div></div></div><div>다음은 이를 구현한 것이다. 여기서, d_model은 임베딩 벡터 공간의 차원수로 논문에서는 512값란 사용하지만, 쉬운 이해를 위해 6을 입력하겠다. max_sequence_length는 문장 최대 시퀀스 크기이다. </div><div><div><span style="font-size: x-small;">import torch</span></div><div><span style="font-size: x-small;">import torch.nn as nn</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;">class PositionalEncoding(nn.Module):</span></div><div><span style="font-size: x-small;"><span style="white-space: pre;"> </span>def __init__(self, d_model, max_sequence_length):</span></div><div><span style="font-size: x-small;"><span style="white-space: pre;"> </span>super().__init__()</span></div><div><span style="font-size: x-small;"><span style="white-space: pre;"> </span>self.max_sequence_length = max_sequence_length</span></div><div><span style="font-size: x-small;"><span style="white-space: pre;"> </span>self.d_model = d_model</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;"><span style="white-space: pre;"> </span>def forward(self):</span></div><div><span style="font-size: x-small;"><span style="white-space: pre;"> </span>event_i = torch.arange(0, self.d_model, 2).float()</span></div><div><span style="font-size: x-small;"><span style="white-space: pre;"> </span>denominator = torch.pow(10000, event_i /self.d_model)</span></div><div><span style="font-size: x-small;"><span style="white-space: pre;"> </span>position = torch.arange(self.max_sequence_length).reshape(self.max_sequence_length, 1)</span></div><div><span style="font-size: x-small;"><span style="white-space: pre;"> </span>even_PE = torch.sin(position / denominator) # 사인함수로 위치 인코딩</span></div><div><span style="font-size: x-small;"><span style="white-space: pre;"> </span>odd_PE = torch.cos(position / denominator) # 코사인함수로 위치 인코딩</span></div><div><span style="font-size: x-small;"><span style="white-space: pre;"> </span>stacked = torch.stack([even_PE, odd_PE], dim=2)</span></div><div><span style="font-size: x-small;"><span style="white-space: pre;"> </span>PE = torch.flatten(stacked, start_dim=1, end_dim=2)</span></div><div><span style="font-size: x-small;"><span style="white-space: pre;"> </span>return PE</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;">pe = PositionalEncoding(d_model=6, max_sequence_length=10)</span></div><div><span style="font-size: x-small;">PE = pe.forward()</span></div><div><span style="font-size: x-small;">print(PE)</span></div></div></div><div><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both;"><br /></div></div></div><div><b>Self-Attention & Multi-Head Attention</b></div><div>앞의 단어에 대한 다음 단어를 예측하도록, 셀프 어텐션(Self-Attention) 계산 모델을 구현해야 한다. 예를 들어, 입력된 데이터 토큰을 다음과 같이 변환하는 모델을 개발해야 한다고 하자.</div><div style="text-align: center;">Hello I love you > 안녕, 나는 너를 사랑해</div><div><br /></div><div>이 경우, 이 변환모델은 Hello의 경우, '안녕', '나는', '너를', '사랑해' 토큰 중에 '안녕'에 초점을 맞춰, '안녕'이란 토큰을 리턴해야 한다. 그러므로, 어떤 토큰이 입력되면, 어떤 토큰값을 출력해야 하는 지를 정의해야 한다. 이를 행렬로 나타내면 다음과 같다.</div><div style="text-align: center;">Hello I love you</div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhrtFO3tUINnX5kPGfK3mHdTW0fyfMaEBEeTcAbkatDOCZ6q_pPJZZrDV3VqvL36O-QFL68DfX-8ZdY-T793aYhVisVt0LU5VBRttFuvOzixKXD4RKABm_zoBBBtzh5LrY9Tme9S971MN2c8w_1uIXAG_pKTzMFEaTtPt4Vs65qiFdLZs04VIMoruJ9soiA" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="99" data-original-width="512" height="62" src="https://blogger.googleusercontent.com/img/a/AVvXsEhrtFO3tUINnX5kPGfK3mHdTW0fyfMaEBEeTcAbkatDOCZ6q_pPJZZrDV3VqvL36O-QFL68DfX-8ZdY-T793aYhVisVt0LU5VBRttFuvOzixKXD4RKABm_zoBBBtzh5LrY9Tme9S971MN2c8w_1uIXAG_pKTzMFEaTtPt4Vs65qiFdLZs04VIMoruJ9soiA" width="320" /></a></div><br /></div><div>이 행렬의 가로와 세로는 토큰을 의미한다. 각 토큰이 다른 어떤 토큰에 초점을 맞추는 지가 확률로 정의된다. 이 예에서, 첫 행의 경우, 토큰 'I'에 초점을 맞추는 것을 확인할 수 있다. 이 어텐션 개념은 RNN, LSTM 문제점을 개선하려는 의도에서 개발되었다. </div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhjKxyELVAIZiSAFV_VONkXqG3Rb4VWcqnusvIF_MI7NbFL4Ti1P860x6kLR9uD4IKGKX6HvunctkKBKxNNb71CPbtlfCCfdon-yYiNWdtN5MZCHZP11hcTSkCiMLmrwdFhaNnkIIAWxO0EHDZ6B6WLFFEDR3qGYoBN6p62NxG_3faTMFd1rQ9H_vNbEwGM" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="491" data-original-width="772" height="204" src="https://blogger.googleusercontent.com/img/a/AVvXsEhjKxyELVAIZiSAFV_VONkXqG3Rb4VWcqnusvIF_MI7NbFL4Ti1P860x6kLR9uD4IKGKX6HvunctkKBKxNNb71CPbtlfCCfdon-yYiNWdtN5MZCHZP11hcTSkCiMLmrwdFhaNnkIIAWxO0EHDZ6B6WLFFEDR3qGYoBN6p62NxG_3faTMFd1rQ9H_vNbEwGM" width="320" /></a></div></div><div class="separator" style="clear: both; text-align: center;">주어진 문장에서 어텐션 예시(적색 표시는 현재 단어 토큰, 청색은 주의 집중되어 활성화되는 토큰을 표현. <a href="https://arxiv.org/pdf/1601.06733.pdf">Long Short-Term Memory-Networks for Machine Reading</a>, 2016)</div><div><br /></div><div>특정 토큰이 초점을 맞추는 확률을 계산을 위해, Google 트랜스포머 논문에서는 이 어텐션 개념을 사용한다. </div><div style="text-align: center;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjBF4kNqbBTj-PlZt9uFaIWuLyXStJAlLrbg7gqk20C8M0eRzE5DzjGGsdzUfriq3_hIJXHUMjQW8v4s4VEUIIz0FVJQ9vnIqOPLVI4gXThc6id1IU39cwYKPMshN1Pwf29c8tZfudSvb8i3BJUcMpzIjl-_TIawTJM_0hygpuP4LFAkE2MRfYPy028muS1" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="100" data-original-width="456" height="55" src="https://blogger.googleusercontent.com/img/a/AVvXsEjBF4kNqbBTj-PlZt9uFaIWuLyXStJAlLrbg7gqk20C8M0eRzE5DzjGGsdzUfriq3_hIJXHUMjQW8v4s4VEUIIz0FVJQ9vnIqOPLVI4gXThc6id1IU39cwYKPMshN1Pwf29c8tZfudSvb8i3BJUcMpzIjl-_TIawTJM_0hygpuP4LFAkE2MRfYPy028muS1=w251-h55" width="251" /></a></div></div><div style="text-align: center;">여기서, Q = Query (what I'm looking for). 질의어. 디코더의 출력값</div><div style="text-align: center;">K = Key (what I can offer). 토큰 간 관계 유사도 계산을 위해 Query와 비교할 때 사용됨</div><div style="text-align: center;">V = Value (what I actually offer). Query, Key에 대한 최종 출력으로 관계성 계산에 사용</div><div style="text-align: center;">d<span style="font-size: xx-small;">k </span>= k 벡터의 차원</div><div style="text-align: center;">M = Mask (미래 데이터 토큰만 학습데이터로 고려함)</div><div><br /></div><div>Query는 디코더에서 포지션 인코딩한 벡터값이 입력된다. 이 값은 트랜스포머의 Self-attention 처리를 위해 Key, Value는 인코더의 포지션 인코딩한 벡터를 이용한다. 이때, 다음의 어텐션 계산에 따른 유사도가 가까워지도록 학습되도록, Q, K, V는 신경망 레이어로 정의한다. </div><div><br /></div><div>결국 이 어텐션 함수는 손실함수와 유사한 역할을 하나, 입력 데이터 토큰들 간의 유사도를 통한 관계성 계산에 집중한다. 어텐션 함수는 앞서 임베딩 토큰들이 입력되는 Q, K, V 레이어 가중치를 조정하게 되는 기준치(유사도)를 제시하는 역할을 한다. </div><div><br /></div><div>입력된 쿼리 Q에 대한 다른 토큰들과 유사도(초점을 맞춰야할 토큰)을 계산하기 위해, 다음 그림과 같이 입력되는 모든 토큰에 대해 하나의 키, 값 벡터를 가지게 된다. </div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEiy3Dg0VtVDyjPywY0JiJNcmJPd-m6ngSWmR87GCdyx_6lChkB-vkZk4vps0m81qnRiVb3wCp6TJQVH_du3gpRopKEQznOt8yp0D3vVwVE6AKPeo4Cabqs-jOFRgRsVDiU3JNOXyROlRYfmA8Qw-9-rOd_8GoxbmODSNNtCOK8e78d0JrFKZhQcvZkQX-AI" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="511" data-original-width="805" height="254" src="https://blogger.googleusercontent.com/img/a/AVvXsEiy3Dg0VtVDyjPywY0JiJNcmJPd-m6ngSWmR87GCdyx_6lChkB-vkZk4vps0m81qnRiVb3wCp6TJQVH_du3gpRopKEQznOt8yp0D3vVwVE6AKPeo4Cabqs-jOFRgRsVDiU3JNOXyROlRYfmA8Qw-9-rOd_8GoxbmODSNNtCOK8e78d0JrFKZhQcvZkQX-AI=w400-h254" width="400" /></a></div><div class="separator" style="clear: both; text-align: center;">Q, K, V 벡터의 어텐션 계산 개념 </div><br /></div><div>이 어텐션 수식에 의해, 학습과정을 반복하면, 생성될 토큰을 가리키는 어텐션 스코어(attention score)가 계산되도록 해야 한다. 참고로, 어텐션 스코어는 다음 그림과 같이, 특정 토큰 뒤에 생성될 토큰을 가리키는 확률 행렬로 구성되며, 앞의 어텐션 수식을 모두 계산한 후 얻을 수 있다. </div><div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEiEd_fCawqydFOJL6N70_Pv5Cye7m52RmhOrMzEjXQZdFcehU3rw9wQOMt69e3BP8ff15ZmekVZhmb2DpFZNjL8HW3s56T-dQdk4felUOONSTh4t0pecFZ6YiwTtVOeekPUkSzb-JOesyRtFSCXWf-JWIUMUsitEWF5IgHXAU_sAR-UEWk68MeQ71KC8MoQ" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="521" data-original-width="601" height="173" src="https://blogger.googleusercontent.com/img/a/AVvXsEiEd_fCawqydFOJL6N70_Pv5Cye7m52RmhOrMzEjXQZdFcehU3rw9wQOMt69e3BP8ff15ZmekVZhmb2DpFZNjL8HW3s56T-dQdk4felUOONSTh4t0pecFZ6YiwTtVOeekPUkSzb-JOesyRtFSCXWf-JWIUMUsitEWF5IgHXAU_sAR-UEWk68MeQ71KC8MoQ=w200-h173" width="200" /></a></div><div class="separator" style="clear: both; text-align: center;">어텐션 확률 스코어 매트릭스 예</div></div></div><div><br /></div><div>어텐션 수식에서 어텐션 스코어는 유사도에 따라 계산되어야 한다. 이는 수식에서 QK^T 이 담당한다. Q, K, V의 학습 가중치를 계산하기 위해, Value가 주어진다. QK 벡터 간 곱은 cosine 함수를 사용한다. cosine 함수의 출력 θ는 두 입력 벡터가 주어졌을 때, 서로 가까울수록 1.0에 가까워지고, 멀수록 -1.0에 가까워진다. </div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEiQDhtZksZHNGcBrvJZtHUC3Dc5aTUFPUKe6XO-yLYWPL7UFe6_Gx5EFYfbPVgX5WBNyjGwAb4RhOr6tPmE4zUSYQAKEPNNbQCVDFtD5KD-l04kzNq1g8CHDqsQI69hFH5F6uaL7gdK4vXaJSAaChgKM3JSszhtXBDnh5pgf2wt2GFnmo4A9qfwtAU7i6fm" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="443" data-original-width="609" height="167" src="https://blogger.googleusercontent.com/img/a/AVvXsEiQDhtZksZHNGcBrvJZtHUC3Dc5aTUFPUKe6XO-yLYWPL7UFe6_Gx5EFYfbPVgX5WBNyjGwAb4RhOr6tPmE4zUSYQAKEPNNbQCVDFtD5KD-l04kzNq1g8CHDqsQI69hFH5F6uaL7gdK4vXaJSAaChgKM3JSszhtXBDnh5pgf2wt2GFnmo4A9qfwtAU7i6fm=w229-h167" width="229" /></a></div><div class="separator" style="clear: both; text-align: center;">임베딩 벡터 차원에서 각 토큰과 cosine 유사도 거리 예</div><br /></div><div>어텐션 스코어를 계산한 후, 가중치 계산 시 특정 입력 신호의 그레디언트 소멸 문제를 개선하기 위해 SQRT(d_k) 값으로 정규화처리한다. </div><div><br /></div><div>문장 번역은 과거의 데이터를 입력받아, 미래의 문장을 생성해야 하므로, 과거에 대한 가중치는 마스크 처리해야 한다. 수식의 M부분에 해당한다. </div><div><br /></div><div><div>이 수식을 그대로 구현하면 다음과 같다.</div><div><div><div><span style="font-size: x-small;">import numpy as np</span></div><div><span style="font-size: x-small;">import math</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;">L, d_k, d_v = 4, 8, 8 # my name is tom</span></div><div><span style="font-size: x-small;">q = np.random.randn(L, d_k) # what I am looking for</span></div><div><span style="font-size: x-small;">k = np.random.randn(L, d_k) # what I can offer</span></div><div><span style="font-size: x-small;">v = np.random.randn(L, d_v) # what I actually offer</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;">def softmax(x):</span></div><div><span style="font-size: x-small;"> return (np.exp(x).T / np.sum(np.exp(x), axis=-1)).T</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;">def scaled_dot_product_attention(q, k, v, mask=None):</span></div><div><span style="font-size: x-small;"> d_k = q.shape[-1]</span></div><div><span style="font-size: x-small;"> scaled = np.matmul(q, k.T) / math.sqrt(d_k) # QK^T / sqrt(d_k)</span></div><div><span style="font-size: x-small;"> if mask is not None:</span></div><div><span style="font-size: x-small;"> scaled = scaled + mask # considering future predition token</span></div><div><span style="font-size: x-small;"> attention = softmax(scaled)</span></div><div><span style="font-size: x-small;"> # softmax(QK^T / sqrt(d_k) + M)V</span></div><div><span style="font-size: x-small;"> out = np.matmul(attention, v) # QK^T</span></div><div><span style="font-size: x-small;"> return out, attention</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;"># masking. to ensure words can only attend to previous words. dont't look at future words</span></div><div><span style="font-size: x-small;">mask = np.tril(np.ones([L, L])) # lower triangular matrix</span></div><div><span style="font-size: x-small;">print(mask)</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;">values, attention = scaled_dot_product_attention(q, k, v, mask=None)</span></div><div><span style="font-size: x-small;">print('Q\n', q)</span></div><div><span style="font-size: x-small;">print('K\n', k)</span></div><div><span style="font-size: x-small;">print('V\n', v)</span></div><div><span style="font-size: x-small;">print('New V\n', values)</span></div><div><span style="font-size: x-small;">print('Attention\n', attention)</span></div><div><br /></div></div><div>앞의 Self-Attention 모델을 이용해 주어진 입력에 대한 출력을 생성한다면, softmax 함수를 이용할 것이다. 하지만, 이 경우, 단일 Self-Attention 네트워크로 인한 다양한 문맥에 따른 출력을 생성하지 못하는 문제가 있다. </div><div><br /></div><div>이 문제를 해결하기 위해, 트랜스포머 모델은 QKV 텐서를 몇개로 복사해 각각 별도로 학습 계산한다. 이를 멀티헤드 어텐션이라 한다. 이를 통해, 각 입력 토큰은 앞서 입력된 토큰들의 다양한 관계 조합에 따른 출력 토큰이 생성될 수 있도록 한다.</div><div><br /></div><div>예를 들어, 'Hello I love you'를 그대로 변환해 '안녕 나는 사랑해 너를'로 생성하지 않고, '나는 너를 사랑해'라고 출력해야 한다. 또한, 'Hi, I love game like Matrix' 로 입력하면, '나는 매트릭스같은 영화를 좋아해'를 생성해야 한다. 즉, love와 같은 단어 토큰의 의미는 문맥에 따라 달라져야 한다. </div><div><br /></div><div>또한, 'which do you like dog or cat' 에서 어텐션하는 단어도 다양할 수 있다. which가 dog나 cat에 집중할 수도 있고, you가 like에 집중될 수도 있다. 이는 주어지는 문장들에 따라 달라질 수 있어야 한다. 이렇게 유사하지만, 문맥 관계에 따라 가변적인 의미를 어텐션할 수 있도록, 어텐션 레이어 한 개 만 사용하지 않고, 여러 개로 분할해 가중치를 학습한다. 그 결과 벡터는 concatenate 함수를 이용해 단순 결합한다. 논문에는 다음과 같이 수식으로 표시되어 있다.</div></div></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhm-bvyhxYC1293D1vv6kUzDwO5fKmtO5eQ7U9sL5P6G3lLpnsz6SSTWnEYimm4Z7TOI6NgIbWfaMtSOfFurFrXFtURdfmLycRbWl34N-FBqMHvQAiA0hRd37Ja3RXD5mGVe8zbprJsKeuYA_8nKQanPm4ftmuTsUj_bz-g7LVK2PQryjQv9TLT14QSx65t" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="223" data-original-width="968" height="100" src="https://blogger.googleusercontent.com/img/a/AVvXsEhm-bvyhxYC1293D1vv6kUzDwO5fKmtO5eQ7U9sL5P6G3lLpnsz6SSTWnEYimm4Z7TOI6NgIbWfaMtSOfFurFrXFtURdfmLycRbWl34N-FBqMHvQAiA0hRd37Ja3RXD5mGVe8zbprJsKeuYA_8nKQanPm4ftmuTsUj_bz-g7LVK2PQryjQv9TLT14QSx65t=w432-h100" width="432" /></a></div><div class="separator" style="clear: both; text-align: center;">멀티헤드 어텐션 수식(Google)</div><br /></div><div>이 과정을 모두 거치고 나면, 인코더에 입력되는 텍스트 토큰들의 상호 관계를 유사도 기반으로 계산된 어텐션 스코어를 출력하는 가중치 모델을 얻게 된다. 이 가중치 데이터셋은 Query로 값을 검색하는 데 사용되는 일종의 데이터베이스 역할을 한다.</div><div><br /></div><div>이제, 입력 임베딩 벡터를 멀티헤드로 나누어, QKV 텐서를 계산한다. 참고로, 논문에서 멀티헤더 수는 8로 고정되어 있다. </div><div><div><span style="font-size: x-small;">import io, math, numpy as np</span></div><div><span style="font-size: x-small;">import torch</span></div><div><span style="font-size: x-small;">import torch.nn as nn</span></div><div><span style="font-size: x-small;">import torch.nn.functional as F</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;">def scaled_dot_product(q, k, v, mask=None):</span></div><div><span style="white-space: normal;"><span style="font-size: x-small;"><span style="white-space: pre;"> </span>d_k = q.shape[-1]</span></span></div><div><span style="white-space: normal;"><span style="font-size: x-small;"><span style="white-space: pre;"> </span>scaled = torch.matmul(q, k.transpose(-1, -2)) / math.sqrt(d_k) # QK^T / sqrt(d_k)</span></span></div><div><span style="white-space: normal;"><span style="font-size: x-small;"><span style="white-space: pre;"> </span>if mask is not None:</span></span></div><div><span style="white-space: normal;"><span style="font-size: x-small;"><span style="white-space: pre;"> </span>scaled += mask # considering future predition token</span></span></div><div><span style="white-space: normal;"><span style="font-size: x-small;"><span style="white-space: pre;"> </span>attention = F.softmax(scaled, dim=-1)</span></span></div><div><span style="white-space: normal;"><span style="font-size: x-small;"><span style="white-space: pre;"> </span>values = torch.matmul(attention, v) # QK^T</span></span></div><div><span style="white-space: normal;"><span style="font-size: x-small;"><span style="white-space: pre;"> </span>return values, attention</span></span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;">class multihead_attention(nn.Module):</span></div><div><span style="white-space: normal;"><span style="font-size: x-small;"><span style="white-space: pre;"> </span>def __init__(self, input_dim, d_model, num_heads):</span></span></div><div><span style="white-space: normal;"><span style="font-size: x-small;"><span style="white-space: pre;"> </span>super().__init__()</span></span></div><div><span style="white-space: normal;"><span style="font-size: x-small;"><span style="white-space: pre;"> </span>self.input_dim = input_dim</span></span></div><div><span style="white-space: normal;"><span style="font-size: x-small;"><span style="white-space: pre;"> </span>self.d_model = d_model</span></span></div><div><span style="white-space: normal;"><span style="font-size: x-small;"><span style="white-space: pre;"> </span>self.num_heads = num_heads</span></span></div><div><span style="white-space: normal;"><span style="font-size: x-small;"><span style="white-space: pre;"> </span>self.head_dim = d_model // num_heads # 멀티헤드 수 만큼 나눔</span></span></div><div><span style="white-space: normal;"><span style="font-size: x-small;"><span style="white-space: pre;"> </span>self.qkv_layer = nn.Linear(input_dim, 3 * d_model) # QKV 벡터 가중치 계산 레이어</span></span></div><div><span style="white-space: normal;"><span style="font-size: x-small;"><span style="white-space: pre;"> </span>self.linear_layer = nn.Linear(d_model, d_model)</span></span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="white-space: normal;"><span style="font-size: x-small;"><span style="white-space: pre;"> </span>def forward(self, x, mask=None):</span></span></div><div><span style="white-space: normal;"><span style="font-size: x-small;"><span style="white-space: pre;"> </span>batch_size, sequence_length, input_dim = x.size()</span></span></div><div><span style="white-space: normal;"><span style="font-size: x-small;"><span style="white-space: pre;"> </span>print(f'x.size(): {x.size()}')</span></span></div><div><span style="white-space: normal;"><span style="font-size: x-small;"><span style="white-space: pre;"> </span>qkv = self.qkv_layer(x) # QKV 레이어 가중치는 아래 QKV 유사도에 따라 학습됨</span></span></div><div><span style="white-space: normal;"><span style="font-size: x-small;"><span style="white-space: pre;"> </span>qkv = qkv.reshape(batch_size, sequence_length, self.num_heads, 3 * self.head_dim)</span></span></div><div><span style="white-space: normal;"><span style="font-size: x-small;"><span style="white-space: pre;"> </span>qkv = qkv.permute(0, 2, 1, 3)</span></span></div><div><span style="white-space: normal;"><span style="font-size: x-small;"><span style="white-space: pre;"> </span>q, k, v = qkv.chunk(3, dim=-1)</span></span></div><div><span style="white-space: normal;"><span style="font-size: x-small;"><span style="white-space: pre;"> </span>values, attention = scaled_dot_product(q, k, v, mask) # QKV 유사도 계산</span></span></div><div><span style="white-space: normal;"><span style="font-size: x-small;"><span style="white-space: pre;"> </span>values = values.reshape(batch_size, sequence_length, self.num_heads * self.head_dim)</span></span></div><div><span style="white-space: normal;"><span style="font-size: x-small;"><span style="white-space: pre;"> </span>out = self.linear_layer(values)</span></span></div><div><span style="white-space: normal;"><span style="font-size: x-small;"><span style="white-space: pre;"> </span>return out</span></span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;">input_dim = 1024</span></div><div><span style="font-size: x-small;">d_model = 512</span></div><div><span style="font-size: x-small;">num_heads = 8</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;">batch_size = 30</span></div><div><span style="font-size: x-small;">sequence_length = 5</span></div><div><span style="font-size: x-small;">x = torch.randn((batch_size, sequence_length, input_dim))</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;">model = multihead_attention(input_dim, d_model, num_heads)</span></div><div><span style="font-size: x-small;">out = model.forward(x)</span></div><div><span style="font-size: x-small;">print(out)</span></div><div><br /></div></div><div>실행 결과는 다음과 같다. </div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEh97aLMBfXmV7JfVJKbd7Fq8PcqZUuS6F348XYndyVDYaeieP5Pu7eOY-kp5PBn3p_V1M8fp7Y0ZcyuJDXyhd0fL4oJoG5fgiAt_5FGpTDqqlwPSbij2pYsOL59HDutf207htp2wqX9_ojDkBZOewn-KIsThr5gJjT-IA9N9FMBmJNYxywXBs0oKQodWGeW" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="381" data-original-width="976" height="156" src="https://blogger.googleusercontent.com/img/a/AVvXsEh97aLMBfXmV7JfVJKbd7Fq8PcqZUuS6F348XYndyVDYaeieP5Pu7eOY-kp5PBn3p_V1M8fp7Y0ZcyuJDXyhd0fL4oJoG5fgiAt_5FGpTDqqlwPSbij2pYsOL59HDutf207htp2wqX9_ojDkBZOewn-KIsThr5gJjT-IA9N9FMBmJNYxywXBs0oKQodWGeW=w400-h156" width="400" /></a></div><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhBb9XqteC-rYDD60_1PqqjrP5RUWKpLc7FXlMh10kr7lK43hjgVm1NKQGcW8m1pJQaoLHZyvPYp68k-wxVgnEg2IycADDa2dH35-WQ5zqpp0tpJUL6jl5cIDTagOZPLtqBBpi3bHILLI02SSoZjsjs71ClsjayDkD6pYjoY24elPko2D5kEY8IY8xJyj-H" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="300" data-original-width="955" height="126" src="https://blogger.googleusercontent.com/img/a/AVvXsEhBb9XqteC-rYDD60_1PqqjrP5RUWKpLc7FXlMh10kr7lK43hjgVm1NKQGcW8m1pJQaoLHZyvPYp68k-wxVgnEg2IycADDa2dH35-WQ5zqpp0tpJUL6jl5cIDTagOZPLtqBBpi3bHILLI02SSoZjsjs71ClsjayDkD6pYjoY24elPko2D5kEY8IY8xJyj-H=w400-h126" width="400" /></a></div><div class="separator" style="clear: both; text-align: center;">멀티헤드 어텐션 계산 결과 일부</div></div></div><div><br /></div></div><div style="text-align: left;"><b>Add & Normal</b></div><div style="text-align: left;">딥러닝 신경망은 학습을 계속할 수록 가중치를 조정하는 기울기값이 감쇄하는 현상이 있다. 이를 방지하기 위해, 잔차연결(Residual Connection)으로 이전 레이어의 출력값과 멀티헤드 계산된 결과의 출력값을 더한다. 이는 기존에 사용된 <a href="https://en.wikipedia.org/wiki/Residual_neural_network">ResNet</a> 개념을 재활용한 것이다.</div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEiLXZQcX5CeBy1ZfDAGaZZ2S6w0sfJbOOartoHy2lKC12uOglYkx2E1OJnOVUpFnKU261PJcb2SyrXJRsPEQJlAS2nFuxleUG-H3f3TmYOq2d2Ir--61kp4prDLhODJg4VX-SGb9u8J0bDslnEO93zJc4cxxZs7zqxTF6ViN_N1xoB_zWIACbT6y8KTCCrd" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="179" data-original-width="330" height="109" src="https://blogger.googleusercontent.com/img/a/AVvXsEiLXZQcX5CeBy1ZfDAGaZZ2S6w0sfJbOOartoHy2lKC12uOglYkx2E1OJnOVUpFnKU261PJcb2SyrXJRsPEQJlAS2nFuxleUG-H3f3TmYOq2d2Ir--61kp4prDLhODJg4VX-SGb9u8J0bDslnEO93zJc4cxxZs7zqxTF6ViN_N1xoB_zWIACbT6y8KTCCrd=w200-h109" width="200" /></a></div><div class="separator" style="clear: both; text-align: center;">ResNet 네트워크(wikipedia)</div><div><br /></div></div><div style="text-align: left;">아울러, 레이어의 특정 유닛에 편중된 학습이 되지 않도록, 레이어의 가중치 평균이 0이 되도록 <a href="https://arxiv.org/pdf/1602.07868.pdf">가중치 정규화</a>를 수행한다. </div><div style="text-align: left;"><br /></div><div style="text-align: left;">다음은 이를 구현한 것이다. </div><div style="text-align: left;"><div><span style="font-size: x-small;">import torch</span></div><div><span style="font-size: x-small;">from torch import nn</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;">class LayerNormalization():</span></div><div><span style="font-size: x-small;"> def __init__(self, parameters_shape, eps=1e-5):</span></div><div><span style="font-size: x-small;"> self.parameters_shape = parameters_shape</span></div><div><span style="font-size: x-small;"> self.eps = eps</span></div><div><span style="font-size: x-small;"> self.gamma = nn.Parameter(torch.ones(parameters_shape))</span></div><div><span style="font-size: x-small;"> self.beta = nn.Parameter(torch.zeros(parameters_shape))</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;"> def forward(self, inputs):</span></div><div><span style="font-size: x-small;"> dims = [-(i + 1) for i in range(len(self.parameters_shape))]</span></div><div><span style="font-size: x-small;"> mean = inputs.mean(dim=dims, keepdim=True)</span></div><div><span style="font-size: x-small;"> print(f'Mean size: {mean.size()}, Mean: {mean}')</span></div><div><span style="font-size: x-small;"> var = ((inputs - mean) ** 2).mean(dim=dims, keepdim=True)</span></div><div><span style="font-size: x-small;"> std = (var + self.eps).sqrt()</span></div><div><span style="font-size: x-small;"> print(f'std size: {std.size()}, std: {std}')</span></div><div><span style="font-size: x-small;"> y = (inputs - mean) / std</span></div><div><span style="font-size: x-small;"> print(f'y size: {y.size()}, y: {y}')</span></div><div><span style="font-size: x-small;"> out = self.gamma * y + self.beta</span></div><div><span style="font-size: x-small;"> print(f'out size: {out.size()}, out: {out}')</span></div><div><span style="font-size: x-small;"> return out</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;">batch_size = 3</span></div><div><span style="font-size: x-small;">sentense_length = 5 </span></div><div><span style="font-size: x-small;">embedding_dim = 8</span></div><div><span style="font-size: x-small;">inputs = torch.randn(sentense_length, batch_size, embedding_dim)</span></div><div><span style="font-size: x-small;">print(f'inputs size: {inputs.size()}, inputs: {inputs}')</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;">layer_norm = LayerNormalization(parameters_shape=(embedding_dim,))</span></div><div><span style="font-size: x-small;">out = layer_norm.forward(inputs)</span></div><div><span style="font-size: x-small;">print(f'out size: {out.size()}, out: {out}')</span></div></div><div style="text-align: left;"><br /></div><div style="text-align: left;"><b>Encoder 전체 구현</b></div><div style="text-align: left;">다음 그림은 이 전체 계산 과정을 간략히 보여준다. </div><div style="text-align: left;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgfHCUz4IE-S1vf9PltOFxdrcrNsEwJKmot9vofbmum8F_SSjBDv_1cfDZdxMuC9G7lcgpXYIbtTo1g6Y4xaB6Zj608lRX85QsUrnyQ6G9L4qMCGaO7sQI_2sdSmCse7zkY8EPkSalgqTNQAtIP85g6NfniN85Pq_cs-diNaygjSNuJVwlQgdMkEH8aN7RI" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="804" data-original-width="870" height="389" src="https://blogger.googleusercontent.com/img/a/AVvXsEgfHCUz4IE-S1vf9PltOFxdrcrNsEwJKmot9vofbmum8F_SSjBDv_1cfDZdxMuC9G7lcgpXYIbtTo1g6Y4xaB6Zj608lRX85QsUrnyQ6G9L4qMCGaO7sQI_2sdSmCse7zkY8EPkSalgqTNQAtIP85g6NfniN85Pq_cs-diNaygjSNuJVwlQgdMkEH8aN7RI=w422-h389" width="422" /></a></div><div class="separator" style="clear: both; text-align: center;">트랜스포머 인코더 계산 과정</div><br />다음 코드는 인코더를 구현한 것이다. </div><div style="text-align: left;"><div><span style="font-size: x-small;">import torch</span></div><div><span style="font-size: x-small;">import math</span></div><div><span style="font-size: x-small;">from torch import nn</span></div><div><span style="font-size: x-small;">import torch.nn.functional as F</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;">def scaled_dot_product(q, k, v, mask=None):</span></div><div><span style="font-size: x-small;"> d_k = q.size()[-1]</span></div><div><span style="font-size: x-small;"> scaled = torch.matmul(q, k.transpose(-1, -2)) / math.sqrt(d_k)</span></div><div><span style="font-size: x-small;"> print(f"scaled.size() : {scaled.size()}")</span></div><div><span style="font-size: x-small;"> if mask is not None:</span></div><div><span style="font-size: x-small;"> print(f"ADDING MASK of shape {mask.size()}") </span></div><div><span style="font-size: x-small;"> # Broadcasting add. So just the last N dimensions need to match</span></div><div><span style="font-size: x-small;"> scaled += mask</span></div><div><span style="font-size: x-small;"> attention = F.softmax(scaled, dim=-1)</span></div><div><span style="font-size: x-small;"> values = torch.matmul(attention, v)</span></div><div><span style="font-size: x-small;"> return values, attention</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;">class MultiHeadAttention(nn.Module):</span></div><div><span style="font-size: x-small;"> def __init__(self, d_model, num_heads):</span></div><div><span style="font-size: x-small;"> super().__init__()</span></div><div><span style="font-size: x-small;"> self.d_model = d_model</span></div><div><span style="font-size: x-small;"> self.num_heads = num_heads</span></div><div><span style="font-size: x-small;"> self.head_dim = d_model // num_heads</span></div><div><span style="font-size: x-small;"> self.qkv_layer = nn.Linear(d_model , 3 * d_model)</span></div><div><span style="font-size: x-small;"> self.linear_layer = nn.Linear(d_model, d_model)</span></div><div><span style="font-size: x-small;"> </span></div><div><span style="font-size: x-small;"> def forward(self, x, mask=None):</span></div><div><span style="font-size: x-small;"> batch_size, max_sequence_length, d_model = x.size()</span></div><div><span style="font-size: x-small;"> print(f"x.size(): {x.size()}")</span></div><div><span style="font-size: x-small;"> qkv = self.qkv_layer(x)</span></div><div><span style="font-size: x-small;"> print(f"qkv.size(): {qkv.size()}")</span></div><div><span style="font-size: x-small;"> qkv = qkv.reshape(batch_size, max_sequence_length, self.num_heads, 3 * self.head_dim)</span></div><div><span style="font-size: x-small;"> print(f"qkv.size(): {qkv.size()}")</span></div><div><span style="font-size: x-small;"> qkv = qkv.permute(0, 2, 1, 3)</span></div><div><span style="font-size: x-small;"> print(f"qkv.size(): {qkv.size()}")</span></div><div><span style="font-size: x-small;"> q, k, v = qkv.chunk(3, dim=-1)</span></div><div><span style="font-size: x-small;"> print(f"q size: {q.size()}, k size: {k.size()}, v size: {v.size()}, ")</span></div><div><span style="font-size: x-small;"> values, attention = scaled_dot_product(q, k, v, mask)</span></div><div><span style="font-size: x-small;"> print(f"values.size(): {values.size()}, attention.size:{ attention.size()} ")</span></div><div><span style="font-size: x-small;"> values = values.reshape(batch_size, max_sequence_length, self.num_heads * self.head_dim)</span></div><div><span style="font-size: x-small;"> print(f"values.size(): {values.size()}")</span></div><div><span style="font-size: x-small;"> out = self.linear_layer(values)</span></div><div><span style="font-size: x-small;"> print(f"out.size(): {out.size()}")</span></div><div><span style="font-size: x-small;"> return out</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;">class LayerNormalization(nn.Module):</span></div><div><span style="font-size: x-small;"> def __init__(self, parameters_shape, eps=1e-5):</span></div><div><span style="font-size: x-small;"> super().__init__()</span></div><div><span style="font-size: x-small;"> self.parameters_shape=parameters_shape</span></div><div><span style="font-size: x-small;"> self.eps=eps</span></div><div><span style="font-size: x-small;"> self.gamma = nn.Parameter(torch.ones(parameters_shape))</span></div><div><span style="font-size: x-small;"> self.beta = nn.Parameter(torch.zeros(parameters_shape))</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;"> def forward(self, inputs):</span></div><div><span style="font-size: x-small;"> dims = [-(i + 1) for i in range(len(self.parameters_shape))]</span></div><div><span style="font-size: x-small;"> mean = inputs.mean(dim=dims, keepdim=True)</span></div><div><span style="font-size: x-small;"> print(f"Mean ({mean.size()})")</span></div><div><span style="font-size: x-small;"> var = ((inputs - mean) ** 2).mean(dim=dims, keepdim=True)</span></div><div><span style="font-size: x-small;"> std = (var + self.eps).sqrt()</span></div><div><span style="font-size: x-small;"> print(f"Standard Deviation ({std.size()})")</span></div><div><span style="font-size: x-small;"> y = (inputs - mean) / std</span></div><div><span style="font-size: x-small;"> print(f"y: {y.size()}")</span></div><div><span style="font-size: x-small;"> out = self.gamma * y + self.beta</span></div><div><span style="font-size: x-small;"> print(f"self.gamma: {self.gamma.size()}, self.beta: {self.beta.size()}")</span></div><div><span style="font-size: x-small;"> print(f"out: {out.size()}")</span></div><div><span style="font-size: x-small;"> return out</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;">class PositionwiseFeedForward(nn.Module):</span></div><div><span style="font-size: x-small;"> def __init__(self, d_model, hidden, drop_prob=0.1):</span></div><div><span style="font-size: x-small;"> super(PositionwiseFeedForward, self).__init__()</span></div><div><span style="font-size: x-small;"> self.linear1 = nn.Linear(d_model, hidden)</span></div><div><span style="font-size: x-small;"> self.linear2 = nn.Linear(hidden, d_model)</span></div><div><span style="font-size: x-small;"> self.relu = nn.ReLU()</span></div><div><span style="font-size: x-small;"> self.dropout = nn.Dropout(p=drop_prob)</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;"> def forward(self, x):</span></div><div><span style="font-size: x-small;"> x = self.linear1(x)</span></div><div><span style="font-size: x-small;"> print(f"x after first linear layer: {x.size()}")</span></div><div><span style="font-size: x-small;"> x = self.relu(x)</span></div><div><span style="font-size: x-small;"> print(f"x after activation: {x.size()}")</span></div><div><span style="font-size: x-small;"> x = self.dropout(x)</span></div><div><span style="font-size: x-small;"> print(f"x after dropout: {x.size()}")</span></div><div><span style="font-size: x-small;"> x = self.linear2(x)</span></div><div><span style="font-size: x-small;"> print(f"x after 2nd linear layer: {x.size()}")</span></div><div><span style="font-size: x-small;"> return x</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;">class EncoderLayer(nn.Module):</span></div><div><span style="font-size: x-small;"> def __init__(self, d_model, ffn_hidden, num_heads, drop_prob):</span></div><div><span style="font-size: x-small;"> super(EncoderLayer, self).__init__()</span></div><div><span style="font-size: x-small;"> self.attention = MultiHeadAttention(d_model=d_model, num_heads=num_heads)</span></div><div><span style="font-size: x-small;"> self.norm1 = LayerNormalization(parameters_shape=[d_model])</span></div><div><span style="font-size: x-small;"> self.dropout1 = nn.Dropout(p=drop_prob)</span></div><div><span style="font-size: x-small;"> self.ffn = PositionwiseFeedForward(d_model=d_model, hidden=ffn_hidden, drop_prob=drop_prob)</span></div><div><span style="font-size: x-small;"> self.norm2 = LayerNormalization(parameters_shape=[d_model])</span></div><div><span style="font-size: x-small;"> self.dropout2 = nn.Dropout(p=drop_prob)</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;"> def forward(self, x):</span></div><div><span style="font-size: x-small;"> residual_x = x # 잔차 연결을 위한 텐서 값 보관</span></div><div><span style="font-size: x-small;"> print("ATTENTION 1")</span></div><div><span style="font-size: x-small;"> x = self.attention(x, mask=None) # 멀티헤드 어텐션 계산 </span></div><div><span style="font-size: x-small;"> print("DROPOUT 1")</span></div><div><span style="font-size: x-small;"> x = self.dropout1(x) # 드롭아웃</span></div><div><span style="font-size: x-small;"> print("ADD AND LAYER NORMALIZATION 1")</span></div><div><span style="font-size: x-small;"> x = self.norm1(x + residual_x) # 잔차 연결 및 레이어 가중치 정규화</span></div><div><span style="font-size: x-small;"> residual_x = x </span></div><div><span style="font-size: x-small;"> print("ATTENTION 2")</span></div><div><span style="font-size: x-small;"> x = self.ffn(x)</span></div><div><span style="font-size: x-small;"> print("DROPOUT 2")</span></div><div><span style="font-size: x-small;"> x = self.dropout2(x)</span></div><div><span style="font-size: x-small;"> print("ADD AND LAYER NORMALIZATION 2")</span></div><div><span style="font-size: x-small;"> x = self.norm2(x + residual_x)</span></div><div><span style="font-size: x-small;"> return x</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;">class Encoder(nn.Module):</span></div><div><span style="font-size: x-small;"> def __init__(self, d_model, ffn_hidden, num_heads, drop_prob, num_layers):</span></div><div><span style="font-size: x-small;"> super().__init__()</span></div><div><span style="font-size: x-small;"> self.layers = nn.Sequential(*[EncoderLayer(d_model, ffn_hidden, num_heads, drop_prob) for _ in range(num_layers)])</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;"> def forward(self, x):</span></div><div><span style="font-size: x-small;"> x = self.layers(x)</span></div><div><span style="font-size: x-small;"> return x</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;">d_model = 512</span></div><div><span style="font-size: x-small;">num_heads = 8</span></div><div><span style="font-size: x-small;">drop_prob = 0.1</span></div><div><span style="font-size: x-small;">batch_size = 30</span></div><div><span style="font-size: x-small;">max_sequence_length = 200</span></div><div><span style="font-size: x-small;">ffn_hidden = 2048</span></div><div><span style="font-size: x-small;">num_layers = 5</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;">encoder = Encoder(d_model, ffn_hidden, num_heads, drop_prob, num_layers)</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;">x = torch.randn((batch_size, max_sequence_length, d_model)) </span></div><div><span style="font-size: x-small;">out = encoder(x)</span></div><div><span style="font-size: x-small;"> </span></div><div><span style="font-size: x-small;">print(out)</span></div></div><div style="text-align: left;"><br /></div><div style="text-align: left;">트랜스포머 인코더 실행 결과는 다음과 같다. </div><div style="text-align: left;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhyi7RlsjhiugF4jOB3zTDEk2_YME7yU6ewLoehfDRWqknUOpMMjmIZcK2ohlDd9g-1rBuViNMznca7b0G7sPiwDnYhY_TclQaqaqKJo5AKIurEHSFFh5A4_Y98pSeFT5L3vZA9IF9hb2QxiJyrAbvXLK_obHoTKkflVdeTdGTZkZ9UqEP33K1EOP-eXh9Z" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="496" data-original-width="1491" height="176" src="https://blogger.googleusercontent.com/img/a/AVvXsEhyi7RlsjhiugF4jOB3zTDEk2_YME7yU6ewLoehfDRWqknUOpMMjmIZcK2ohlDd9g-1rBuViNMznca7b0G7sPiwDnYhY_TclQaqaqKJo5AKIurEHSFFh5A4_Y98pSeFT5L3vZA9IF9hb2QxiJyrAbvXLK_obHoTKkflVdeTdGTZkZ9UqEP33K1EOP-eXh9Z=w534-h176" width="534" /></a></div><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEg7CcKXuByU4of__kJaA_w-Rz_ldb5YmaOfhLKAc3s39gqc12g3bOmSUmKlCHzQ9eay5VVlP5kSMva8cu49lsdSvSJ5HNTlVSnXqnGY0f5gawln-iU5A10ENU1JfEA93saxULrWH6pWhR9mbzxkZ_CuxvxidiYBWFyzDedYybp5gPvenjPjEKpqpXnXXc1O" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="558" data-original-width="1612" height="185" src="https://blogger.googleusercontent.com/img/a/AVvXsEg7CcKXuByU4of__kJaA_w-Rz_ldb5YmaOfhLKAc3s39gqc12g3bOmSUmKlCHzQ9eay5VVlP5kSMva8cu49lsdSvSJ5HNTlVSnXqnGY0f5gawln-iU5A10ENU1JfEA93saxULrWH6pWhR9mbzxkZ_CuxvxidiYBWFyzDedYybp5gPvenjPjEKpqpXnXXc1O=w533-h185" width="533" /></a></div></div><div class="separator" style="clear: both; text-align: center;">트랜스포머 인코더 실행 결과 일부</div><br /></div><div style="text-align: left;"><b><span style="font-size: medium;">결론</span></b></div><div style="text-align: left;"><div>딥러닝 모델 트랜스포머 핵심 코드 개발하는 과정을 살펴봄으로써, 트랜스포머의 기본 동작 메커니즘을 확인해 보았다. </div><div><br /></div><div>트랜스포머는 입력 데이터 토큰들과 출력 데이터 토큰들 간의 순서쌍을 통해, 유사성을 계산한다. 특정 토큰 다음에 어떤 토큰이 생성될 지를 확률적으로 계산하는 어텐션 개념을 사용한다. 멀티헤드 어텐션을 통해 다양한 문맥을 고려한 문장 예측이 가능하다.</div><div><br /></div><div>트랜스포머는 순서가 있는 데이터 토큰의 예측, 생성, 비교 과정을 수학적으로 일반화한 확률 통계 모델이므로, 사실상, 데이터 종류에 구애받지 않는다. 그러므로, 멀티모델 생성AI의 핵심 기술로 사용될 수 있다. <br /></div><div><br /></div><div>다음 시간에는 <a href="https://daddynkidsmakers.blogspot.com/2024/02/blog-post.html">트랜스포머의 디코더 코드 분석</a>을 통해, 디코딩 동작 부분을 확인해 보도록 한다.</div><div><ul style="text-align: left;"><li><a href="https://daddynkidsmakers.blogspot.com/2024/02/blog-post.html">트랜스포머의 디코더 코드 분석</a></li></ul></div><div><br /></div></div><div style="text-align: left;"><b><span style="font-size: medium;">레퍼런스</span></b></div><div style="text-align: left;"><ul style="text-align: left;"><li><a href="https://daddynkidsmakers.blogspot.com/2017/02/blog-post_24.html">머신러닝 딥러닝 신경망 개념, 종류 및 개발</a></li><li><a href="https://daddynkidsmakers.blogspot.com/2022/11/3.html">딥러닝 기반 3차원 스캔 데이터, 포인트 클라우드 학습</a></li><li><a href="https://daddynkidsmakers.blogspot.com/2023/03/generative-ai.html">생성(Generative) AI 오픈소스 딥러닝 모델 Stable Diffusion, ControlNet 개념 및 ComfyUI 사용법</a></li><li><a href="https://github.com/mac999/computer_vision_deeplearning/tree/main">Computer_vision_deeplearning: computer vision based on deep learning lecture materials, Github</a></li><li><a href="https://arxiv.org/abs/1706.03762">Attention Is All You Need</a>, Google</li><li><a href="https://uvadlc-notebooks.readthedocs.io/en/latest/tutorial_notebooks/tutorial6/Transformers_and_MHAttention.html">Transformers and Multi-Head Attention — UvA DL Notebooks v1.2 documentation (uvadlc-notebooks.readthedocs.io)</a></li><li><a href="https://www.youtube.com/watch?v=QCJQG4DuHT0&list=PLTl9hO2Oobd97qfWC40gOSU8C0iu0m2l4">Self Attention in Transformer Neural Networks</a></li><li><a href="https://dev.to/kjlubick/building-an-ml-transformer-in-a-spreadsheet-36g">Building a ML Transformer in a Spreadsheet - DEV Community</a></li><li><a href="https://e2eml.school/transformers.html">Transformers from Scratch (e2eml.school)</a></li><li><a href="https://theaisummer.com/transformer/">How Transformers work in deep learning and NLP: an intuitive introduction | AI Summer (theaisummer.com)</a></li><li><a href="https://rutube.ru/video/14cf5e9556207a6d27c05d9e870266a6/">Building a ML Transformer in a Spreadsheet</a></li><li><a href="https://docs.google.com/spreadsheets/d/1a66brmCdnU8oED2qCzAxX6NNa1KpTeJOv2_pSifXA5A/edit#gid=985698178">ML Transformer in a Spreadsheet Template - Google Sheets</a></li><li><a href="https://docs.google.com/spreadsheets/d/1nbz3z88-0b41dLliRkKcXPnEk_xGd3LmPCqjmUxSWtU/edit#gid=1641427179">ML Transformer in a Spreadsheet - Google Sheets</a></li><li><a href="https://arxiv.org/pdf/1706.03762.pdf">Attention is All you need</a> (<a href="https://silhyeonha-git.tistory.com/16">참고</a>1, <a href="https://cypsw.tistory.com/entry/Attention-Is-All-You-Need">참고</a>2)</li><li><a href="https://www.youtube.com/watch?v=XfpMkf4rD6E">Stanford CS25: V2 I Introduction to Transformers</a></li><li><a href="https://www.youtube.com/watch?v=67n0eudtcqI">Appendix to Building a ML Transformer in a Spreadsheet</a></li><li><a href="https://www.sciencedirect.com/science/article/abs/pii/S0926580521003800">Transformer machine learning language model for auto-alignment of long-term and short-term plans in construction - ScienceDirect</a></li><li><a href="https://www.youtube.com/watch?v=XfpMkf4rD6E">Stanford CS25: V2 I Introduction to Transformers w/ Andrej Karpathy</a></li><li><a href="https://kazemnejad.com/blog/transformer_architecture_positional_encoding/">Transformer Architecture: The Positional Encoding - Amirhossein Kazemnejad's Blog</a> (#<a href="https://www.blossominkyung.com/deeplearning/transfomer-positional-encoding">1</a>)</li><li><a href="https://medium.com/deeper-learning/glossary-of-deep-learning-word-embedding-f90c3cec34ca">Glossary of Deep Learning: Word Embedding | by Jaron Collis | Deeper Learning | Medium</a></li><li><a href="http://mccormickml.com/2016/04/19/word2vec-tutorial-the-skip-gram-model/">Word2Vec Tutorial - The Skip-Gram Model · Chris McCormick (mccormickml.com)</a></li><li><a href="https://synthesis.ai/2023/01/05/modern-generative-ai-an-overview/">Generative AI Models in Image Generation: Overview - Synthesis AI</a></li><li><a href="https://kazemnejad.com/blog/transformer_architecture_positional_encoding/">Transformer Architecture: The Positional Encoding - Amirhossein Kazemnejad's Blog</a></li><li><a href="https://lilianweng.github.io/posts/2018-06-24-attention/">Attention? Attention! | Lil'Log (lilianweng.github.io)</a></li><li><a href="https://arxiv.org/pdf/1601.06733.pdf" style="text-align: center;">Long Short-Term Memory-Networks for Machine Reading</a><span style="text-align: center;">, 2016</span></li><li><span style="text-align: center;"><a href="https://towardsdatascience.com/illustrated-self-attention-2d627e33b20a">Illustrated: Self-Attention. A step-by-step guide to self-attention… | by Raimi Karim | Towards Data Science</a></span></li><li><a href="https://www.analyticsvidhya.com/blog/2019/01/fundamentals-deep-learning-recurrent-neural-networks-scratch-python/">RNN From Scratch | Building RNN Model In Python (analyticsvidhya.com)</a></li><li><a href="https://medium.com/nerd-for-tech/recurrent-neural-networks-3a0adb1d4515">Recurrent Neural Networks From Scratch | by Maciej Balawejder | Nerd For Tech | Medium</a></li><li><a href="https://pub.towardsai.net/building-a-recurrent-neural-network-from-scratch-in-python-3ad244b1054f">Building A Recurrent Neural Network From Scratch In Python | by Youssef Hosni | Towards AI</a></li></ul></div></div>Daddy Makerhttp://www.blogger.com/profile/02275758703365576780noreply@blogger.com0tag:blogger.com,1999:blog-5201956450461596914.post-87092341406155847332023-12-21T18:20:00.000-08:002024-02-29T20:52:39.715-08:00GPU CUDA 기반 데이터 병렬처리 지원 파이썬 Numba 라이브러리 사용법 소개<div style="text-align: left;">이 글은 GPU CUDA 병렬처리를 지원하는 Numba 라이브러리를 간략히 소개한다. CUDA는 현재 딥러닝 기술의 기반처럼 사용되는 사실상 산업표준이다. 딥러닝은 모든 연산이 텐서 행렬 계산이므로, NVIDIA GPU에 내장된 수많은 계산 유닛(실수 계산에 특화된 CPU)들을 사용한다. </div><div style="text-align: left;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgFOA4Uit3qr3snne1HenfGdQiVHxY1Q7L33vxXEGroermoJF1cgSnjkFPj-rodJaCBhN_Fy-qiuHQsrMlsbzQfz4gwbDMnRDAK5QSf_kxj3FhAsLdxb4QEzRyZfM8sBHKEtTXmhwbrDlh6FDFeNoGcueUCuyuT-nCAkUxxbfD3gKjizykA9xuQP7FrXTrv" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="424" data-original-width="855" height="199" src="https://blogger.googleusercontent.com/img/a/AVvXsEgFOA4Uit3qr3snne1HenfGdQiVHxY1Q7L33vxXEGroermoJF1cgSnjkFPj-rodJaCBhN_Fy-qiuHQsrMlsbzQfz4gwbDMnRDAK5QSf_kxj3FhAsLdxb4QEzRyZfM8sBHKEtTXmhwbrDlh6FDFeNoGcueUCuyuT-nCAkUxxbfD3gKjizykA9xuQP7FrXTrv=w400-h199" width="400" /></a></div><div class="separator" style="clear: both; text-align: center;">CUDA 아키텍처(<a href="https://docs.nvidia.com/deploy/mps/index.html" style="text-align: left;">Multi-Process Service :: GPU Deployment and Management Documentation</a>)</div></div><div style="text-align: left;"><br /></div><div style="text-align: left;">CUDA는 내장된 수많은 계산 유닛에 입력 데이터를 할당하고, 행렬연산을 하여, 출력된 데이터를 CPU 메모리가 접근할 수 있도록 데이터 고속 전송/교환하는 역할을 한다. 그러므로, 딥러닝 모델 학습 성능은 GPU CUDA 성능에 직접적 영향을 받는다. 이 글은 파이썬에서 CUDA를 이용해 수치해석 등 계산성능을 극대화할 수 있는 방법과 간단한 예제를 살펴본다.</div><div style="text-align: left;"><ul style="text-align: left;"><li>Github - <a href="https://github.com/mac999/cuda">CUDA programming example (github.com)</a></li></ul></div><div style="text-align: left;"><a href="https://github.com/mac999/cuda"></a></div><div><br /></div><div style="text-align: left;"><b>GPU CUDA 소개</b></div><div style="text-align: left;">CUDA는 게임 화면에 렌더링되는 3차원 이미지를 2차원 픽셀에 맵핑하기 위한 수많은 행렬처리를 실시간 병렬 처리할 수 있도록 개발되어 왔다. 이런 이유로, 행렬 고속 연산이 필요한 딥러닝 학습에 적극 사용된 것이다. </div><div style="text-align: left;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgCYgbbKX3y5gRr8-p9KN4xpE9osLrM9gQoc9De4k5CZRo1vWflMSbtqMFqdkFTr8lyZPlM2RiaXd6_-4ABq_79JFH_9sQjuGrQiJzoUhR1Ods0H_6ejOx4eSb1rMyR36AH6ZCB1KW_5uiFkNLyb-ZZvUYPpfEq8nSIkEcbvd_waHodNsniFJdXmaK39H-o" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="359" data-original-width="850" height="169" src="https://blogger.googleusercontent.com/img/a/AVvXsEgCYgbbKX3y5gRr8-p9KN4xpE9osLrM9gQoc9De4k5CZRo1vWflMSbtqMFqdkFTr8lyZPlM2RiaXd6_-4ABq_79JFH_9sQjuGrQiJzoUhR1Ods0H_6ejOx4eSb1rMyR36AH6ZCB1KW_5uiFkNLyb-ZZvUYPpfEq8nSIkEcbvd_waHodNsniFJdXmaK39H-o=w400-h169" width="400" /></a></div><div class="separator" style="clear: both; text-align: center;">CUDA 기반 실시간 텐서 행렬 연산 결과</div><div style="text-align: left;"><br /></div><div style="text-align: left;">CUDA는 오랫동안 개발자의 요구사항을 반영해 발전되어, 개발 플랫폼으로서 탄탄한 생태계를 구축했다.</div><div style="text-align: left;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhftlVpr_rOKB6El2dcuRKWC3mEO8uoMvQ_hmA0MV3Ts2fAjQbcRNWt4xz1mL_io4mUH4Gs7_4tsWtmr5bzAYtzCgIzz4ykhZGtVm-l8_i7KA_cvQPF_JuwA3oh9jAYuyENm_Xd5QCLDQGIQPxYbXdUio2gfXvqqZ9TTIcTV-QzEwPZYSKwiht3dDnA-rpd" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="600" data-original-width="1100" height="219" src="https://blogger.googleusercontent.com/img/a/AVvXsEhftlVpr_rOKB6El2dcuRKWC3mEO8uoMvQ_hmA0MV3Ts2fAjQbcRNWt4xz1mL_io4mUH4Gs7_4tsWtmr5bzAYtzCgIzz4ykhZGtVm-l8_i7KA_cvQPF_JuwA3oh9jAYuyENm_Xd5QCLDQGIQPxYbXdUio2gfXvqqZ9TTIcTV-QzEwPZYSKwiht3dDnA-rpd=w400-h219" width="400" /></a></div><div class="separator" style="clear: both; text-align: center;">NVIDIA 개발자 사이트 (<a href="https://developer.nvidia.com/" style="text-align: left;">NVIDIA Developer</a>)</div><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEj5iMk-PXJp3UGa-WG24UPGhQnRms5LvyJSU1YsBmVhNOIzqz7J9snFG_LOI-nb3l9knK_RjLVOVCI7cohIkflrziiZvKJSm53W-9DcEp3eOXNBEaxp3K5JJEU6SUBvRDpdahMXCX624xQV066vkoHnMvA7quOy0KG_aSvUlsS9x1EnKRmEs7Ept3ZgaprG" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="1134" data-original-width="1935" height="188" src="https://blogger.googleusercontent.com/img/a/AVvXsEj5iMk-PXJp3UGa-WG24UPGhQnRms5LvyJSU1YsBmVhNOIzqz7J9snFG_LOI-nb3l9knK_RjLVOVCI7cohIkflrziiZvKJSm53W-9DcEp3eOXNBEaxp3K5JJEU6SUBvRDpdahMXCX624xQV066vkoHnMvA7quOy0KG_aSvUlsS9x1EnKRmEs7Ept3ZgaprG" width="320" /></a></div></div><div class="separator" style="clear: both; text-align: center;">CUDA 기반 레이트레이싱 렌더링 결과 (<a href="https://raytracey.blogspot.com/2016/01/gpu-path-tracing-tutorial-3-take-your.html" style="text-align: left;">Ray Tracey's blog: GPU path tracing tutorial 3: GPU</a>)</div><br /></div><div style="text-align: left;">사실, 많은 스타트업이 이런 기능을 지원하는 딥러닝용 AI 칩을 <a href="https://www.arm.com/glossary/fpga#:~:text=Field%20Programmable%20Gate%20Arrays%20(FPGAs,requirements%20after%20the%20manufacturing%20process.">FPGA</a> 기법 등을 이용해 개발, 홍보하고 있으나, 이런 개발자 지원도구와 플랫폼 생태계 없다면, 산업계에서는 의미가 없다고 볼 수 있다. </div><br /><b>Numba 소개</b></div><div style="text-align: left;">Numba(넘바)는 파이썬 기반 CUDA GPU 프로그래밍을 지원한다. 넘바는 컴파일 기술을 지원하여, CPU와 GPU모드에서 코딩되는 데이터구조, 함수호출을 추상화한다. 넘바는 NVIDIA의 CUDA 함수와 설정을 랩핑한 고수준의 함수 API를 제공한다. 이를 통해, 개발자가 CUDA의 세부 설정에 신경쓸 필요없이, 데이터 병렬 처리 개발에만 집중할 수 있다. </div><div style="text-align: left;"><br /></div><div style="text-align: left;"><b>개발환경</b></div><div style="text-align: left;">넘바 개발 환경은 다음과 같다. </div><div style="text-align: left;"><ul style="text-align: left;"><li>NVIDIA Compute Capability 5.0 이상 CUDA 지원 GPU 장착 PC (2023.12 시점)</li><li>NVIDIA CUDA 11.2 이상 (<a href="https://numba.readthedocs.io/en/stable/cuda/minor_version_compatibility.html#minor-version-compatibility">링크</a> 참고)</li><li>NVIDIA TX1, TX2, 자비애, 젯슨 나노 </li><li>GTX 9, 10, 16 시리즈. RTX 20, 30, 40 시리즈. H100 시리즈</li></ul><div>CONDA 환경의 경우, 다음과 같이 CUDA 툴킷을 자동 설치할 수 있다.</div><div>conda install cudatoolkit </div></div><div style="text-align: left;"> </div><div style="text-align: left;">넘바는 cuda python을 이용해 NVIDIA GPU CUDA와 바인딩한다. </div><div style="text-align: left;">conda install nvidia::cuda-python</div><div style="text-align: left;"><br /></div><div style="text-align: left;"><a href="https://numba.pydata.org/numba-doc/0.17.0/user/installing.html">설치</a>는 다음과 같다.</div><div style="text-align: left;">conda install numba</div><div style="text-align: left;"><br /></div><div style="text-align: left;"><b>CUDA 프로그래밍 개념</b></div><div style="text-align: left;">이 장은 CUDA 프로그래밍 개념을 간단히 짚고 넘어간다. 사실 넘바는 이 부분까지 어느정도 추상화해 놓았으므로, 이런 개념이 있구나 정도만 생각해도 된다. </div><div style="text-align: left;"><br /></div><div style="text-align: left;">CUDA는 실시간 렌더링에 필요한 텍스처 공유 메모리 구조와 기존 CPU설계에 영향을 받아, 그리드 > 블럭 > 쓰레드로 아키텍처가 설계되었다. 쓰레드는 행렬 계산을 지원하는 프로세스라 생각하면 된다. </div><div style="text-align: left;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgczYAHSaHUlbZvCEHKtb5SWQO-zuofdKHq3VBp5SvcDLKBxNDKwpMyvFFRaq_aB7wL39gPXCMrIsrJrUVjvXmWMXi2Sj9YXALdR6xPZVLXwPSruh8sfb8WDfpafCwaOhGoeaTtarhTRIkg-IOzSkvoN39hOil-uhqsC-nfD5pYhkZw33TKMDdb-CXKlN1i" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="611" data-original-width="935" height="261" src="https://blogger.googleusercontent.com/img/a/AVvXsEgczYAHSaHUlbZvCEHKtb5SWQO-zuofdKHq3VBp5SvcDLKBxNDKwpMyvFFRaq_aB7wL39gPXCMrIsrJrUVjvXmWMXi2Sj9YXALdR6xPZVLXwPSruh8sfb8WDfpafCwaOhGoeaTtarhTRIkg-IOzSkvoN39hOil-uhqsC-nfD5pYhkZw33TKMDdb-CXKlN1i=w400-h261" width="400" /></a></div><div class="separator" style="clear: both; text-align: center;">CUDA 논리-물리 구조</div><br /></div><div style="text-align: left;">각 쓰레드들은 행렬 병렬 계산할 수 있다. 다음 그림은 이를 보여준다.</div><div style="text-align: left;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEilmXZ-MQ3GZPlQvZMlsouwtelEtgDV3zTkNC8ljQvxH-9QABb_X6rkgRHRFAO6NvGPl7HKyufszCI0e1qXijBjYJ6z5OqIXoxzSjASn29ZZBfD1dwMLtASWEhiLsxSwpfZhJdkyALlwkWb8hUr6200eFHu1XbRkNM7-q4p-3JpA1tm4P_3g4xzbmWFqE1e" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="436" data-original-width="545" height="320" src="https://blogger.googleusercontent.com/img/a/AVvXsEilmXZ-MQ3GZPlQvZMlsouwtelEtgDV3zTkNC8ljQvxH-9QABb_X6rkgRHRFAO6NvGPl7HKyufszCI0e1qXijBjYJ6z5OqIXoxzSjASn29ZZBfD1dwMLtASWEhiLsxSwpfZhJdkyALlwkWb8hUr6200eFHu1XbRkNM7-q4p-3JpA1tm4P_3g4xzbmWFqE1e=w400-h320" width="400" /></a></div><div class="separator" style="clear: both; text-align: center;">CUDA 그리드 계층 구조 (<a href="https://www.researchgate.net/figure/Figure-2-Execution-model-of-a-CUDA-program-on-NVidias-GPU-Hierarchy-grid-blocks-and_fig2_321666991" style="text-align: left;">Execution model of a CUDA program on NVidia's GPU: Hierarchy grid)</a></div><br />각 쓰레드에서 병렬 실행되는 함수를 커널이라 한다. 다음은 함수가 실행될 그리드, 블럭, 쓰레드가 명시된 CUDA 코드 예시를 보여준다. 하나의 블럭에서 256개 쓰레드를 사용한다고 가정하면, 다음과 같이 코딩된다. </div><div style="text-align: left;"><br /></div><div style="text-align: left;"><div><span style="font-size: x-small;">__global__</span></div><div><span style="font-size: x-small;">void add(int n, float *x, float *y)</span></div><div><span style="font-size: x-small;">{</span></div><div><span style="font-size: x-small;"> int index = threadIdx.x;</span></div><div><span style="font-size: x-small;"> int stride = blockDim.x;</span></div><div><span style="font-size: x-small;"> for (int i = index; i < n; i += stride)</span></div><div><span style="font-size: x-small;"> y[i] = x[i] + y[i];</span></div><div><span style="font-size: x-small;">}</span></div><div><br /></div><div>CUDA를 사용한 커널함수 add 호출은 다음과 같다. <<<>>> 키워드를 통해 사용할 블록, 쓰레드 수를 지정하고 있다. </div><div><div>add<<<1, 256>>>(N, x, y);</div><div><br /></div></div><div>add함수가 호출되면, 다음과 같이 내부적으로 실행된다. </div></div><div style="text-align: left;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEiA8xqi7F1uXeYrZoV4IP1QvyEgkMpJ1ElzMMVVgwNt7aC_vlBbo-qi94-osItwgUf3UskNOqarxShhcfpLoeUH2niAknkn05dr3VkmcPF-nGS_aUQieryb-vn1ynP4d063PWXlOdWCEB7P0w4lKDW1Il6dI3nT0RUi76cHJ1K7pd-YBkvkqB6o3ptPxZQ4" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="516" data-original-width="969" height="239" src="https://blogger.googleusercontent.com/img/a/AVvXsEiA8xqi7F1uXeYrZoV4IP1QvyEgkMpJ1ElzMMVVgwNt7aC_vlBbo-qi94-osItwgUf3UskNOqarxShhcfpLoeUH2niAknkn05dr3VkmcPF-nGS_aUQieryb-vn1ynP4d063PWXlOdWCEB7P0w4lKDW1Il6dI3nT0RUi76cHJ1K7pd-YBkvkqB6o3ptPxZQ4=w449-h239" width="449" /></a></div><div class="separator" style="clear: both; text-align: center;">병렬 데이터 처리를 위한 쓰레드 인덱스 계산 예시(<a href="https://developer.nvidia.com/blog/even-easier-introduction-cuda/" style="text-align: left;">An Even Easier Introduction to CUDA | NVIDIA Technical Blog</a>)</div><br />이런 방식을 통해, 3차원, 4차원 등 고차원 대용량 행렬을 그리드, 블럭, 쓰레드로 나눠, 병렬처리할 수 있다. 모든 수치해석모델은 행렬 텐서로 계산되므로, 사실상, 수학적인 계산 모델은 CUDA에서 실행될 수 있다.<br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgjSrrcSXi7aZS6VNoaiMJX5KFAIwyt3QaKqDD2hKmylT8xT71UHMB5r3ehSNV9UzOVqKyekp25MIbuyIikxjFxsi4rzG0U_EU1BLIkrqgsQbh8t5F-RBTzH75tfwUqizGPjchKKfhTohqFrXU6Anlv5u7bnWk1xVG3mC8C2nvxdXUD6W2WGojbBL2h9qKF" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="367" data-original-width="726" height="203" src="https://blogger.googleusercontent.com/img/a/AVvXsEgjSrrcSXi7aZS6VNoaiMJX5KFAIwyt3QaKqDD2hKmylT8xT71UHMB5r3ehSNV9UzOVqKyekp25MIbuyIikxjFxsi4rzG0U_EU1BLIkrqgsQbh8t5F-RBTzH75tfwUqizGPjchKKfhTohqFrXU6Anlv5u7bnWk1xVG3mC8C2nvxdXUD6W2WGojbBL2h9qKF=w400-h203" width="400" /></a></div><div class="separator" style="clear: both; text-align: center;">행렬(선형대수) 연산 예<span style="text-align: left;">(</span><a href="https://web.stanford.edu/~boyd/vmls/vmls.pdf" style="text-align: left;">선형대수</a><span style="text-align: left;">)</span></div><br /><b>넘바에서 CUDA 코딩 개념</b></div><div style="text-align: left;">넘바에서 간단한 커널 함수를 만들어보자. 배열을 입력받아, 배열값을 하나 증가하는 병렬 처리 함수를 만들어 본다. </div><div style="text-align: left;"><br /></div><div style="text-align: left;"><div>from numba import cuda</div><div><br /></div></div><div style="text-align: left;"><div>@cuda.jit</div><div>def increment_by_one(an_array):</div><div><div> tx = cuda.threadIdx.x # cuda 객체에 포함된 쓰레드 ID 획득</div><div> ty = cuda.blockIdx.x # 그리드내 블럭 ID 획득</div><div> bw = cuda.blockDim.x # 블럭 폭(쓰레드 갯수) 획득</div><div> # Compute flattened index inside the array</div><div> pos = tx + ty * bw # 해당 스레드 접근 인덱스 계산</div><div> if pos < an_array.size: # 전달된 배열 경계 체크</div><div> an_array[pos] += 1 # 배열값 하나씩 증가</div><div><br /></div></div><div>이때 사용된 @는 cuda 데코레이터로서, 파이썬 코드를 CUDA에서 처리될 수 있는 코드로 자동 컴파일하는 역할을 한다(<a href="https://numba.readthedocs.io/en/stable/user/jit.html#basic-usage">참고</a>). 선언된 커널 함수는 GPU 다중쓰레드실행 아키텍처에 직접 맵핑된다. 함수 호출은 다음과 같다. 이때 커널함수는 비동기적으로 실행된다. </div><div><br /></div><div><div>threadsperblock = 32</div><div>blockspergrid = (an_array.size + (threadsperblock - 1)) // threadsperblock</div><div>increment_by_one[blockspergrid, threadsperblock](an_array)</div></div></div><div style="text-align: left;"><br /></div><div style="text-align: left;">커널에 전달된 배열값은 CPU메모리와 CUDA메모리간에 복사되어져야 한다. 이를 위해, NumPy로부터 CPU > CUDA 복사하는 다음 함수를 제공한다. </div><div style="text-align: left;">cpu_mem = np.arrage(10)</div><div style="text-align: left;">cuda_mem = cuda.to_device(cpu_mem)</div><div style="text-align: left;"><br /></div><div style="text-align: left;">CUDA > CPU 복사 함수는 다음과 같다. </div><div style="text-align: left;">cpu_mem = cuda_mem.copy_to_host()</div><div style="text-align: left;"><br /></div><div style="text-align: left;">다음은 커널 함수 내 코드 실행 시 참고사항이다. 커널 함수는 GPU 쓰레드 내에서 실행되므로, 제약사항이 많다. </div><div style="text-align: left;"><ul style="text-align: left;"><li>커널 함수 내에서 메모리를 동적 할당 가능(참고 - <a href="https://numba.readthedocs.io/en/stable/cuda/memory.html">Memory management</a>).</li><li>예외처리 지원 안됨. 일반적으로 Zero Divide 에러 발생 안됨 </li><li>list, dic, set 사용 안됨</li><li>print 사용 가능</li><li>재귀함수 지원</li><li>abs(), bool, complex, enumerate(), float, int, len(), min(), max(), pow(), range(), round(), zip() 지원</li><li>cmath, math 라이브러리 지원</li><li>operator 연산자 add, eq, ge, sub, xor 등 함수 지원</li><li>NumPy 일부 함수 지원(예. sin, cos, tan, deg2rad 등). 배열 생성 등 메모리 할당 관련 함수는 지원 안됨</li><li><a href="https://numba.readthedocs.io/en/stable/cuda/fastmath.html">Fastmath</a>, cuda.random, cuda.select_device 함수 등 지원</li><li>공유 메모리 락 등 처리 위한 <a href="https://numba.pydata.org/numba-doc/latest/cuda/intrinsics.html">atomic</a>, <a href="https://numba.pydata.org/numba-doc/latest/developer/threading_implementation.html">thread</a>, lock 함수 지원</li></ul><div>커널 함수 디버깅을 위해서는 다음과 같은 pdb를 사용해야 한다. </div><div><div>from pdb import set_trace; set_trace()</div></div><div><br /></div><div>이외, 기존 쿠다 코드인 cu 루틴을 호출할 수 있는 기능을 지원한다. </div><div><div>@cuda.jit(link=['functions.cu'])</div><div>def multiply_vectors(r, x, y):</div><div> i = cuda.grid(1)</div><div><br /></div><div> if i < len(r):</div><div> r[i] = mul(x[i], y[i])</div></div><div><br /></div><div><b>병렬 처리 코딩해 보기</b></div><div>이제 모든 재료가 모였으니, 두 개 거대한 벡터 1차원 행렬을 만들어, 난수값을 할당하고, 이를 더하는 함수를 만들고, 이를 병렬처리해 보자. 다음을 코딩하고, 실행해 본다.</div></div><div style="text-align: left;"><div><span style="font-size: x-small;">import numpy as np</span></div><div><span style="font-size: x-small;">from numba import cuda</span></div><div><span style="font-size: x-small;">from tqdm import tqdm</span></div><div><span style="font-size: x-small;">import time</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;">@cuda.jit</span></div><div><span style="font-size: x-small;">def f(a, b, c):</span></div><div><span style="font-size: x-small;"> tid = cuda.grid(1) </span><span style="font-size: small;"># threadIdx.x + (blockIdx.x * blockDim.x)</span></div><div><span style="font-size: x-small;"> # if tid % 100 == 0:</span></div><div><span style="font-size: x-small;"> # print('tid=', tid)</span></div><div><span style="font-size: x-small;"> size = len(c)</span></div><div><span style="font-size: x-small;"> if tid < size:</span></div><div><span style="font-size: x-small;"> c[tid] = a[tid] + b[tid]</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;">N = 10000000</span></div><div><span style="font-size: x-small;">a = cuda.to_device(np.random.random(N)) # 천만개 난수 생성 및 a 배열 값 할당</span></div><div><span style="font-size: x-small;">b = cuda.to_device(np.random.random(N)) # b 배열 값 할당</span></div><div><span style="font-size: x-small;">c = cuda.device_array_like(a) # 계산된 값 전달받을 c 배열 생성</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;"># CPU 모드 커널 함수 계산 성능 측정</span></div><div><span style="font-size: x-small;">start_time_cpu = time.time() </span></div><div><span style="font-size: x-small;">f.forall(len(a))(a, b, c)</span></div><div><span style="font-size: x-small;">end_time_cpu = time.time()</span></div><div><span style="font-size: x-small;">cpu_execution_time = end_time_cpu - start_time_cpu</span></div><div><span style="font-size: x-small;">print(f'CPU len={len(a)}, ', c.copy_to_host())</span></div><div><br /></div><div><span style="font-size: x-small;"># CUDA 모드 커널 함수 계산 성능 측정</span></div><div><span style="font-size: small;">start_time_cuda = time.time()</span></div><div><span style="font-size: x-small;">nthreads = 256 # Enough threads per block for several warps per block</span></div><div><span style="font-size: x-small;">nblocks = (len(a) // nthreads) + 1 # Enough blocks to cover the entire vector depending on its length</span></div><div><span style="font-size: x-small;">f[nblocks, nthreads](a, b, c)</span></div><div><span style="font-size: x-small;">end_time_cuda = time.time()</span></div><div><span style="font-size: x-small;">cuda_execution_time = end_time_cuda - start_time_cuda</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;">print(f'CUDA len={len(a)}, ', c.copy_to_host())</span></div><div><span style="font-size: small;">print(f"CUDA threads: {nthreads}, blocks: {nblocks}")</span></div><div><span style="font-size: x-small;"><br /></span></div><div><span style="font-size: x-small;">print(f"CPU Execution Time: {cpu_execution_time:.6f} seconds")</span></div><div><span style="font-size: x-small;">print(f"CUDA Execution Time: {cuda_execution_time:.6f} seconds")</span></div></div><div style="text-align: left;"> </div><div style="text-align: left;">실행 결과는 다음과 같다. CUDA 병렬처리 계산속도가 CPU 모드 보다 4배 가까이 빠른 것을 볼 수 있다. </div><div style="text-align: left;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEguglGidbEjoVy6VCmMDJgV-Bc4FmEkcrlxqjRQmtUUB5gjv0zg9ugL2YEsBNyQKmd_pA_dWRwXtTRsGLPxqCci4FgOwb5aGIOMjDzhUwwUXu_OjPtVsyhgVO2noUOx3DgKUgGHVkfl0q_Lqd8QyAgYVw9TNrVNi5jCg7Wt-ZeFjS4etayulWjJg1zqit0n" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="403" data-original-width="680" height="190" src="https://blogger.googleusercontent.com/img/a/AVvXsEguglGidbEjoVy6VCmMDJgV-Bc4FmEkcrlxqjRQmtUUB5gjv0zg9ugL2YEsBNyQKmd_pA_dWRwXtTRsGLPxqCci4FgOwb5aGIOMjDzhUwwUXu_OjPtVsyhgVO2noUOx3DgKUgGHVkfl0q_Lqd8QyAgYVw9TNrVNi5jCg7Wt-ZeFjS4etayulWjJg1zqit0n" width="320" /></a></div><br /><b>마무리 </b></div><div style="text-align: left;">이 글에서 GPU CUDA 병렬처리를 지원하는 Numba 라이브러리를 간략히 소개하고 사용법을 정리해 보았다. Numba를 이용해, 3차원, 4차원 등 고차원 대용량 행렬을 그리드, 블럭, 쓰레드로 나눠, 병렬처리할 수 있다. </div><div style="text-align: left;"><br /></div><div style="text-align: left;">모든 수치해석모델은 행렬 텐서로 계산되므로, 사실상, 수학적인 계산 모델은 CUDA에서 실행될 수 있다. 이를 통해, 행렬 텐서 계산량이 많은 딥러닝 뿐 아니라, 계산 기하학, 컴퓨터 그래픽스, 시뮬레이션 등을 효과적으로 계산할 수 있다. </div><div style="text-align: left;"><br /></div><div style="text-align: left;">단, 병렬처리를 위해서는 공유되는 메모리(변수, 배열, 행렬 등)의 수정, 접근 사용 시 병렬성을 고려한 코딩 기법이 선행 되어야 한다(동시성, 원자성 데이터 Lock 처리 등. 참고 - <a href="http://b3.stmik-banjarbaru.ac.id/data.bc/18.%20Programming/2013%20CUDA%20Programming%20A%20Developer%92s%20Guide%20to%20Parallel%20Computing%20with%20GPUs.pdf">CUDA Programming: A Developers Guide to Parallel Computing with GPUs</a>). 아울러, CUDA 자체에서 지원되는 라이브러리 함수들을 제외하고는 커널 함수에서는 전혀 사용할 수 없으므로, 복잡한 라이브러리 함수 사용시 이를 CUDA 함수를 이용해 포팅하는 노력이 있어야 한다.</div><div style="text-align: left;"><br /></div><div style="text-align: left;">참고로, 다음 링크 확인해 보면, 좀 더 다양한 사용 예제를 확인할 수 있다.</div><div style="text-align: center;"><br /></div><div style="text-align: center;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgIq2xKXWG7KU0lp0jKIrNmQu9h_-sB-atriiE6DBpHZUQUM3C5AgcXZ7Hrxen5K_vyqd-DpGJdmPtMGNxu9priSkCxPNYscr_eKk5P_PdlABtvb-XjJ3N89oXPmteTuDed916hT9XbD7loZQ7iDJoj5E5jyaCkECk5lNxAjynev4Byfq5zANOlmdMrFyqW" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="570" data-original-width="1014" height="180" src="https://blogger.googleusercontent.com/img/a/AVvXsEgIq2xKXWG7KU0lp0jKIrNmQu9h_-sB-atriiE6DBpHZUQUM3C5AgcXZ7Hrxen5K_vyqd-DpGJdmPtMGNxu9priSkCxPNYscr_eKk5P_PdlABtvb-XjJ3N89oXPmteTuDed916hT9XbD7loZQ7iDJoj5E5jyaCkECk5lNxAjynev4Byfq5zANOlmdMrFyqW" width="320" /></a></div></div><div style="text-align: center;"><a href="https://numba.readthedocs.io/en/stable/cuda/examples.html">Example</a> (Laplace’s equation simulation example)</div><div style="text-align: center;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEiPp9b54N_I1hFrBHE5uGbLAcxom7pLvNWMUPe71wtAjHN_o7Eas-TklkPMAHfFBxTM22bG9hi95aXtEsCIz7QN2Tavl4fa339ReFlKltsh1EpemsuUQsuCspiNQC6l8jxxMqDFjisWHrQholjBBQvPdprtD6WFhdV-eFjr0dibum9FIwVgQkJ6aODryE-l" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="489" data-original-width="924" height="169" src="https://blogger.googleusercontent.com/img/a/AVvXsEiPp9b54N_I1hFrBHE5uGbLAcxom7pLvNWMUPe71wtAjHN_o7Eas-TklkPMAHfFBxTM22bG9hi95aXtEsCIz7QN2Tavl4fa339ReFlKltsh1EpemsuUQsuCspiNQC6l8jxxMqDFjisWHrQholjBBQvPdprtD6WFhdV-eFjr0dibum9FIwVgQkJ6aODryE-l" width="320" /></a></div></div><div style="text-align: center;"><a href="https://numba.readthedocs.io/en/stable/developer/mission.html">Numba Mission Statement</a></div><div style="text-align: left;"><br /></div><div style="text-align: left;"><b>레퍼런스</b></div><div style="text-align: left;"><ul style="text-align: left;"><li><a href="https://web.stanford.edu/~boyd/vmls/vmls.pdf">Applied Linear Algebra, Stanford</a></li><li><a href="https://github.com/NVIDIA/cuda-python">NVIDIA/cuda-python: CUDA Python Low-level Bindings (github.com)</a></li><li><a href="https://www.nvidia.com/en-us/geforce/news/geforce-gtx-ray-tracing-coming-soon/">Accelerating The Real-Time Ray Tracing Ecosystem: DXR For GeForce RTX and GeForce GTX | GeForce News | NVIDIA</a></li><li><a href="https://docs.nvidia.com/deploy/mps/index.html">Multi-Process Service :: GPU Deployment and Management Documentation (nvidia.com)</a></li><li><a href="https://numba.readthedocs.io/en/stable/cuda/examples.html">Examples — Numba documentation</a></li><li><a href="https://nyu-cds.github.io/python-numba/05-cuda/">Introduction to Numba: CUDA Programming (nyu-cds.github.io)</a></li><li><a href="https://diglib.eg.org/bitstream/handle/10.2312/egt20211037/CUDA_day2.pdf?sequence=3&isAllowed=y">Microsoft</a>, CPU programming</li><li><a href="http://b3.stmik-banjarbaru.ac.id/data.bc/18.%20Programming/2013%20CUDA%20Programming%20A%20Developer%92s%20Guide%20to%20Parallel%20Computing%20with%20GPUs.pdf">CUDA Programming: A Developers Guide to Parallel Computing with GPUs</a></li></ul></div><div style="text-align: left;"><br /></div><div style="text-align: left;"><br /></div>Daddy Makerhttp://www.blogger.com/profile/02275758703365576780noreply@blogger.com0tag:blogger.com,1999:blog-5201956450461596914.post-77884268571582858122023-12-13T17:19:00.000-08:002023-12-22T03:04:00.646-08:003차원 포인트 클라우드 Segment 모델 Anything 3D 소개<p>이 글은 3차원 포인트 클라우드 Segment 모델 Anything 3D를 간략히 설명한다. </p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEia17GzDj3f5hInVaZIRSQs0dT7hjZty_qnO-QJwAYRMdZi39bFexwHNM6SF2ENQI2OBHEDHJ7_I4uT9v7KYNlUSDGhWPRLguqMry2Bo0LxDnsJqxlJ8DkSK-UHG5WtZV0hIzpCsesskEpVkPd9JXXE-3D49nndZzfNBder4tXg1Ohfb5Bd00LS6LdCSCIp" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="604" data-original-width="2750" height="118" src="https://blogger.googleusercontent.com/img/a/AVvXsEia17GzDj3f5hInVaZIRSQs0dT7hjZty_qnO-QJwAYRMdZi39bFexwHNM6SF2ENQI2OBHEDHJ7_I4uT9v7KYNlUSDGhWPRLguqMry2Bo0LxDnsJqxlJ8DkSK-UHG5WtZV0hIzpCsesskEpVkPd9JXXE-3D49nndZzfNBder4tXg1Ohfb5Bd00LS6LdCSCIp=w534-h118" width="534" /></a></div><div class="separator" style="clear: both; text-align: center;">Segment Anything 3D 예시</div><div><br /></div><div><b>머리말</b></div><div><div>홍콩대에서 개발한 Segment Anything 모델은 2D 이미지 세그먼트 정보를 3D 공간에 맵핑해, 3D 세그먼트를 예측한다. </div></div><div><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgXOaRQ5yUoks8z9nPF1xy34s1FlRGxBPd_ljZTqJVDz05Y-q2z7BmTxvWVcNkIsDDvq1gpMItHwdTHt-q6wmMEkHVIPwkVzSEkheaFWz9gJJOhwICnszWyelTa85G4Ld6WxvVBM7wDAswLz-1eGv9AbY9prWjIx20ueAmS4u1BWVfPtVYZd7PelUbATRwY/s998/a2.JPG" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="440" data-original-width="998" height="219" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgXOaRQ5yUoks8z9nPF1xy34s1FlRGxBPd_ljZTqJVDz05Y-q2z7BmTxvWVcNkIsDDvq1gpMItHwdTHt-q6wmMEkHVIPwkVzSEkheaFWz9gJJOhwICnszWyelTa85G4Ld6WxvVBM7wDAswLz-1eGv9AbY9prWjIx20ueAmS4u1BWVfPtVYZd7PelUbATRwY/w499-h219/a2.JPG" width="499" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div><div><div><br /></div><div><b>설치 방법</b></div><div><a href="https://github.com/Pointcept/SegmentAnything3D">소스 코드</a>를 다운로드 받은 후, 다음과 같이 터미널에서 실행한다.</div><div>conda create -n sam3d python=3.8 -y</div><div>conda activate sam3d</div><div>conda install pytorch==1.11.0 torchvision==0.12.0 torchaudio==0.11.0 cudatoolkit=11.3 -c pytorch</div><div>conda install plyfile -c conda-forge -y</div><div>pip install scikit-image opencv-python open3d imageio</div><div>pip install git+https://github.com/facebookresearch/segment-anything.git </div><div><br /></div><div>cd libs/pointops</div><div>python setup.py install</div><div>TORCH_CUDA_ARCH_LIST="ARCH LIST" python setup.py install</div><div>cd ../..</div><div><br /></div><div><b>데이터 준비 방법</b></div><div>다음과 같이 테스트 데이터셋을 다운로드한다.</div><div><ul style="text-align: left;"><li>ScanNet: http://www.scan-net.org</li></ul></div><div><br /></div><div>ScanNet 전처리 코드를 다음과 같이 실행한다.</div><div>python scannet-preprocess/preprocess_scannet.py --dataset_root ${RAW_SCANNET_DIR} --output_root ${PROCESSED_SCANNET_DIR}</div><div><br /></div><div>RGBD 데이터를 준비한다 (<a href="https://github.com/wbhu/BPNet">링크</a>).</div><div>python scannet-preprocess/prepare_2d_data/prepare_2d_data.py --scannet_path data/scannetv2 --output_path data/scannetv2_images --export_label_images</div><div><br /></div><div><b>모델 실행하기</b></div><div>다음과 같이 환경 변수를 설정한 후, sam3d.py를 실행한다.</div><div><br /></div><div># RGB_PATH: the path of rgb data</div><div># DATA_PATH: the path of pointcload data</div><div># SAVE_PATH: Where to save the pcd results</div><div># SAVE_2DMASK_PATH: Where to save 2D segmentation result from SAM</div><div># SAM_CHECKPOINT_PATH: the path of checkpoint for SAM</div><div><br /></div><div>python sam3d.py --rgb_path $RGB_PATH --data_path $DATA_PATH --save_path $SAVE_PATH --save_2dmask_path $SAVE_2DMASK_PATH --sam_checkpoint_path $SAM_CHECKPOINT_PATH </div><div><br /></div><div>데이터 처리 파이프라인은 다음과 같이 실행된다.</div><div><br /></div><div>SAM 마스크: 생성 SAM을 사용하여 2D 프레임에서 세그먼트 마스크를 가져온 다음, 깊이 정보를 통해 3D 공간에 매핑한다.</div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEg4xPcFbzAcXCD46TJganYdETARt9ijNzAHB5_truXYIPRvMTE9lWDr3ElKdyfm5Ptb8hfQNdLWq9ItB1CYGISza6KrvYZO6y4cy3C963lov0Tcgm24pDkckhqwTirRVngrQTPQ5-MehiriXHlb0lu5zG8TkOsl8b8IHZw_FpS56POPx1TzW2xrxu0tppxU" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="696" data-original-width="1928" height="145" src="https://blogger.googleusercontent.com/img/a/AVvXsEg4xPcFbzAcXCD46TJganYdETARt9ijNzAHB5_truXYIPRvMTE9lWDr3ElKdyfm5Ptb8hfQNdLWq9ItB1CYGISza6KrvYZO6y4cy3C963lov0Tcgm24pDkckhqwTirRVngrQTPQ5-MehiriXHlb0lu5zG8TkOsl8b8IHZw_FpS56POPx1TzW2xrxu0tppxU=w400-h145" width="400" /></a></div><br /></div><div>두 개의 인접 포인트 클라우드 병합: Bidirectional-group-overlap-algorithm을 사용해 두 개의 인접한 포인트 클라우드를 병합한다.</div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhAuXbD6mNb-HOfB4yUr8Qn7-31tvsScokT4aacdr9TB3IHi3bTZnEcF1U8sA77qxF96TJYCp2zifP4AZfm3hoYrZwoZAEav9wMGCajK9Cdl4I7s7rqw3SsJaFYLGMgaSXkNbotPeWsa46lBDwHrxTsZ4syhNY5eo2q6fLdq9o0pmLyjEYxKdBi5-GYJuv-" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="1118" data-original-width="1734" height="206" src="https://blogger.googleusercontent.com/img/a/AVvXsEhAuXbD6mNb-HOfB4yUr8Qn7-31tvsScokT4aacdr9TB3IHi3bTZnEcF1U8sA77qxF96TJYCp2zifP4AZfm3hoYrZwoZAEav9wMGCajK9Cdl4I7s7rqw3SsJaFYLGMgaSXkNbotPeWsa46lBDwHrxTsZ4syhNY5eo2q6fLdq9o0pmLyjEYxKdBi5-GYJuv-" width="320" /></a></div><br /></div><div>영역 병합: 영역 별로 전체 포인트 클라우드를 병합한다.</div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhj1LkwlY-U4ZDtZ4D6vs3SJE9pjmtSg92uw3PVWipvtreN9wgMlpS-nm8xgmaCZYKXHV1lGf2X-ZkNbq9ps7QG_r7gQtfGhHR12rpbd1zj8CAaop0VYzCnym_iFkC6QXFiCcM1okCivrku_WtMTd7wSpaGJX7l3TZfRrC6jpWPNxqpUESNQXvGXzXEHbwP" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="325" data-original-width="922" height="141" src="https://blogger.googleusercontent.com/img/a/AVvXsEhj1LkwlY-U4ZDtZ4D6vs3SJE9pjmtSg92uw3PVWipvtreN9wgMlpS-nm8xgmaCZYKXHV1lGf2X-ZkNbq9ps7QG_r7gQtfGhHR12rpbd1zj8CAaop0VYzCnym_iFkC6QXFiCcM1okCivrku_WtMTd7wSpaGJX7l3TZfRrC6jpWPNxqpUESNQXvGXzXEHbwP=w400-h141" width="400" /></a></div><br /></div><div>병합 후 세그먼테이션 결과 생성: 기본 매개 변수를 사용하여 Felzenswalb 및 Huttenlocher의 그래프 기반 이미지 분할 알고리즘을 장면에 적용한다. 그런 다음 2개의 세그멘테이션 결과를 병합하여 최종 결과를 얻는다.</div></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhmemSgBBkYibIe1j-i7muRRaA46hFnOOLHVLSeQTZ7mj-Vk9HLrko6nf9bqGpLa_vE6JeNAlxY6n2SaLw3x4gsX-pAJXi03lrIvwTLQFbeh4Qqwu2fiSrF41yoG71wAuEntHWCSrOud2L53Vhto5hMwKZ7eNgru0v5OrE1QX1-M_vVcUHh6t3zE66CQQAJ" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="1084" data-original-width="1732" height="250" src="https://blogger.googleusercontent.com/img/a/AVvXsEhmemSgBBkYibIe1j-i7muRRaA46hFnOOLHVLSeQTZ7mj-Vk9HLrko6nf9bqGpLa_vE6JeNAlxY6n2SaLw3x4gsX-pAJXi03lrIvwTLQFbeh4Qqwu2fiSrF41yoG71wAuEntHWCSrOud2L53Vhto5hMwKZ7eNgru0v5OrE1QX1-M_vVcUHh6t3zE66CQQAJ=w400-h250" width="400" /></a></div><br />상세한 내용은 아래 논문을 참고하라.</div><div><ul style="text-align: left;"><li><a href="https://arxiv.org/abs/2306.03908">SAM3D: Segment Anything in 3D Scenes (arxiv.org)</a></li></ul></div><div class="separator" style="clear: both; text-align: center;"><br /></div></div><b>레퍼런스</b><p></p><p></p><ul style="text-align: left;"><li><a href="https://github.com/Pointcept/SegmentAnything3D">Pointcept/SegmentAnything3D: SAM3D: Segment Anything in 3D Scenes (github.com)</a></li><li><a href="https://towardsdatascience.com/segment-anything-3d-for-point-clouds-complete-guide-sam-3d-80c06be99a18">Segment Anything 3D for Point Clouds: Complete Guide | Towards Data Science</a></li></ul><p></p>Daddy Makerhttp://www.blogger.com/profile/02275758703365576780noreply@blogger.com0tag:blogger.com,1999:blog-5201956450461596914.post-23155696860579590202023-12-13T17:17:00.000-08:002024-02-25T20:46:44.629-08:00동영상 생성 AI 오픈소스 Anything 3D 모델 생성 소개<p>이 글은 Anything 3차원 모델 생성 AI 사용 방법을 간략히 설명한다. </p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjfC5rMLhUTUYrgiLygkAaW6tXLMbMQIZ8gzETFhUVcxgZNl5S3ar0Rio_ASa5S-_CLcLXFEz96aYGHVf-bttEKCBxO92RWPZeYpT4BABgY4fIX1h9Bxdp0L1mJZD-sGOdrmX70N6oYTrHBi5Nz3n1AWS59ORaqmHAvd61vjCYlSS8IwHu0mzcTIpPuotlY" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="1143" data-original-width="2999" height="153" src="https://blogger.googleusercontent.com/img/a/AVvXsEjfC5rMLhUTUYrgiLygkAaW6tXLMbMQIZ8gzETFhUVcxgZNl5S3ar0Rio_ASa5S-_CLcLXFEz96aYGHVf-bttEKCBxO92RWPZeYpT4BABgY4fIX1h9Bxdp0L1mJZD-sGOdrmX70N6oYTrHBi5Nz3n1AWS59ORaqmHAvd61vjCYlSS8IwHu0mzcTIpPuotlY=w400-h153" width="400" /></a></div><div class="separator" style="clear: both; text-align: center;">Anything 3D 결과 예시</div><div><br /></div><div>Anything 3D 모델은 NeRF(Neural Radiance Field) 모델을 통해, 단일 렌더링 뷰에서 프롬프트만 통해 모든 대상 개체의 3D 결과를 얻을 수 있다. </div><div><br /></div><div>실행을 위해서는 다음 링크에서 소스를 다운로드, 패키지 설치 후 사용하면 된다. </div><div><ul style="text-align: left;"><li><a href="https://github.com/Anything-of-anything/Anything-3D">Anything-of-anything/Anything-3D: Segment-Anything + 3D. Let's lift anything to 3D. (github.com)</a></li></ul></div><div>실행 결과는 다음과 같다. </div><div><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEijsgOn3oOHani4_WPqOQDD-uk_5ss6uStKmymxIH6ftkKY44NkEN5er0FyZ91yGBaMHmwJAlxp50qmQWmpu420AudYoKktuzB2IEU7VrXO5to5z6rYHeAzANh4VKG51VbMiUenJoqrqyKh36LDZho9fXla79omrILkYur13hMrFSghSP2WOZUbVpWgdyqz/s360/corgi.gif" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="360" data-original-width="360" height="200" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEijsgOn3oOHani4_WPqOQDD-uk_5ss6uStKmymxIH6ftkKY44NkEN5er0FyZ91yGBaMHmwJAlxp50qmQWmpu420AudYoKktuzB2IEU7VrXO5to5z6rYHeAzANh4VKG51VbMiUenJoqrqyKh36LDZho9fXla79omrILkYur13hMrFSghSP2WOZUbVpWgdyqz/w200-h200/corgi.gif" width="200" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div></div></div><b>레퍼런스</b><p></p><p></p><ul style="text-align: left;"><li><a href="https://github.com/Anything-of-anything/Anything-3D">Anything-of-anything/Anything-3D: Segment-Anything</a></li><li><a href="https://towardsdatascience.com/segment-anything-3d-for-point-clouds-complete-guide-sam-3d-80c06be99a18">Segment Anything 3D for Point Clouds: Complete Guide | Towards Data Science</a></li><li><a data-saferedirecturl="https://www.google.com/url?q=https://github.com/ashawkey/stable-dreamfusion&source=gmail&ust=1703671713939000&usg=AOvVaw3Fc5lTd6BHvFv-PW8tPCbh" href="https://github.com/ashawkey/stable-dreamfusion" style="background-color: white; color: #1155cc; font-family: Arial, Helvetica, sans-serif;" target="_blank">ashawkey/stable-dreamfusion: Text-to-3D & Image-to-3D & Mesh Exportation with NeRF </a><a href="https://arxiv.org/abs/2311.04400">LRM: Large Reconstruction Model for Single Image to 3D (arxiv.org)</a></li></ul>Daddy Makerhttp://www.blogger.com/profile/02275758703365576780noreply@blogger.com0tag:blogger.com,1999:blog-5201956450461596914.post-50360758105098581112023-12-12T02:17:00.000-08:002023-12-22T00:03:09.639-08:003차원 모델 공간정보 렌더링 도구 Deck.GL 소개<div style="text-align: left;">이 글은 3차원 모델 공간정보 렌더링 도구 Deck.GL 을 간략히 소개한다.<br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEic3GXp5TiC5fLDVslQJgsF6Aopo_JOvcVCYTi701-0jNG7Gt22_ITVX8xnK77OquNEdOFQdRviK-c-sklCBIzJi__elUfuz1X7WC3lllkWjhKUa0yZnoWXiW5jSRyGW_5x2wOjw_Ioh4Rhhc0sObG1MzXCqMwAtAnYGla-m4px1qoq3wCA_aeVlYZpYf9q" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="359" data-original-width="824" height="174" src="https://blogger.googleusercontent.com/img/a/AVvXsEic3GXp5TiC5fLDVslQJgsF6Aopo_JOvcVCYTi701-0jNG7Gt22_ITVX8xnK77OquNEdOFQdRviK-c-sklCBIzJi__elUfuz1X7WC3lllkWjhKUa0yZnoWXiW5jSRyGW_5x2wOjw_Ioh4Rhhc0sObG1MzXCqMwAtAnYGla-m4px1qoq3wCA_aeVlYZpYf9q=w400-h174" width="400" /></a></div><br /></div><div style="text-align: left;"><b>소개</b></div><div style="text-align: left;"><div>deck.gl 는 대규모 데이터 세트의 고성능 WebGL 기반 시각화를 지원한다. 사용자는 기존 레이어를 구성하여 최소한의 노력으로 인상적인 시각적 결과를 생성할 수 있다. deck.gl의 확장 가능한 아키텍처를 활용하여 사용자 정의 요구 사항을 구현할 수 있다. deck.gl는 데이터(일반적으로 JSON 객체의 배열)를 시각적 레이어 스택에 매핑한다. </div><div><br /></div><div>Deck.gl 는 사용자 정의가 가능하도록 설계되었다. 모든 레이어에는 렌더링의 각 단계를 프로그래밍 방식으로 제어할 수 있는 유연한 API가 함께 제공된다. </div><div><br /></div></div><div style="text-align: left;"><b>사용 예제</b></div><div style="text-align: left;">설치는 다음과 같다.</div><div style="text-align: left;">npm install deck.gl --save</div><div style="text-align: left;"><br /></div><div style="text-align: left;"><a href="https://github.com/visgl/deck.gl/tree/8.9-release/examples">예제</a>는 다음과 같이 실행한다.</div><div style="text-align: left;"><div>git clone git@github.com:visgl/deck.gl.git</div><div>cd deck.gl/examples/get-started/pure-js/basic</div><div>npm install</div><div>npm start</div><div><br /></div><div>결과는 다음과 같다. </div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEg7c9-2wQYZmgDIi7rLl6vUqCLm5vZFxFAa-hQogEBY7M1vN5RyheP-jnp5fOytaFAH7DuTXqYAGlBmgzdvbS7ceQ5PKYvigaG6SzzPwGrG4HR9Ie-GvQ9lewUOzlZgQa90AGn3ATaEl_IbtHHmRwNgqtyUvTfr6ca2B8uJDBSX38ggRofG0Pc3cARh5sY0" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="1053" data-original-width="1945" height="216" src="https://blogger.googleusercontent.com/img/a/AVvXsEg7c9-2wQYZmgDIi7rLl6vUqCLm5vZFxFAa-hQogEBY7M1vN5RyheP-jnp5fOytaFAH7DuTXqYAGlBmgzdvbS7ceQ5PKYvigaG6SzzPwGrG4HR9Ie-GvQ9lewUOzlZgQa90AGn3ATaEl_IbtHHmRwNgqtyUvTfr6ca2B8uJDBSX38ggRofG0Pc3cARh5sY0=w400-h216" width="400" /></a></div><div class="separator" style="clear: both; text-align: center;"><a href="https://deck.gl/examples">deck.gl 제공 예제</a></div><div><br /></div>이를 이용하면, 다양한 GIS 정보 렌더링을 손쉽게 개발할 수 있다. <br /><br /></div></div><div style="text-align: left;"><b>레퍼런스</b></div><div style="text-align: left;"><ul style="text-align: left;"><li><a href="https://deck.gl/examples">deck.gl</a></li><li><a href="https://github.com/opengeospatial/mf-json">github.com/opengeospatial/mf-json</a></li><li><a href="https://github.com/aistairc/mf-cesium/tree/mf-cesium_api">github.com/aistairc/mf-cesium/tree/mf-cesium_api</a></li><li><a href="https://github.com/MobilityDB/MobilityDB">github.com/MobilityDB/MobilityDB</a></li><li><a href="https://github.com/movingpandas/movingpandas">github.com/movingpandas/movingpandas</a></li></ul></div>Daddy Makerhttp://www.blogger.com/profile/02275758703365576780noreply@blogger.com0tag:blogger.com,1999:blog-5201956450461596914.post-82041346174863911252023-12-02T17:46:00.000-08:002023-12-21T23:55:16.906-08:00생성AI 기반 텍스트 to 비디오 도구 ControlNet<div style="text-align: left;">이 글은 생성AI 기반 텍스트 to 비디오 생성 도구인 ControlNet에 대해 정리한다.<br /><br /></div><div style="text-align: left;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhTMAx8T9NPOGbbEwfjSfFacg5A0M-jkTCEflZAJdqrVA3N6vcEa3HtQraoSXnmEUSHo_73174PWamdBTo2nQ2GUqDnyHf9IfSJFUpbiSLTPJ6iWROnvgEUxJacef7AEvBDHbYsSmjUK1whSoWyktohUh2OIeGdgrcYIbtuZTyHioNxM2tHZqKqEyTSHqq0" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="583" data-original-width="800" height="233" src="https://blogger.googleusercontent.com/img/a/AVvXsEhTMAx8T9NPOGbbEwfjSfFacg5A0M-jkTCEflZAJdqrVA3N6vcEa3HtQraoSXnmEUSHo_73174PWamdBTo2nQ2GUqDnyHf9IfSJFUpbiSLTPJ6iWROnvgEUxJacef7AEvBDHbYsSmjUK1whSoWyktohUh2OIeGdgrcYIbtuZTyHioNxM2tHZqKqEyTSHqq0" width="320" /></a></div><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgOYAt67GRztdXBONL72sb_fHIDQIXQIl0EXUiJ_N4QpBjzsLP6lnUm6WjGgW7XqlLmqC8mE-BOAQ4sGWOsGqY58wOenA6oRjKo0ElAJKEfcg8qzxMFoBTrrz1WitTQsxHu_Ih8vwqKUvWVDUCBznfuHHWX_bROWwerFDBYPNmybdSvmRkF8SkVYJ-zLJO5" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="231" data-original-width="540" height="137" src="https://blogger.googleusercontent.com/img/a/AVvXsEgOYAt67GRztdXBONL72sb_fHIDQIXQIl0EXUiJ_N4QpBjzsLP6lnUm6WjGgW7XqlLmqC8mE-BOAQ4sGWOsGqY58wOenA6oRjKo0ElAJKEfcg8qzxMFoBTrrz1WitTQsxHu_Ih8vwqKUvWVDUCBznfuHHWX_bROWwerFDBYPNmybdSvmRkF8SkVYJ-zLJO5" width="320" /></a></div><br /></div></div><div style="text-align: left;"><div><b>소개</b></div><div>생성형 AI 기술은 텍스트, 이미지, 오디오 또는 비디오와 같은 새로운 콘텐츠를 생성할 수 있다. 이러한 AI 모델은 대규모 데이터 세트에서 훈련되며 복잡한 알고리즘을 사용하여 패턴을 학습하고 새로운 콘텐츠를 생성한다. 많은 관심을 끌고 있는 Text2Image 모델 중 하나는 Stable Diffusion이다</div><div><br /></div><div>Stable Diffusion에는 많은 기능이 있지만 Stable Diffusion의 확장 기능인 Control-Net은 동일한 이미지의 여러 변형을 생성할 수 있다.</div><div><br /></div><div>Stable Diffusion은 2022년에 출시된 딥러닝, 텍스트-이미지 AI/머신러닝 모델이다. 주로 텍스트 설명에 따라 상세한 이미지를 생성하는 데 사용되지만 인페인팅, 아웃페인팅, 텍스트 프롬프트에 따라 안내되는 이미지-이미지 번역 생성과 같은 다른 작업에도 적용할 수 있다. 스타트업 Stability AI가 여러 학술 연구원 및 비영리 단체와 협력하여 개발했다.</div><div><br /></div><div><b>ControlNet</b></div><div>ControlNet은 신경망 구조, 아키텍처 또는 새로운 신경망 구조로, 추가 조건을 추가하여 확산 모델을 제어하는 데 도움이 된다. ControlNet은 기존 확산 모델을 가져와서 아키텍처를 변경하고 아래 그림과 같이 원하는 것을 추가할 수 있다.</div><div><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgmt-nxwwowblMtL_T28BVbZXyN84xqL9GN-SUQVzdQ1M1HNUXs-7ULdtsrxAUB6UipJxAHgHhtByyOXFCZPMdSxGWgmGTkNp1_WjfCY4dfcs18-ZcPegkIrZy6AdzVMpKNyIx00ge2slU6ppb_JgkHaSF4qV5feZ5uJWXcCcI6um06rrFuvjYvZqgCFu5i" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="291" data-original-width="770" height="151" src="https://blogger.googleusercontent.com/img/a/AVvXsEgmt-nxwwowblMtL_T28BVbZXyN84xqL9GN-SUQVzdQ1M1HNUXs-7ULdtsrxAUB6UipJxAHgHhtByyOXFCZPMdSxGWgmGTkNp1_WjfCY4dfcs18-ZcPegkIrZy6AdzVMpKNyIx00ge2slU6ppb_JgkHaSF4qV5feZ5uJWXcCcI6um06rrFuvjYvZqgCFu5i=w400-h151" width="400" /></a></div></div><br /></div><div>Hugging Face 허브에는 이미 50개의 +public 및 open ControlNet 모델이 있으며, 기본 모델은 1200+개 좋아요를 받았다. ControlNet은 이미지에서 대상의 모양/형태를 식별하는 데 도움이 되는 여러 모델이 패키지로 제공된다. </div><div><br /></div><div><b>깊이 맵(Depth Map)</b></div><div>Stable diffusion v2의 이미지 깊이와 마찬가지로 ControlNet은 입력 이미지에서 깊이 맵을 추론할 수 있다. ControlNet의 깊이 맵은 Stable Diffusion v2보다 해상도가 높다.</div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEijqpz2l0HBoGv2I7Rn4ykLmrpZlovfBWWPcoKhE_rQInYvMZMTKw09_jRWj-Co7TnP2qMyCp5EWCbczFvXnAaSCya9WdmQwAn8OprVfvIuHvOX5_ULPPOfD4r-qxyzyiDqIy6Z3CBKuSrWi4nmg5K-PGafjTAR3B_JaObaCyQEu8YGvl78WqClgGZAe2vJ" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="363" data-original-width="828" height="140" src="https://blogger.googleusercontent.com/img/a/AVvXsEijqpz2l0HBoGv2I7Rn4ykLmrpZlovfBWWPcoKhE_rQInYvMZMTKw09_jRWj-Co7TnP2qMyCp5EWCbczFvXnAaSCya9WdmQwAn8OprVfvIuHvOX5_ULPPOfD4r-qxyzyiDqIy6Z3CBKuSrWi4nmg5K-PGafjTAR3B_JaObaCyQEu8YGvl78WqClgGZAe2vJ" width="320" /></a></div><br /></div><div><b>세그멘테이션 맵(Segmentation Map)</b></div><div>입력 영상에서 추출된 분할 맵을 기반으로 영상을 생성한다.</div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEg0KVVYMyufEA7ieEXA7xDyI5os91UTaRSG0z-AskT1kfEAD3DnlfpV2lBcKys8Qaw_COQEMg5NpLTkuvpIAB20kK3-V_veXzaj_i_fSqIQphHEiKGY_7BsTvXyM_XZiSyY9AcJJxVm260jUdrP4S0h3borcKYcWcziBYCsXWU7ylKFA8H0PllQbEhdbk8p" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="166" data-original-width="444" height="120" src="https://blogger.googleusercontent.com/img/a/AVvXsEg0KVVYMyufEA7ieEXA7xDyI5os91UTaRSG0z-AskT1kfEAD3DnlfpV2lBcKys8Qaw_COQEMg5NpLTkuvpIAB20kK3-V_veXzaj_i_fSqIQphHEiKGY_7BsTvXyM_XZiSyY9AcJJxVm260jUdrP4S0h3borcKYcWcziBYCsXWU7ylKFA8H0PllQbEhdbk8p" width="320" /></a></div><br /></div><div><b>사람 자세 감지</b></div><div>Openpose는 손, 다리, 머리의 위치와 같은 사람의 포즈를 추출할 수 있는 빠른 키포인트 감지 모델이다. </div><div><br /></div><div>OpenPose를 사용하는 ControlNet 워크플로를 지원한다. 키포인트는 OpenPose를 사용하여 입력 이미지에서 추출되어 키포인트의 위치를 포함하는 컨트롤 맵으로 저장된다. 그런 다음 텍스트 프롬프트와 함께 추가 컨디셔닝으로 Stable Diffusion에 공급 된다. 이미지는 이 두 가지 조건을 기반으로 생성된다.</div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEiZmbG1CgG2rxa5PQiHG1YgowkLdLeg00nBM1i-nryg3x2GzOEz0BYiGEo6cQ8kYidnQZHiOU3C6NQA2xd38TWRdeduUQ0xP0MJHQav-i6nPvfSuLIXw94BqdmxML0iwwqOFkkHkPWWlWS8nG-xaqSql2Wlei1b1l-B8TfUkOPzkrYC6mz_HwKExhdGYUfr" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="313" data-original-width="768" height="163" src="https://blogger.googleusercontent.com/img/a/AVvXsEiZmbG1CgG2rxa5PQiHG1YgowkLdLeg00nBM1i-nryg3x2GzOEz0BYiGEo6cQ8kYidnQZHiOU3C6NQA2xd38TWRdeduUQ0xP0MJHQav-i6nPvfSuLIXw94BqdmxML0iwwqOFkkHkPWWlWS8nG-xaqSql2Wlei1b1l-B8TfUkOPzkrYC6mz_HwKExhdGYUfr=w400-h163" width="400" /></a></div></div><div><br /></div><div><b>이미지 재구성</b></div><div>Control-Net with Stable Diffusion을 사용하여 저해상도 또는 저하된 이미지에서 고해상도 이미지를 재구성할 수 있다. 이는 포렌식 또는 의료 영상과 같이 이미지에서 가능한 한 많은 세부 정보를 얻는 것이 중요한 분야에서 유용할 수 있다.</div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhbhrC-N41CNJ9P4mdwHPnCKoes57XdK6LNKwmG1uLAev7lB5xEJsgPS76KR8cJDuIHNiOSxdySbBpGMskWlMlMxhWJlUCT-ZOFvRWVxBYUe_XFtDElKZhmLVGA6sb4aoUmOA4NBg6-y5ZW7YZBX4yvVS4MsNnoJ9zhvU5iY29heyfl0EQ4AJrnW0ITNV8D" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="600" data-original-width="800" height="240" src="https://blogger.googleusercontent.com/img/a/AVvXsEhbhrC-N41CNJ9P4mdwHPnCKoes57XdK6LNKwmG1uLAev7lB5xEJsgPS76KR8cJDuIHNiOSxdySbBpGMskWlMlMxhWJlUCT-ZOFvRWVxBYUe_XFtDElKZhmLVGA6sb4aoUmOA4NBg6-y5ZW7YZBX4yvVS4MsNnoJ9zhvU5iY29heyfl0EQ4AJrnW0ITNV8D" width="320" /></a></div></div><div><b>물체 감지 및 인식</b></div><div>Control-Net with Stable Diffusion은 까다로운 조명 또는 기상 조건에서도 이미지에서 물체를 감지하고 인식하는 데 사용할 수 있다. 이는 빠르고 정확한 물체 인식이 중요한 자율 주행 또는 보안과 같은 분야에서 유용할 수 있다.</div><div>이미지 복원: Control-Net with Stable Diffusion을 사용하여 오래되거나 손상된 이미지를 복원하여 원래 품질로 되돌릴 수 있다. 이것은 예술 복원이나 역사 보존과 같은 분야에서 유용할 수 있다.</div><div><br /></div><div><b>데이터 증강</b></div><div>Control-Net with Stable Diffusion을 사용하여 머신 러닝 모델 훈련을 위한 새롭고 고유한 데이터를 생성할 수 있다. 이는 컴퓨터 비전이나 자연어 처리와 같은 분야에서 유용할 수 있으며, 다양하고 대표적인 데이터 세트를 보유하는 것이 모델 정확도에 중요한다.</div><div>3D 재구성: Control-Net with Stable Diffusion을 사용하여 2D 이미지 또는 비디오에서 3D 모델을 재구성할 수 있다. 이는 사실적이고 몰입감 있는 3D 환경을 만드는 것이 중요한 건축이나 가상 현실과 같은 분야에서 유용할 수 있다.</div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEivZ00wQO1rKvDGrOQeRfmcj2WeS7IvM2lrTBXK75SZW3ByRVe_m6UHAvNyEQpQwkVLG1a1t2bzZHSwqJwe_904BlFFdtkXdvR90jmOWHykq9e8KtJrgfjgmW61cC6XsyXI32dgtGkVboiIUcbdTagVpDiW_akzsNN7ou2QImB9fgEvhm2GhZn0LNpc_74G" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="421" data-original-width="828" height="163" src="https://blogger.googleusercontent.com/img/a/AVvXsEivZ00wQO1rKvDGrOQeRfmcj2WeS7IvM2lrTBXK75SZW3ByRVe_m6UHAvNyEQpQwkVLG1a1t2bzZHSwqJwe_904BlFFdtkXdvR90jmOWHykq9e8KtJrgfjgmW61cC6XsyXI32dgtGkVboiIUcbdTagVpDiW_akzsNN7ou2QImB9fgEvhm2GhZn0LNpc_74G" width="320" /></a></div><br /></div><div><b>Stable Diffusion을 사용한 ControlNet 실행하기</b></div><div>실행 방법은 다음과 같다. </div><div><ul style="text-align: left;"><li>컴퓨터에서 Automatic1111의 스테이블 디퓨전 설치(<a href="https://github.com/Mikubill/sd-webui-controlnet">Mikubill/sd-webui-controlnet: WebUI extension for ControlNet</a>) 및 실행(webui-user.bat 실행)</li><li>서버가 실행되면, URL http://127.0.0.1:7860에 접속함</li><li>Extensions 탭으로 이동하여 Available 하위 탭을 클릭</li><li>로드 버튼을 클릭</li><li>controlnet을 입력. sd-webui-controlnet이라는 확장 프로그램이 표시되면 맨 오른쪽의 작업 열에서 설치를 클릭. WebUI는 필요한 파일을 다운로드하고 Stable Diffusion의 로컬 인스턴스에 ControNet을 설치할 것임.</li><li>설치된 탭으로 이동해 적용버튼 클릭 후, UI를 다시 재시작함</li><li><a href="https://huggingface.co/lllyasviel/ControlNet-v1-1">Huggingface 링크</a>에 있는 모든 모델 다운로드 후, 아래 models 폴더에 파일들 복사해 넣음</li></ul><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEi5d7V11XbKvW0MLXF-2xccXmEdy39npg2g8zkoB32D1qS9v_A7aDgudH0TI1tRGM74ZT-Cnf34XPEADE3D_v-Qher4GmEVhhti9yXBAHYGhR692yoY_VnpOi6P-r2L7_Fv6ahiFaVLGxne9UFk8Vxip2r4zp3RqALiy0n4RuLdbrA7sNkLZjps9bgtIwhR" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="247" data-original-width="656" height="150" src="https://blogger.googleusercontent.com/img/a/AVvXsEi5d7V11XbKvW0MLXF-2xccXmEdy39npg2g8zkoB32D1qS9v_A7aDgudH0TI1tRGM74ZT-Cnf34XPEADE3D_v-Qher4GmEVhhti9yXBAHYGhR692yoY_VnpOi6P-r2L7_Fv6ahiFaVLGxne9UFk8Vxip2r4zp3RqALiy0n4RuLdbrA7sNkLZjps9bgtIwhR=w400-h150" width="400" /></a></div><br /></div></div><div>실행하면, Stable Diffusion GUI에는 다양한 옵션과 설정이 제공된다. 첫 번째 창에는 이미지 페이지에 대한 텍스트가 표시된다. 프롬프트를 제공하고 생성 버튼을 클릭하고 완료될 때까지 기다린다. 아래 이미지에서 볼 수 있듯이 이미지를 생성하는 데 몇 분 정도 걸린다.</div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjYMh7s0SqVcS_1JxmIQGE7k3y3CGN_rrTRekSuE6nAuL6ybSlc1reQ4s1l2PtvzJa-0NWo1YpcfRO8tvsy8mhjmXSCsp96BLZthLr4vx8xF1Zp-U-rx-unzYFEBpFseM9h2GWoJ4TdWmlsi0HZZEblZHEXOJmZLwR1vY_-pVC3JCuYvbQ6bC8CpvoE-VZn" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="379" data-original-width="828" height="146" src="https://blogger.googleusercontent.com/img/a/AVvXsEjYMh7s0SqVcS_1JxmIQGE7k3y3CGN_rrTRekSuE6nAuL6ybSlc1reQ4s1l2PtvzJa-0NWo1YpcfRO8tvsy8mhjmXSCsp96BLZthLr4vx8xF1Zp-U-rx-unzYFEBpFseM9h2GWoJ4TdWmlsi0HZZEblZHEXOJmZLwR1vY_-pVC3JCuYvbQ6bC8CpvoE-VZn" width="320" /></a></div><br /></div><div>첨부된 스크린샷에서 볼 수 있듯이 생성된 이미지를 저장하고, zip 파일을 만들고, img2img로 보내고, inpaint로 보내는 등의 작업에 사용할 수 있는 다양한 옵션이 있다. </div><div><br /></div><div>이미지 저장 버튼을 클릭한 후 이미지 다운로드 옵션을 볼 수 있다. 생성된 이미지에서 Inpaint를 수행할 수 있다. 필요에 따라 Annotator 해상도, Canny 낮은 임계값, Canny 높은 임계값, 무게 등과 같은 모든 것을 선택할 수 있다.</div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgUtmj901DFMtWjDJm6wxOh3j7seUMiIzCnhZq920dQ7K7yTOfeXYe_KvfRmJM7nRuhLeZOyEkVDvdfMZulLZy_SL7HbgnmHiFGEjw46OXaZz6geKc6lDrw7gNAOdLkDcABE1pBfFIZOnaeNAe_u--A_SIa-rk0HzA-MZYruJauxrADOA4mRJqeZwzGE_ur" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="1253" data-original-width="828" height="240" src="https://blogger.googleusercontent.com/img/a/AVvXsEgUtmj901DFMtWjDJm6wxOh3j7seUMiIzCnhZq920dQ7K7yTOfeXYe_KvfRmJM7nRuhLeZOyEkVDvdfMZulLZy_SL7HbgnmHiFGEjw46OXaZz6geKc6lDrw7gNAOdLkDcABE1pBfFIZOnaeNAe_u--A_SIa-rk0HzA-MZYruJauxrADOA4mRJqeZwzGE_ur" width="159" /></a></div><br /></div><div><b>결론</b></div><div>이 글은 ControlNet 기능에 대해 설명하였다. 이를 이용해 간단한 이미지나 텍스트만으로도 다양한 이미지와 영상을 생성할 수 있다.</div><div class="separator" style="clear: both; text-align: center;"><iframe allowfullscreen="" class="BLOG_video_class" height="266" src="https://www.youtube.com/embed/3FZuJdJGFfE" width="320" youtube-src-id="3FZuJdJGFfE"></iframe></div><div><br /></div></div><div style="text-align: left;"><b>레퍼런스</b><ul style="text-align: left;"><li><a href="https://medium.com/@techlatest.net/stable-diffusion-and-control-net-a-beginners-guide-9efefe2790f">Stable Diffusion and Control-Net -A Beginners Guide | Medium</a></li><li><a href="https://weirdwonderfulai.art/resources/installing-controlnet-in-automatic1111/">Installing ControlNet in Automatic1111 | Weird Wonderful AI Art</a></li><li><a href="https://stable-diffusion-art.com/controlnet/">ControlNet v1.1: A complete guide - Stable Diffusion Art (stable-diffusion-art.com)</a></li><li><a href="https://controlavideo.github.io/">Control-A-Video: Controllable Text-to-Video Generation with Diffusion Models (controlavideo.github.io)</a></li><li><a href="https://github.com/Vchitect/LaVie">Vchitect/LaVie: LaVie: High-Quality Video Generation with Cascaded Latent Diffusion Models (github.com)</a></li><li><a href="https://huggingface.co/spaces/Vchitect/LaVie">LaVie - a Hugging Face Space by Vchitect</a></li><li><a href="https://github.com/Vchitect/SEINE">Vchitect/SEINE: SEINE: Short-to-Long Video Diffusion Model for Generative Transition and Prediction (github.com)</a></li><li><a href="https://github.com/Vchitect/SEINE">Vchitect/SEINE: SEINE: Short-to-Long Video Diffusion Model for Generative Transition and Prediction (github.com)</a></li></ul></div>Daddy Makerhttp://www.blogger.com/profile/02275758703365576780noreply@blogger.com0