<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://leeinformation.github.io/Leeinformation.github.io/feed.xml" rel="self" type="application/atom+xml" /><link href="https://leeinformation.github.io/Leeinformation.github.io/" rel="alternate" type="text/html" /><updated>2025-08-14T07:31:17+00:00</updated><id>https://leeinformation.github.io/Leeinformation.github.io/feed.xml</id><title type="html">초보 개발자의 일지</title><subtitle>Programming Language &amp; Information/Hacking</subtitle><author><name>Lee Jeong Min</name></author><entry><title type="html">[AI 부트캠프] LM to LLM - Large Language Model 기초</title><link href="https://leeinformation.github.io/Leeinformation.github.io/bootcamp/LM_to_LLM/" rel="alternate" type="text/html" title="[AI 부트캠프] LM to LLM - Large Language Model 기초" /><published>2025-08-14T00:00:00+00:00</published><updated>2025-08-14T00:00:00+00:00</updated><id>https://leeinformation.github.io/Leeinformation.github.io/bootcamp/LM_to_LLM</id><content type="html" xml:base="https://leeinformation.github.io/Leeinformation.github.io/bootcamp/LM_to_LLM/"><![CDATA[<head>
  <style>
    table.dataframe {
      white-space: normal;
      width: 100%;
      height: 240px;
      display: block;
      overflow: auto;
      font-family: Arial, sans-serif;
      font-size: 0.9rem;
      line-height: 20px;
      text-align: center;
      border: 0px !important;
    }

    table.dataframe th {
      text-align: center;
      font-weight: bold;
      padding: 8px;
    }

    table.dataframe td {
      text-align: center;
      padding: 8px;
    }

    table.dataframe tr:hover {
      background: #b8d1f3; 
    }

    .output_prompt {
      overflow: auto;
      font-size: 0.9rem;
      line-height: 1.45;
      border-radius: 0.3rem;
      -webkit-overflow-scrolling: touch;
      padding: 0.8rem;
      margin-top: 0;
      margin-bottom: 15px;
      font: 1rem Consolas, "Liberation Mono", Menlo, Courier, monospace;
      color: $code-text-color;
      border: solid 1px $border-color;
      border-radius: 0.3rem;
      word-break: normal;
      white-space: pre;
    }

  .dataframe tbody tr th:only-of-type {
      vertical-align: middle;
  }

  .dataframe tbody tr th {
      vertical-align: top;
  }

  .dataframe thead th {
      text-align: center !important;
      padding: 8px;
  }

  .page__content p {
      margin: 0 0 0px !important;
  }

  .page__content p > strong {
    font-size: 0.8rem !important;
  }

  </style>
</head>

<p>◆ Pytorch - DNN 구현</p>

<blockquote>
  <p>(25.06.18 위키라이더)</p>
</blockquote>

<p>LM to LMM 강의의 <strong>Large Language Model 기초</strong> 정리</p>

<hr />

<h1 id="what-is-large-language-model">What is Large Language Model?</h1>

<p>Large Language Model(LLM) : 방대한 파라미터 수를 가진 언어모델</p>

<ul>
  <li>
    <p>전통적인 머신러닝 모델 VS LLM</p>

    <ul>
      <li>
        <p>전통적인 언어모델은 각각의 태스크 처리를 위해 각각의 ML 모델이 필요</p>
      </li>
      <li>
        <p>LLM은 수많은 데이터로 훈련시키기에 여러 태스크를 하나의 LLM에서 처리 가능</p>
      </li>
    </ul>
  </li>
  <li>
    <p>LLM의 핵심</p>

    <ul>
      <li>
        <p><strong><em>Human Alignment</em></strong> : 인간의 선호도를 기반으로 튜닝</p>

        <ul>
          <li>첫 Pre-training을 통해 언어를 학습 후 두 번째 fine-tuning(Instruction Tuning)에서 인간의 선호도를 학습</li>
        </ul>
      </li>
      <li>
        <p>리셋 모먼트 : 인간의 업무 생산성을 높여주는 수많은 도구들이 등장 → <strong>AI Agent</strong></p>
      </li>
      <li>
        <p><strong><em>In-Context Learning</em></strong> : 내재화된 In-Context 파라미터 정보를 이용하여 실제 모델의 능력을 끌어냄</p>

        <ul>
          <li>
            <p><strong>Zero-shot</strong> : 예시를 주지 않고 명령만 줌</p>
          </li>
          <li>
            <p><strong>One-shot</strong> : 하나의 예시를 같이 줌</p>
          </li>
          <li>
            <p><strong>Few-show</strong> : 여러개의 예시를 같이 줌</p>
          </li>
        </ul>
      </li>
    </ul>
  </li>
</ul>

<blockquote>
  <p>특정 크기를 기점으로 모델의 성능이 급격히 상승 → 없던 능력이 생김(번역만 할줄 알던 모델이 요약, 정리 등의 다양한 태스크) : <strong><em>창발성</em></strong></p>
</blockquote>

<hr />

<h2 id="llm의-제작-프로세스">LLM의 제작 프로세스</h2>

<p><strong>필요한 리소스들</strong></p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>- Infra

    - Hyper Scale Cloud

    - Super Computing

    - Hyper Scale Data Center

    - 운영 환경 (하드웨어)



- Backbone Model

    - EX) ChatGPT : GPT 3.5 기반으로 학습



- Tuning (비용 효율적 측면)

    - 경량화 방법

    - 반도체 기술



- Data
</code></pre></div></div>

<hr />

<ul>
  <li>
    <p><strong>Data Processing</strong></p>

    <ol>
      <li>
        <p>Raw Corpus 수집(최소 1조개 이상)</p>
      </li>
      <li>
        <p>Quality Filtering : 품질이 낮은 데이터 필터링</p>
      </li>
      <li>
        <p>De-duplication : 중복 데이터 제거</p>
      </li>
      <li>
        <p>Privacy Reduction : 개인 정보와 관련된 데이터 제거</p>
      </li>
      <li>
        <p>Tokenization</p>
      </li>
    </ol>
  </li>
  <li>
    <p><strong>Pre-training &amp; Supervised Fine-tuning</strong></p>

    <ul>
      <li>
        <p>Pre-training 과정에서 문장을 생성 (다음 단어를 예측하면서)</p>
      </li>
      <li>
        <p>Supervised Finetuning을 통해 질문에 잘 답변할 수 있도록 학습</p>
      </li>
    </ul>
  </li>
</ul>

<hr />

<h2 id="llm의-방향성">LLM의 방향성</h2>

<ul>
  <li>
    <p><strong><em>Data &amp; Size</em></strong></p>

    <ul>
      <li>
        <p>Chinchilla : GPT에 비해 모델의 크기는 작아지고, 데이터는 많아짐</p>

        <blockquote>
          <p>모델의 전체 용량에 비해 일부만 사용하던 것을 데이터를 늘려 전체 용량 사용</p>
        </blockquote>
      </li>
      <li>
        <p>Modeling VS Data</p>

        <ul>
          <li>
            <p>실제 모델의 크기를 키웠을 때 Vanilla Transformer(Baseline Model)의 성능이 가장 좋았음</p>

            <blockquote>
              <p>모델의 크기를 키우고 데이터의 양을 늘리는 것이 중요</p>
            </blockquote>
          </li>
        </ul>
      </li>
      <li>
        <p>데이터 구성</p>

        <ul>
          <li>
            <p>말뭉치의 출처에 대한 조합에 따라 사전학습의 성능이 달라짐</p>
          </li>
          <li>
            <p>데이터 구성 / 데이터 비율 또한 중요함</p>
          </li>
        </ul>
      </li>
    </ul>

    <blockquote>
      <p>결국 사전학습 모델의 크기가 언어 모델의 능력이 창발됨</p>
    </blockquote>
  </li>
</ul>

<hr />

<ul>
  <li>
    <p><strong><em>Multimodal</em></strong></p>

    <ul>
      <li>언어에만 한정하는게 아닌 다양한 작업을 할 수 있는 모델 (Vision, Speech, … etc)</li>
    </ul>
  </li>
  <li>
    <p><strong><em>Multilingual</em></strong></p>

    <ul>
      <li>
        <p>다국어 처리를 위한 모델</p>
      </li>
      <li>
        <p>대부분의 모델들은 현재 영어를 기반으로 처리 → 다양한 언어 처리 목적</p>
      </li>
    </ul>
  </li>
  <li>
    <p><strong><em>Synthetic Data</em></strong></p>

    <ul>
      <li>
        <p>사람보다 뛰어나게 labeling하는 case가 발생</p>
      </li>
      <li>
        <p>모델의 성능이 올라갈수록 사람이 labeling하는 것보다 우수한 성능을 보여줌</p>
      </li>
    </ul>
  </li>
  <li>
    <p><strong><em>Domain Specialized</em></strong></p>

    <ul>
      <li>각 태스크에 맞게 잘 처리하는 것도 중요 (도메인 특화)</li>
    </ul>
  </li>
</ul>

<hr />

<ul>
  <li>
    <p><strong><em>Evaluation</em></strong></p>

    <ul>
      <li>
        <p>기존 ML 평가방식과 다른 평가방식이 필요</p>
      </li>
      <li>
        <p>Reasoning, Knowledge, Conversation, Creativity, … 등 새로운 평가방식 사용</p>
      </li>
      <li>
        <p>뿐만 아니라 기존의 자연어 처리 태스크에서 부족한 수학문제와 같은 추론, 상식, 바이어스 문제 등을 얼마나 잘 해결하는지의 평가 방식도 존재</p>
      </li>
      <li>
        <p>LLM-Eval : LLM이 LLM을 평가</p>
      </li>
    </ul>
  </li>
</ul>

<hr />

<ul>
  <li>
    <p><strong><em>Prompt Engineering</em></strong></p>

    <ul>
      <li>
        <p>대화형 AI가 생성하는 결과물의 품질을 높일 수 있는 prompt 입력 값들의 조합을 탐색하는 작업</p>
      </li>
      <li>
        <p>Instruction, Context, Input Data, Output Indicator 등의 예시를 주는 방식 등</p>
      </li>
      <li>
        <p>Chain-of-thought prompting : 답변에 도달하는 과정을 학습시키는 목적 / 사람의 생각의 흐름을 학습</p>
      </li>
      <li>
        <p>Optimization, Tuning : Lasting step by step, 감정 호소 등 고품질의 결과물을 도출할 수 있도록 유도</p>
      </li>
      <li>
        <p>Prompt Parameter Tuning</p>

        <ul>
          <li>
            <p>Temperature : 답변의 창의성과 무작위성 조정 / 값이 낮을수록 사실에 근거한 정확한 답변, 높을수록 창의적인 결과물</p>
          </li>
          <li>
            <p>Top_p : 답변의 무작위성 제어</p>
          </li>
          <li>
            <p>frequency_penalty : 값이 높을수록 흔하지 않은 단어를 답변에 포함할 가능성이 낮음</p>
          </li>
          <li>
            <p>presence_penalty : 값이 높을수록 유사하거나 동일한 단어 및 문구를 답변 시 반복할 가능성이 낮음</p>
          </li>
        </ul>
      </li>
      <li>
        <p>Automatic Curriculum : 목표만 설정하면 달성을 위해 필요한 것들을 자동으로 실행</p>
      </li>
    </ul>
  </li>
</ul>

<hr />

<h1 id="llm의-근간-이론">LLM의 근간 이론</h1>

<ul>
  <li>
    <p><strong><em>In-Context Learning</em></strong></p>

    <ul>
      <li>
        <p>Fine Tuning은 대규모 corpus로 사전학습 후 적은 규모의 특화 데이터로 일부 task에 대해 능력을 집중적으로 향상시키는 작업</p>

        <blockquote>
          <p>모델의 가중치를 업데이트하면서 gradient가 업데이트 됨</p>
        </blockquote>
      </li>
      <li>
        <p>In-Context Learning은 Zero-shot, One-shot, Few-shot을 통해 프롬프트를 주는 것 만으로도 성능 향상 가능</p>

        <blockquote>
          <p>모델의 가중치를 업데이트 하지 않음</p>
        </blockquote>
      </li>
      <li>
        <p><strong>Zero-Shot Learning</strong> : 예시를 전혀 보지 않고 모델 업데이트 없이 새로운 태스크 수행</p>

        <blockquote>
          <p>특정 작업(독해, 번역, 요약, Q&amp;A 등)에서 기존의 SOTA 모델들보다 좋음</p>
        </blockquote>
      </li>
      <li>
        <p><strong>One-Shot Learning</strong> : 단 하나의 예시와 task에 대한 자연어 지시문을 제공</p>

        <blockquote>
          <p>사람이 소통하는 방법과 가장 흡사</p>
        </blockquote>
      </li>
      <li>
        <p><strong>Few-Shot Learning</strong> : 모델 추론 시 단 몇 개의 예시만 참고하여 정답 생성</p>

        <blockquote>
          <p>새로운 태스크에 효율적으로 빠르게 적응</p>
        </blockquote>
      </li>
    </ul>
  </li>
</ul>

<hr />

<h2 id="chatgpt">ChatGPT</h2>

<ul>
  <li>
    <p><strong><em>ChatGPT</em></strong></p>

    <ul>
      <li>
        <p>Multi-Tasking : 기존의 chatbot은 여러 개의 언어 모델이 조합됨 / ChatGPT는 하나의 모델이지만 다양한 태스크를 수행할 수 있음</p>
      </li>
      <li>
        <p>외부 Plugin과 결합하려는 시도</p>
      </li>
      <li>
        <p>GPT-3.5를 fine-tuning</p>
      </li>
      <li>
        <p>InstructGPT의 ‘sibling model’, 학습 방식 유사</p>
      </li>
      <li>
        <p>Demonstration Data : 데이터를 대화형으로 바꿈</p>
      </li>
      <li>
        <p>보상 모델을 활용하여 강화학습으로 업데이트</p>
      </li>
    </ul>
  </li>
  <li>
    <p><strong><em>ChatGPT 학습 방법</em></strong></p>

    <ul>
      <li>
        <p>일반적인 언어모델은 다음 토큰 예측 or 일부 태크스를 수행하는 것에 초점</p>

        <blockquote>
          <p>사람의 지시에 따르지 못함</p>
        </blockquote>
      </li>
      <li>
        <p>Instruction Tuning을 통해 사람의 명령을 따르도록 Fine Tuning</p>
      </li>
    </ul>

    <ol>
      <li>
        <p>SFT(Supervised Fine Tuning)</p>

        <ul>
          <li>
            <p>예제 데이터 수집 후 Supervised policy 학습</p>
          </li>
          <li>
            <p>이를 위해 지시 프롬프트와 그에 대한 결과물로 이루어진 데이터셋 정의</p>
          </li>
          <li>
            <p>해당 데이터셋을 GPT-3에 대해 Fine-Tuning → SFT 모델</p>
          </li>
        </ul>
      </li>
      <li>
        <p>결과물에 대한 사람의 선호도 데이터 학습</p>

        <ul>
          <li>
            <p>Reward Model 학습 : SFT로 생성된 결과물에 대해 사람의 선호도를 반영할 수 있는 Reward Model 학습</p>
          </li>
          <li>
            <p>Comparison dataset은 33K 개의 프롬프트(프롬프트와 그에 따른 결과물, 그 결과에 대한 선호도 순위로 구성)로 이를 Reward Model 학습에 적합하게 구성</p>
          </li>
          <li>
            <p>프롬프트가 주어질 때 Reward Model은 결과물에 대해 사람의 선호도를 예측하는 방법 학습</p>
          </li>
        </ul>
      </li>
      <li>
        <p>RLHF</p>

        <ul>
          <li>
            <p>강화학습을 통해 Reward Model에 대해 policy를 최적화</p>

            <ol>
              <li>
                <p>GPT는 프롬프트를 보고 결과 문장 생성</p>
              </li>
              <li>
                <p>생성된 문장들을 Reward Model이 평가, reward 계산</p>
              </li>
              <li>
                <p>보상 값이 GPT에게 주어지고 모델은 보상을 최대화하는 방향, 사람이 원하는 문장을 생성하는 방향으로 정책 업데이트</p>
              </li>
            </ol>
          </li>
        </ul>
      </li>
    </ol>
  </li>
</ul>

<hr />

<ul>
  <li>
    <p><strong><em>ChatGPT 활용법</em></strong></p>

    <ul>
      <li>
        <p>Persona Injection : 사용자의 특성, GPT의 역할 등을 먼저 정의 후 태스크 수행</p>
      </li>
      <li>
        <p>프롬프트 구성 : 지시사항, 참고 데이터, 출력 지도, 사용자 입력데이터</p>
      </li>
      <li>
        <p>출력물 형태 지정</p>
      </li>
      <li>
        <p>문장이 길어지거나 복잡하다면 구역을 지정</p>
      </li>
      <li>
        <p>예시 들기</p>
      </li>
    </ul>
  </li>
</ul>

<hr />

<h2 id="peft">PEFT</h2>

<blockquote>
  <p>모델이 점점 커지면서 일반적인 그래픽카드로 모델 전체를 fine tuning하는 것은 불가능</p>
</blockquote>

<ul>
  <li>
    <p><strong><em>PEFT</em></strong> : 사전 훈련된 언어 모델을 특정 작업이나 상황에 적용할 때, 가중치의 일부만 업데이트하는 fine tuning 방법</p>

    <ul>
      <li>
        <p>일부만 fine tuning하므로 연산량 대폭 감소</p>
      </li>
      <li>
        <p>catastrophic forgetting 완화</p>
      </li>
      <li>
        <p>적은 데이터에서 fine tuning 하거나, 도메인 밖의 데이터를 일반화할 때 좋은 성능</p>
      </li>
    </ul>

    <blockquote>
      <p>적은 수의 파라미터를 학습하는 것만으로 모델 전체를 fine tuning 하는 것과 유사한 효과</p>
    </blockquote>
  </li>
  <li>
    <p><strong><em>Prefix-Tuning</em></strong> : 연속적인 태스크 특화 벡터를 활용해 언어모델을 최적화</p>

    <ul>
      <li>
        <p>각 layer의 입력 앞에 task-specific vectors를 붙여 tuning</p>
      </li>
      <li>
        <p>각 task에 대하여 파라미터를 튜닝시켜 task마다 적합한 task-specific vectors 도출</p>
      </li>
    </ul>

    <blockquote>
      <p>하나의 언어모델로 여러 개의 태스크를 처리할 수 있음</p>
    </blockquote>
  </li>
  <li>
    <p><strong><em>Prompt Tuning</em></strong> : 전체 파라미터 튜닝하는 대신, 입력 프롬프트 임베딩만 학습</p>

    <ul>
      <li>PSMs를 동결시키고 입력 텍스트에 추가되는 downstream task 당 k개의 토큰만 학습</li>
    </ul>

    <blockquote>
      <p>모델에 입력되는 프롬프트에 해당하는 가중치만 학습</p>
    </blockquote>
  </li>
  <li>
    <p><strong><em>P-Tuning</em></strong> : PLMs의 전체 가중치를 fine-tuning하지 않고, continuous prompt embeddings만 tuning</p>

    <ul>
      <li>
        <p>언어모델의 입력부에 prompt encoder를 두어 나온 출력 값을 prompt의 tokne embedding으로 사용</p>
      </li>
      <li>
        <p>태스크와 관련된 anchor tokens을 추가하여 성능 개선</p>
      </li>
    </ul>
  </li>
  <li>
    <p><strong><em>LoRA</em></strong> : 사전 학습된 가중치를 고정한 상태로 유지하며, dense layer 변화에 대한 rank decomposition metrices를 최적화</p>

    <ul>
      <li>기존 출력층에 저차원의 피드포워드 layer를 추가, adapter layer만 학습</li>
    </ul>
  </li>
  <li>
    <p><strong><em>Quantization</em></strong> : 추론 시간을 줄이는 것이 주 목적</p>

    <ul>
      <li>모델의 파라미터를 lower bit로 표현함으로써 계산과 메모리 접근 속도를 높이는 경량화 기법</li>
    </ul>
  </li>
  <li>
    <p><strong><em>QLoRA</em></strong> : 기존 사전학습 모델의 가중치는 양자화로 저장, LoRA에 의해 더해지는 가중치는 16 bit finetuning 유지</p>

    <blockquote>
      <p>메모리 사용량을 대폭 줄임으로써 효율적으로 언어 모델 사용 가능</p>
    </blockquote>
  </li>
  <li>
    <p><strong><em>IA3</em></strong> : Self-Attention, Cross-Attention에서의 키, 밸류 값을 rescale해주는 벡터와 position-wise feed-forward network의 값에 rescale을 해주는 벡터를 추가하여 모델 튜닝</p>

    <blockquote>
      <p>기존에 공개된 LoRA보다 적은 파라미터를 사용</p>
    </blockquote>
  </li>
</ul>

<hr />]]></content><author><name>Lee Jeong Min</name></author><category term="Bootcamp" /><category term="python" /><category term="AI" /><category term="패스트캠퍼스" /><category term="패스트캠퍼스AI부트캠프" /><category term="업스테이지패스트캠퍼스" /><category term="UpstageAILab" /><category term="국비지원" /><category term="패스트캠퍼스업스테이지에이아이랩" /><category term="패스트캠퍼스업스테이지부트캠프" /><summary type="html"><![CDATA[◆ Pytorch - DNN 구현 (25.06.18 위키라이더) LM to LMM 강의의 Large Language Model 기초 정리 What is Large Language Model? Large Language Model(LLM) : 방대한 파라미터 수를 가진 언어모델 전통적인 머신러닝 모델 VS LLM 전통적인 언어모델은 각각의 태스크 처리를 위해 각각의 ML 모델이 필요 LLM은 수많은 데이터로 훈련시키기에 여러 태스크를 하나의 LLM에서 처리 가능 LLM의 핵심 Human Alignment : 인간의 선호도를 기반으로 튜닝 첫 Pre-training을 통해 언어를 학습 후 두 번째 fine-tuning(Instruction Tuning)에서 인간의 선호도를 학습 리셋 모먼트 : 인간의 업무 생산성을 높여주는 수많은 도구들이 등장 → AI Agent In-Context Learning : 내재화된 In-Context 파라미터 정보를 이용하여 실제 모델의 능력을 끌어냄 Zero-shot : 예시를 주지 않고 명령만 줌 One-shot : 하나의 예시를 같이 줌 Few-show : 여러개의 예시를 같이 줌 특정 크기를 기점으로 모델의 성능이 급격히 상승 → 없던 능력이 생김(번역만 할줄 알던 모델이 요약, 정리 등의 다양한 태스크) : 창발성 LLM의 제작 프로세스 필요한 리소스들 - Infra - Hyper Scale Cloud - Super Computing - Hyper Scale Data Center - 운영 환경 (하드웨어) - Backbone Model - EX) ChatGPT : GPT 3.5 기반으로 학습 - Tuning (비용 효율적 측면) - 경량화 방법 - 반도체 기술 - Data Data Processing Raw Corpus 수집(최소 1조개 이상) Quality Filtering : 품질이 낮은 데이터 필터링 De-duplication : 중복 데이터 제거 Privacy Reduction : 개인 정보와 관련된 데이터 제거 Tokenization Pre-training &amp; Supervised Fine-tuning Pre-training 과정에서 문장을 생성 (다음 단어를 예측하면서) Supervised Finetuning을 통해 질문에 잘 답변할 수 있도록 학습 LLM의 방향성 Data &amp; Size Chinchilla : GPT에 비해 모델의 크기는 작아지고, 데이터는 많아짐 모델의 전체 용량에 비해 일부만 사용하던 것을 데이터를 늘려 전체 용량 사용 Modeling VS Data 실제 모델의 크기를 키웠을 때 Vanilla Transformer(Baseline Model)의 성능이 가장 좋았음 모델의 크기를 키우고 데이터의 양을 늘리는 것이 중요 데이터 구성 말뭉치의 출처에 대한 조합에 따라 사전학습의 성능이 달라짐 데이터 구성 / 데이터 비율 또한 중요함 결국 사전학습 모델의 크기가 언어 모델의 능력이 창발됨 Multimodal 언어에만 한정하는게 아닌 다양한 작업을 할 수 있는 모델 (Vision, Speech, … etc) Multilingual 다국어 처리를 위한 모델 대부분의 모델들은 현재 영어를 기반으로 처리 → 다양한 언어 처리 목적 Synthetic Data 사람보다 뛰어나게 labeling하는 case가 발생 모델의 성능이 올라갈수록 사람이 labeling하는 것보다 우수한 성능을 보여줌 Domain Specialized 각 태스크에 맞게 잘 처리하는 것도 중요 (도메인 특화) Evaluation 기존 ML 평가방식과 다른 평가방식이 필요 Reasoning, Knowledge, Conversation, Creativity, … 등 새로운 평가방식 사용 뿐만 아니라 기존의 자연어 처리 태스크에서 부족한 수학문제와 같은 추론, 상식, 바이어스 문제 등을 얼마나 잘 해결하는지의 평가 방식도 존재 LLM-Eval : LLM이 LLM을 평가 Prompt Engineering 대화형 AI가 생성하는 결과물의 품질을 높일 수 있는 prompt 입력 값들의 조합을 탐색하는 작업 Instruction, Context, Input Data, Output Indicator 등의 예시를 주는 방식 등 Chain-of-thought prompting : 답변에 도달하는 과정을 학습시키는 목적 / 사람의 생각의 흐름을 학습 Optimization, Tuning : Lasting step by step, 감정 호소 등 고품질의 결과물을 도출할 수 있도록 유도 Prompt Parameter Tuning Temperature : 답변의 창의성과 무작위성 조정 / 값이 낮을수록 사실에 근거한 정확한 답변, 높을수록 창의적인 결과물 Top_p : 답변의 무작위성 제어 frequency_penalty : 값이 높을수록 흔하지 않은 단어를 답변에 포함할 가능성이 낮음 presence_penalty : 값이 높을수록 유사하거나 동일한 단어 및 문구를 답변 시 반복할 가능성이 낮음 Automatic Curriculum : 목표만 설정하면 달성을 위해 필요한 것들을 자동으로 실행 LLM의 근간 이론 In-Context Learning Fine Tuning은 대규모 corpus로 사전학습 후 적은 규모의 특화 데이터로 일부 task에 대해 능력을 집중적으로 향상시키는 작업 모델의 가중치를 업데이트하면서 gradient가 업데이트 됨 In-Context Learning은 Zero-shot, One-shot, Few-shot을 통해 프롬프트를 주는 것 만으로도 성능 향상 가능 모델의 가중치를 업데이트 하지 않음 Zero-Shot Learning : 예시를 전혀 보지 않고 모델 업데이트 없이 새로운 태스크 수행 특정 작업(독해, 번역, 요약, Q&amp;A 등)에서 기존의 SOTA 모델들보다 좋음 One-Shot Learning : 단 하나의 예시와 task에 대한 자연어 지시문을 제공 사람이 소통하는 방법과 가장 흡사 Few-Shot Learning : 모델 추론 시 단 몇 개의 예시만 참고하여 정답 생성 새로운 태스크에 효율적으로 빠르게 적응 ChatGPT ChatGPT Multi-Tasking : 기존의 chatbot은 여러 개의 언어 모델이 조합됨 / ChatGPT는 하나의 모델이지만 다양한 태스크를 수행할 수 있음 외부 Plugin과 결합하려는 시도 GPT-3.5를 fine-tuning InstructGPT의 ‘sibling model’, 학습 방식 유사 Demonstration Data : 데이터를 대화형으로 바꿈 보상 모델을 활용하여 강화학습으로 업데이트 ChatGPT 학습 방법 일반적인 언어모델은 다음 토큰 예측 or 일부 태크스를 수행하는 것에 초점 사람의 지시에 따르지 못함 Instruction Tuning을 통해 사람의 명령을 따르도록 Fine Tuning SFT(Supervised Fine Tuning) 예제 데이터 수집 후 Supervised policy 학습 이를 위해 지시 프롬프트와 그에 대한 결과물로 이루어진 데이터셋 정의 해당 데이터셋을 GPT-3에 대해 Fine-Tuning → SFT 모델 결과물에 대한 사람의 선호도 데이터 학습 Reward Model 학습 : SFT로 생성된 결과물에 대해 사람의 선호도를 반영할 수 있는 Reward Model 학습 Comparison dataset은 33K 개의 프롬프트(프롬프트와 그에 따른 결과물, 그 결과에 대한 선호도 순위로 구성)로 이를 Reward Model 학습에 적합하게 구성 프롬프트가 주어질 때 Reward Model은 결과물에 대해 사람의 선호도를 예측하는 방법 학습 RLHF 강화학습을 통해 Reward Model에 대해 policy를 최적화 GPT는 프롬프트를 보고 결과 문장 생성 생성된 문장들을 Reward Model이 평가, reward 계산 보상 값이 GPT에게 주어지고 모델은 보상을 최대화하는 방향, 사람이 원하는 문장을 생성하는 방향으로 정책 업데이트 ChatGPT 활용법 Persona Injection : 사용자의 특성, GPT의 역할 등을 먼저 정의 후 태스크 수행 프롬프트 구성 : 지시사항, 참고 데이터, 출력 지도, 사용자 입력데이터 출력물 형태 지정 문장이 길어지거나 복잡하다면 구역을 지정 예시 들기 PEFT 모델이 점점 커지면서 일반적인 그래픽카드로 모델 전체를 fine tuning하는 것은 불가능 PEFT : 사전 훈련된 언어 모델을 특정 작업이나 상황에 적용할 때, 가중치의 일부만 업데이트하는 fine tuning 방법 일부만 fine tuning하므로 연산량 대폭 감소 catastrophic forgetting 완화 적은 데이터에서 fine tuning 하거나, 도메인 밖의 데이터를 일반화할 때 좋은 성능 적은 수의 파라미터를 학습하는 것만으로 모델 전체를 fine tuning 하는 것과 유사한 효과 Prefix-Tuning : 연속적인 태스크 특화 벡터를 활용해 언어모델을 최적화 각 layer의 입력 앞에 task-specific vectors를 붙여 tuning 각 task에 대하여 파라미터를 튜닝시켜 task마다 적합한 task-specific vectors 도출 하나의 언어모델로 여러 개의 태스크를 처리할 수 있음 Prompt Tuning : 전체 파라미터 튜닝하는 대신, 입력 프롬프트 임베딩만 학습 PSMs를 동결시키고 입력 텍스트에 추가되는 downstream task 당 k개의 토큰만 학습 모델에 입력되는 프롬프트에 해당하는 가중치만 학습 P-Tuning : PLMs의 전체 가중치를 fine-tuning하지 않고, continuous prompt embeddings만 tuning 언어모델의 입력부에 prompt encoder를 두어 나온 출력 값을 prompt의 tokne embedding으로 사용 태스크와 관련된 anchor tokens을 추가하여 성능 개선 LoRA : 사전 학습된 가중치를 고정한 상태로 유지하며, dense layer 변화에 대한 rank decomposition metrices를 최적화 기존 출력층에 저차원의 피드포워드 layer를 추가, adapter layer만 학습 Quantization : 추론 시간을 줄이는 것이 주 목적 모델의 파라미터를 lower bit로 표현함으로써 계산과 메모리 접근 속도를 높이는 경량화 기법 QLoRA : 기존 사전학습 모델의 가중치는 양자화로 저장, LoRA에 의해 더해지는 가중치는 16 bit finetuning 유지 메모리 사용량을 대폭 줄임으로써 효율적으로 언어 모델 사용 가능 IA3 : Self-Attention, Cross-Attention에서의 키, 밸류 값을 rescale해주는 벡터와 position-wise feed-forward network의 값에 rescale을 해주는 벡터를 추가하여 모델 튜닝 기존에 공개된 LoRA보다 적은 파라미터를 사용]]></summary></entry><entry><title type="html">Attention is all you need</title><link href="https://leeinformation.github.io/Leeinformation.github.io/thesis/Transformer/" rel="alternate" type="text/html" title="Attention is all you need" /><published>2025-06-23T00:00:00+00:00</published><updated>2025-06-23T00:00:00+00:00</updated><id>https://leeinformation.github.io/Leeinformation.github.io/thesis/Transformer</id><content type="html" xml:base="https://leeinformation.github.io/Leeinformation.github.io/thesis/Transformer/"><![CDATA[<head>
  <style>
    table.dataframe {
      white-space: normal;
      width: 100%;
      height: 240px;
      display: block;
      overflow: auto;
      font-family: Arial, sans-serif;
      font-size: 0.9rem;
      line-height: 20px;
      text-align: center;
      border: 0px !important;
    }

    table.dataframe th {
      text-align: center;
      font-weight: bold;
      padding: 8px;
    }

    table.dataframe td {
      text-align: center;
      padding: 8px;
    }

    table.dataframe tr:hover {
      background: #b8d1f3; 
    }

    .output_prompt {
      overflow: auto;
      font-size: 0.9rem;
      line-height: 1.45;
      border-radius: 0.3rem;
      -webkit-overflow-scrolling: touch;
      padding: 0.8rem;
      margin-top: 0;
      margin-bottom: 15px;
      font: 1rem Consolas, "Liberation Mono", Menlo, Courier, monospace;
      color: $code-text-color;
      border: solid 1px $border-color;
      border-radius: 0.3rem;
      word-break: normal;
      white-space: pre;
    }

  .dataframe tbody tr th:only-of-type {
      vertical-align: middle;
  }

  .dataframe tbody tr th {
      vertical-align: top;
  }

  .dataframe thead th {
      text-align: center !important;
      padding: 8px;
  }

  .page__content p {
      margin: 0 0 0px !important;
  }

  .page__content p > strong {
    font-size: 0.8rem !important;
  }

  </style>
</head>

<h1 id="abstract">Abstract</h1>

<p>대표적인 sequence transduction(EX 문장 번역 등) 모델은 인코더와 디코더를 포함하는 RNN or CNN으로 구성</p>

<p>가장 뛰어난 성능을 보이는 모델은 <strong><em>attention mechanism</em></strong>을 통해 인코더와 디코더를 연결</p>

<blockquote>
  <p>기존에 존재하던 Sequence Transduction 모델은 주요 연산을 RNN, CNN으로 수행, 보조적인 역할을 Attention 수행</p>
</blockquote>

<p>저자가 제시하는 <strong>Transformer</strong>라는 새로운 네트워크 아키텍처는 RNN과 CNN을 완전히 배제하고 <strong><em>attention mechanism</em></strong>만을 사용한다.</p>

<p>실험 결과 다음과 같은 우수한 성능을 보여줌</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>- 영어-독일어 번역 작업에서 앙상블을 포함한 기존 최고의 성능 모델 보다 높은 성능 달성

- 영어-프랑스어 작업에서 8개의 GPU로 학습 → 학습 비용 측면 및 성능 측면 모두 우수

- 대규모 데이터 / 제한된 데이터 모두 영어 선거구 구문 분석에서 잘 적용됨 → Transformer가 다른 task에서도 일반화가 잘 됨
</code></pre></div></div>

<h1 id="introduction">Introduction</h1>

<p>LSTM, GRU와 같은 RNN은 sequence 모델링 및 변환에서 SOTA</p>

<ul>
  <li>
    <p>RNN 한계</p>

    <ul>
      <li>
        <p>입력 및 출력 시퀀스의 심볼 위치를 따라 계산</p>
      </li>
      <li>
        <p>\(h_t = f(h_{t-1}, x_t)\) 로 표현 → 이전 상태(\(h_{t-1}\))에 의존하여 순차적으로 처리</p>
      </li>
      <li>
        <p>이러한 순차적 처리로 병렬화가 불가능</p>
      </li>
      <li>
        <p>역전파를 위해 hidden state를 메모리에 추가로 저장해야 하므로 메모리 제약적</p>
      </li>
    </ul>
  </li>
</ul>

<blockquote>
  <p>이를 위해 <strong>인수분해 기법</strong> 및 <strong>조건부 계산</strong>을 통해 효율을 증가시켰으나, sequence 연산 자체의 근본적 제약은 여전히 존재</p>
</blockquote>

<ul>
  <li>
    <p><strong>Attention</strong></p>

    <ul>
      <li>
        <p>RNN만으로는 길이가 길어질수록 sequence 모델링 및 다음 은닉층으로 전달하는것이 힘듦</p>
      </li>
      <li>
        <p>입출력 sequence 거리와 관계없이 의존성을 모델링 가능</p>
      </li>
      <li>
        <p>대부분의 경우에서 RNN 모델과 함께 사용됨</p>
      </li>
    </ul>
  </li>
  <li>
    <p><strong><em>Transformer</em></strong></p>

    <ul>
      <li>
        <p>순환 구조를 완전히 제거</p>
      </li>
      <li>
        <p><strong>Attention</strong>만으로 입출력 간 전역 의존성을 도출</p>
      </li>
    </ul>
  </li>
</ul>

<h2 id="background">Background</h2>

<p>순차적 계산을 줄이고 병렬화를 시도하려고 했던 CNN 모델들은 CNN을 기본 블록으로 모든 위치를 병렬 처리</p>

<p>→ 여전히 내부 은닉층이 깊어지면 거리에 따라 선형적 or 로그적으로 연산량이 증가함 → 먼 거리에 대한 의존성 학습이 어려움</p>

<ul>
  <li>
    <p>Transformer</p>

    <ul>
      <li>
        <p>임의의 두 위치 간 관계를 상수 시간으로 계산</p>
      </li>
      <li>
        <p>거리에 무관한 연산량</p>
      </li>
      <li>
        <p>Attention-weighted positions 평균화로 해상도 감소 → 연산 횟수 감소</p>
      </li>
      <li>
        <p>Multi-Head Attention으로 상쇄 가능</p>
      </li>
    </ul>
  </li>
  <li>
    <p><strong><em>Self-Attention(Intra-Attention)</em></strong></p>

    <ul>
      <li>
        <p>단일 sequence(문장) 내 서로 다른 위치를 연관시킴</p>
      </li>
      <li>
        <p>이전부터 Self-Attention은 독해, 추상적 요약, 텍스트 수반 등 다양한 작업에서 사용됨</p>
      </li>
    </ul>

    <p>◆ RNN, CNN을 사용하지 않고 self-attention만으로 입출력을 최초로 표현 → <strong>Transformer</strong></p>
  </li>
</ul>

<h1 id="model-architecture">Model Architecture</h1>

<p>전체적 구조로 <strong>Encoder-Decoder</strong> 사용</p>

<ul>
  <li>
    <p><strong><em>Encoder</em></strong> : 입력 sequence (\(x_1, x_2, ..., x_n\)) → 연속 표현 (\(z_1, z_2, z_n\))</p>

    <ul>
      <li>
        <p>입력 텍스트를 토큰화 → 정수 ID로 매핑 → 실수 벡터 변환</p>
      </li>
      <li>
        <p>이를 통해 단어 각각의 의미적 유사성이 벡터 공간에서 거리로 표현됨</p>
      </li>
    </ul>
  </li>
  <li>
    <p><strong><em>Decoder</em></strong> : 연속 표현 (\(z_1, z_2, ..., z_n\)) → 출력 sequence (\(y_1, y_2, ..., y_n\)) 생성</p>
  </li>
</ul>

<h2 id="encoder-and-decoder-stacks">Encoder and Decoder Stacks</h2>

<ul>
  <li>
    <p>Encoder</p>

    <ul>
      <li>
        <p>6개의 동일한 layer를 쌓음</p>
      </li>
      <li>
        <p>각 레이어는 2개의 sub-layer 존재</p>

        <ol>
          <li>
            <p>Multi-head self-attention</p>
          </li>
          <li>
            <p>Position-wise fully connected feed-forward network</p>
          </li>
        </ol>
      </li>
      <li>
        <p>이 각각의 sub-layer에 잔차 연결을 적용한 다음 layer 정규화를 적용</p>
      </li>
      <li>
        <p>출력은 \(LayerNorm(x + Sublayer(x))\)</p>
      </li>
      <li>
        <p>모델의 모든 sub-layer와 embbeding의 출력 차원은 512</p>
      </li>
      <li>
        <p>Encoder에서는 문장의 전체적인 의미와 맥락을 파악</p>
      </li>
    </ul>
  </li>
  <li>
    <p>Decoder</p>

    <ul>
      <li>
        <p>6개의 동일한 layer를 쌓음</p>
      </li>
      <li>
        <p>각 레이어는 3개의 sub-layer 존재</p>

        <ol>
          <li>
            <p>Masked multi-head self-attention (해당 layer를 수정하여 현재 위치와 이전 위치의 정보만을 사용 (미래 위치 차단))</p>
          </li>
          <li>
            <p>Multi-head attention over encoder output (Query는 이전 decoder layer, Key, Value는 encoder output)</p>
          </li>
          <li>
            <p>Position-wise feed-forward network</p>
          </li>
        </ol>
      </li>
      <li>
        <p>Encoder와 동일하게 잔차연결 + layer 정규화 적용</p>
      </li>
      <li>
        <p>Decoder에서는 입력 문장에 대해 출력 문장을 하나씩 순서대로 생성</p>
      </li>
    </ul>
  </li>
</ul>

<h2 id="attention">Attention</h2>

<ul>
  <li>
    <p><strong>Scaled Dot-Product Attention</strong></p>

    <ul>
      <li>
        <p>입력 : Query, \(d_k\)(Key), \(d_v\)(Value)</p>
      </li>
      <li>
        <p>출력 : \(Attention(Q, K, V) = softmax\left(\frac{QK^T}{\sqrt{d_k}}\right)V\)</p>

        <ul>
          <li>
            <p>\({QK^T}\) : 모든 Query와 모든 Key의 내적 → 유사도 계산 → 각 단어들의 문법적, 의미적 연관 확인</p>
          </li>
          <li>
            <p>\(\left(1/\sqrt{d_k}\right)\) : Query와 Key의 차원이 커질수록 내적 값이 커지므로, Query와 Key의 차원을 제곱근으로 나눠서 정규화 (스케일링)</p>

            <p>★ \(\left(\frac{QK^T}{\sqrt{d_k}}\right)\) : Attention scores 계산</p>
          </li>
          <li>
            <p>softmax : 각 단어 별 Attention socres를 확률 분포로 변환</p>
          </li>
          <li>
            <p>\(V\) : Value와 가중합을 취함으로써 가장 확률 분포로 변환된 Attention scores를 기반으로 확률에 비례하여 모든 Value를 가중 평균</p>
          </li>
        </ul>
      </li>
      <li>
        <p>정리</p>
      </li>
    </ul>

    <blockquote>
      <p>Query, Key의 내적으로 유사도 계산 → 정규화를 통한 스케일링 → softmax를 통한 확률 분포 변환 → Value들의 가중 평균 계산</p>
    </blockquote>
  </li>
</ul>

<hr />

<ul>
  <li>
    <p><strong>Multi-Head Attention</strong></p>

    <ul>
      <li>
        <p>Single-Head Attention의 경우 하나의 관점에서만 attention scores를 계산</p>
      </li>
      <li>
        <p>여러 개의 관점에서 attention scores를 계산하여 병합하여 사용</p>

        <ol>
          <li>
            <p>문법적 관계</p>
          </li>
          <li>
            <p>의미적 유사성</p>
          </li>
          <li>
            <p>감정 관계</p>
          </li>
          <li>
            <p>개채명 인식</p>
          </li>
          <li>
            <p>…</p>
          </li>
        </ol>
      </li>
      <li>
\[MultiHead(Q, K, V) = Concat(head_1, head_2, ..., head_h) * W^O\]
      </li>
      <li>
\[head_i = Attention(Q * W^Q_i, K * W^K_i, V * W^V_i)\]

        <ul>
          <li>각 헤드의 가중치(\(W\))가 다름 → 서로 다른 관점에서 해석</li>
        </ul>
      </li>
      <li>각 헤드는 Single-Head Attention보다 작은 차원으로 이루어져 있어 총 계산량은 거의 동일</li>
    </ul>
  </li>
</ul>

<hr />

<ul>
  <li>
    <p><strong>Applications of Attention in Model</strong></p>

    <ul>
      <li>
        <p><strong><em>Encoder_Decoder Attention</em></strong></p>

        <ul>
          <li>
            <p>Query : 이전 Decoder layer</p>
          </li>
          <li>
            <p>Key, Value : Encoder Output</p>
          </li>
          <li>
            <p>결론 : Decoder에서 단어를 생성할 때마다 입력 sequence의 모든 위치를 참조하여 다음 단어를 생성</p>
          </li>
        </ul>
      </li>
      <li>
        <p><strong><em>Encoder</em></strong></p>

        <ul>
          <li>
            <p>self-attention layers가 포함</p>
          </li>
          <li>
            <p>Key, Value, Query 모두 이전 Encoder 계층의 출력에서 가져옴</p>
          </li>
          <li>
            <p>모든 위치를 참조하여 문맥 이해</p>
          </li>
        </ul>
      </li>
      <li>
        <p><strong><em>Decoder</em></strong></p>

        <ul>
          <li>
            <p>self-attention layers가 포함</p>
          </li>
          <li>
            <p>미래의 정보가 과거로 흘러들어가는 것을 방지 → Masking</p>
          </li>
        </ul>
      </li>
    </ul>
  </li>
</ul>

<h2 id="position-wise-feed-forward-networks">Position-wise Feed-Forward Networks</h2>

<p>Attention에는 sub-layers 이외에도 완전 연결 피드 포워드 네트워크도 존재</p>

<ul>
  <li>
    <p><strong>Fully connected feed-forward</strong></p>

    <ul>
      <li>
        <p>두 개의 선형 변환과 사이에 ReLU 활성화 함수 적용</p>
      </li>
      <li>
\[FFN(x) = max(0, xW_1 + b_1)W_2 + b_2\]
      </li>
      <li>비선형 변환 → attention이 처리한 정보를 변환을 통해 더욱 복잡한 관계를 학습</li>
    </ul>
  </li>
</ul>

<h2 id="embeddings-and-softmax">Embeddings and Softmax</h2>

<blockquote>
  <p>학습된 임베딩을 사용하여 입력 및 출력 토큰을 같은 크기의 벡터로 통일</p>
</blockquote>

<ul>
  <li>
    <p>학습된 임베딩 : 특정 단어(벡터)가 처음에는 무작위 벡터 → 학습 과정에서 의미를 획득</p>

    <ul>
      <li>해당 과정은 학습 데이터에서 패턴을 발견하여 특정 벡터의 방향으로 학습</li>
    </ul>
  </li>
  <li>
    <p>Transformer에서 입출력 토큰을 임베딩</p>

    <ul>
      <li>
        <p>입력 : 입력 문장에 대해 이해 및 의미 파악(학습)</p>
      </li>
      <li>
        <p>출력 : 다음에 나올 단어 예측을 위해 출력 토큰을 다시 임베딩하여 추론에 사용</p>

        <ul>
          <li>디코더의 출력 → 벡터 출력 → 선형 변환 → softmax → 다음 토큰 생성 확률 예측</li>
        </ul>
      </li>
    </ul>
  </li>
</ul>

<blockquote>
  <p>입력 임베딩, 출력 임베딩, softmax 전 선형 변환 간 <strong>동일한 가중치 행렬</strong> 공유 / 임베딩 layer에서는 가중치에 벡터 크기의 제곱근을 곱해줌</p>
</blockquote>

<ul>
  <li>
    <p>동일한 가중치를 공유 → 의미적 일관성을 보장</p>
  </li>
  <li>
    <p>벡터 크기의 제곱근을 곱해줌 → Positional encoding과 크기 균형을 맞춰주기 위해(스케일링)</p>
  </li>
</ul>

<h2 id="positional-encoding">Positional Encoding</h2>

<p>Transformer에는 순환 및 합성곱 존재 X → 순서 정보를 모름</p>

<blockquote>
  <p>Encoder, Decoder의 입력 임베딩에 Positional Encoding 추가</p>
</blockquote>

<ul>
  <li>
\[PE(pos, 2i) = sin(pos/1000^{2i/d_{model}})\]
  </li>
  <li>
\[PE(pos, 2i+1) = cos(pos/1000^{2i/d_{model}})\]
  </li>
  <li>
    <p>모델이 <strong>상대적 위치</strong>를 학습</p>

    <ul>
      <li>
        <p>각 차원마다 서로 다른 정현파를 가짐</p>

        <ul>
          <li>단어들 간 거리가 짧은 패턴 ~ 긴 패턴까지 학습 가능</li>
        </ul>
      </li>
    </ul>
  </li>
</ul>

<blockquote>
\[PE(pos+k) = T_K \times PE(pos)\]
</blockquote>

<ul>
  <li>
    <p>단어 간 거리가 같다면 pos(위치)는 달라져도 $T_K$는 변하지 않음</p>

    <ul>
      <li>\(T_K\)는 <strong>거리 K</strong>를 변환 행렬로 구현한 것 → 삼각함수의 덧셈정리로 구현</li>
    </ul>
  </li>
  <li>
    <p>즉, pos 값과는 무관한 일관된 패턴 학습 가능</p>
  </li>
  <li>
    <p>자연어 특성 상 절대적 위치는 다르지만, 상대적인 구조는 동일하기에 상대적 위치를 학습</p>
  </li>
  <li>
    <p>상대적 구조를 표현하는 것이 \(T_K\)</p>
  </li>
</ul>

<h1 id="why-self-attention">Why Self-Attention</h1>

<p>RNN/CNN 대신 Self-Attention을 사용한 이유</p>

<ul>
  <li>
    <p>비교 기준</p>

    <ol>
      <li>
        <p>layer당 총 계산 복잡도</p>
      </li>
      <li>
        <p>병렬화할 수 있는 계산의 양</p>
      </li>
      <li>
        <p>네트워크 장거리 의존성 경로 길이</p>
      </li>
    </ol>
  </li>
  <li>
    <p>n : sequence length</p>
  </li>
  <li>
    <p>d : embedding dimension</p>
  </li>
  <li>
    <p>k : kernel size</p>
  </li>
  <li>
    <p>r : restricted attention span</p>
  </li>
</ul>

<hr />

<table>
  <thead>
    <tr>
      <th>Layer Type</th>
      <th>Complexity per Layer</th>
      <th>Sequential Operations</th>
      <th>Maximum Path Length</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Self-Attention</td>
      <td>\(O(n² · d)\)</td>
      <td>\(O(1)\)</td>
      <td>\(O(n)\)</td>
    </tr>
    <tr>
      <td>Recurrent</td>
      <td>\(O(n · d²)\)</td>
      <td>\(O(n)\)</td>
      <td>\(O(n)\)</td>
    </tr>
    <tr>
      <td>Convolutional</td>
      <td>\(O(k · n · d²)\)</td>
      <td>\(O(1)\)</td>
      <td>\(O(log_k(n))\)</td>
    </tr>
    <tr>
      <td>Self-Attention (restricted)</td>
      <td>\(O(r · n · d)\)</td>
      <td>\(O(1)\)</td>
      <td>\(O(n/r)\)</td>
    </tr>
  </tbody>
</table>

<hr />

<ul>
  <li>
    <p><strong><em>계산 복잡도</em></strong></p>

    <ul>
      <li>
        <p>대부분의 경우 임베딩 차원이 sequence 길이 보다 큼</p>

        <ul>
          <li>임베딩 차원이 클수록 표현력이 증가</li>
        </ul>
      </li>
      <li>
        <p>따라서 임베딩 차원이 선형적으로 증가하는 Self-Attention의 계산 복잡도가 CNN,RNN보다 효율적</p>
      </li>
    </ul>
  </li>
  <li>
    <p><strong><em>병렬 처리 능력</em></strong></p>

    <ul>
      <li>
        <p>RNN : 순차적 계산이 필수적 → \(O(n)\)</p>
      </li>
      <li>
        <p>CNN : 병렬 처리 가능</p>
      </li>
      <li>
        <p>Self-Attention : 병렬 처리 가능</p>
      </li>
    </ul>
  </li>
  <li>
    <p><strong><em>장거리 의존성</em></strong></p>

    <ul>
      <li>
        <p>RNN : 순차적으로 계산되어 전달되므로 \(O(n)\)</p>
      </li>
      <li>
        <p>단일 CNN : sequence 길이 n에 대하여 k개의 커널이 필요함 → \(O(n/k)\)</p>
      </li>
      <li>
        <p>확장 CNN : 간격을 넓혀서 더 넓은 범위를 연결 → \(O(log_k(n))\)</p>
      </li>
      <li>
        <p>Self-Attention : 모든 위치 쌍이 직접 연결되어 있으므로 \(O(1)\)</p>
      </li>
    </ul>
  </li>
  <li>
    <p>매우 긴 sequence를 처리할 때 계산 성능 향상을 위해 출력 위치 중심으로 입력 sequence에서 \(r\)만큼으로 이웃 제한 → 장거리 의존성이 \(O(n/r)\)로 증가</p>
  </li>
</ul>

<hr />]]></content><author><name>Lee Jeong Min</name></author><category term="Thesis" /><category term="AI" /><category term="blog" /><category term="jekyll" /><summary type="html"><![CDATA[Abstract 대표적인 sequence transduction(EX 문장 번역 등) 모델은 인코더와 디코더를 포함하는 RNN or CNN으로 구성 가장 뛰어난 성능을 보이는 모델은 attention mechanism을 통해 인코더와 디코더를 연결 기존에 존재하던 Sequence Transduction 모델은 주요 연산을 RNN, CNN으로 수행, 보조적인 역할을 Attention 수행 저자가 제시하는 Transformer라는 새로운 네트워크 아키텍처는 RNN과 CNN을 완전히 배제하고 attention mechanism만을 사용한다. 실험 결과 다음과 같은 우수한 성능을 보여줌 - 영어-독일어 번역 작업에서 앙상블을 포함한 기존 최고의 성능 모델 보다 높은 성능 달성 - 영어-프랑스어 작업에서 8개의 GPU로 학습 → 학습 비용 측면 및 성능 측면 모두 우수 - 대규모 데이터 / 제한된 데이터 모두 영어 선거구 구문 분석에서 잘 적용됨 → Transformer가 다른 task에서도 일반화가 잘 됨 Introduction LSTM, GRU와 같은 RNN은 sequence 모델링 및 변환에서 SOTA RNN 한계 입력 및 출력 시퀀스의 심볼 위치를 따라 계산 \(h_t = f(h_{t-1}, x_t)\) 로 표현 → 이전 상태(\(h_{t-1}\))에 의존하여 순차적으로 처리 이러한 순차적 처리로 병렬화가 불가능 역전파를 위해 hidden state를 메모리에 추가로 저장해야 하므로 메모리 제약적 이를 위해 인수분해 기법 및 조건부 계산을 통해 효율을 증가시켰으나, sequence 연산 자체의 근본적 제약은 여전히 존재 Attention RNN만으로는 길이가 길어질수록 sequence 모델링 및 다음 은닉층으로 전달하는것이 힘듦 입출력 sequence 거리와 관계없이 의존성을 모델링 가능 대부분의 경우에서 RNN 모델과 함께 사용됨 Transformer 순환 구조를 완전히 제거 Attention만으로 입출력 간 전역 의존성을 도출 Background 순차적 계산을 줄이고 병렬화를 시도하려고 했던 CNN 모델들은 CNN을 기본 블록으로 모든 위치를 병렬 처리 → 여전히 내부 은닉층이 깊어지면 거리에 따라 선형적 or 로그적으로 연산량이 증가함 → 먼 거리에 대한 의존성 학습이 어려움 Transformer 임의의 두 위치 간 관계를 상수 시간으로 계산 거리에 무관한 연산량 Attention-weighted positions 평균화로 해상도 감소 → 연산 횟수 감소 Multi-Head Attention으로 상쇄 가능 Self-Attention(Intra-Attention) 단일 sequence(문장) 내 서로 다른 위치를 연관시킴 이전부터 Self-Attention은 독해, 추상적 요약, 텍스트 수반 등 다양한 작업에서 사용됨 ◆ RNN, CNN을 사용하지 않고 self-attention만으로 입출력을 최초로 표현 → Transformer Model Architecture 전체적 구조로 Encoder-Decoder 사용 Encoder : 입력 sequence (\(x_1, x_2, ..., x_n\)) → 연속 표현 (\(z_1, z_2, z_n\)) 입력 텍스트를 토큰화 → 정수 ID로 매핑 → 실수 벡터 변환 이를 통해 단어 각각의 의미적 유사성이 벡터 공간에서 거리로 표현됨 Decoder : 연속 표현 (\(z_1, z_2, ..., z_n\)) → 출력 sequence (\(y_1, y_2, ..., y_n\)) 생성 Encoder and Decoder Stacks Encoder 6개의 동일한 layer를 쌓음 각 레이어는 2개의 sub-layer 존재 Multi-head self-attention Position-wise fully connected feed-forward network 이 각각의 sub-layer에 잔차 연결을 적용한 다음 layer 정규화를 적용 출력은 \(LayerNorm(x + Sublayer(x))\) 모델의 모든 sub-layer와 embbeding의 출력 차원은 512 Encoder에서는 문장의 전체적인 의미와 맥락을 파악 Decoder 6개의 동일한 layer를 쌓음 각 레이어는 3개의 sub-layer 존재 Masked multi-head self-attention (해당 layer를 수정하여 현재 위치와 이전 위치의 정보만을 사용 (미래 위치 차단)) Multi-head attention over encoder output (Query는 이전 decoder layer, Key, Value는 encoder output) Position-wise feed-forward network Encoder와 동일하게 잔차연결 + layer 정규화 적용 Decoder에서는 입력 문장에 대해 출력 문장을 하나씩 순서대로 생성 Attention Scaled Dot-Product Attention 입력 : Query, \(d_k\)(Key), \(d_v\)(Value) 출력 : \(Attention(Q, K, V) = softmax\left(\frac{QK^T}{\sqrt{d_k}}\right)V\) \({QK^T}\) : 모든 Query와 모든 Key의 내적 → 유사도 계산 → 각 단어들의 문법적, 의미적 연관 확인 \(\left(1/\sqrt{d_k}\right)\) : Query와 Key의 차원이 커질수록 내적 값이 커지므로, Query와 Key의 차원을 제곱근으로 나눠서 정규화 (스케일링) ★ \(\left(\frac{QK^T}{\sqrt{d_k}}\right)\) : Attention scores 계산 softmax : 각 단어 별 Attention socres를 확률 분포로 변환 \(V\) : Value와 가중합을 취함으로써 가장 확률 분포로 변환된 Attention scores를 기반으로 확률에 비례하여 모든 Value를 가중 평균 정리 Query, Key의 내적으로 유사도 계산 → 정규화를 통한 스케일링 → softmax를 통한 확률 분포 변환 → Value들의 가중 평균 계산 Multi-Head Attention Single-Head Attention의 경우 하나의 관점에서만 attention scores를 계산 여러 개의 관점에서 attention scores를 계산하여 병합하여 사용 문법적 관계 의미적 유사성 감정 관계 개채명 인식 … \[MultiHead(Q, K, V) = Concat(head_1, head_2, ..., head_h) * W^O\] \[head_i = Attention(Q * W^Q_i, K * W^K_i, V * W^V_i)\] 각 헤드의 가중치(\(W\))가 다름 → 서로 다른 관점에서 해석 각 헤드는 Single-Head Attention보다 작은 차원으로 이루어져 있어 총 계산량은 거의 동일 Applications of Attention in Model Encoder_Decoder Attention Query : 이전 Decoder layer Key, Value : Encoder Output 결론 : Decoder에서 단어를 생성할 때마다 입력 sequence의 모든 위치를 참조하여 다음 단어를 생성 Encoder self-attention layers가 포함 Key, Value, Query 모두 이전 Encoder 계층의 출력에서 가져옴 모든 위치를 참조하여 문맥 이해 Decoder self-attention layers가 포함 미래의 정보가 과거로 흘러들어가는 것을 방지 → Masking Position-wise Feed-Forward Networks Attention에는 sub-layers 이외에도 완전 연결 피드 포워드 네트워크도 존재 Fully connected feed-forward 두 개의 선형 변환과 사이에 ReLU 활성화 함수 적용 \[FFN(x) = max(0, xW_1 + b_1)W_2 + b_2\] 비선형 변환 → attention이 처리한 정보를 변환을 통해 더욱 복잡한 관계를 학습 Embeddings and Softmax 학습된 임베딩을 사용하여 입력 및 출력 토큰을 같은 크기의 벡터로 통일 학습된 임베딩 : 특정 단어(벡터)가 처음에는 무작위 벡터 → 학습 과정에서 의미를 획득 해당 과정은 학습 데이터에서 패턴을 발견하여 특정 벡터의 방향으로 학습 Transformer에서 입출력 토큰을 임베딩 입력 : 입력 문장에 대해 이해 및 의미 파악(학습) 출력 : 다음에 나올 단어 예측을 위해 출력 토큰을 다시 임베딩하여 추론에 사용 디코더의 출력 → 벡터 출력 → 선형 변환 → softmax → 다음 토큰 생성 확률 예측 입력 임베딩, 출력 임베딩, softmax 전 선형 변환 간 동일한 가중치 행렬 공유 / 임베딩 layer에서는 가중치에 벡터 크기의 제곱근을 곱해줌 동일한 가중치를 공유 → 의미적 일관성을 보장 벡터 크기의 제곱근을 곱해줌 → Positional encoding과 크기 균형을 맞춰주기 위해(스케일링) Positional Encoding Transformer에는 순환 및 합성곱 존재 X → 순서 정보를 모름 Encoder, Decoder의 입력 임베딩에 Positional Encoding 추가 \[PE(pos, 2i) = sin(pos/1000^{2i/d_{model}})\] \[PE(pos, 2i+1) = cos(pos/1000^{2i/d_{model}})\] 모델이 상대적 위치를 학습 각 차원마다 서로 다른 정현파를 가짐 단어들 간 거리가 짧은 패턴 ~ 긴 패턴까지 학습 가능 \[PE(pos+k) = T_K \times PE(pos)\] 단어 간 거리가 같다면 pos(위치)는 달라져도 $T_K$는 변하지 않음 \(T_K\)는 거리 K를 변환 행렬로 구현한 것 → 삼각함수의 덧셈정리로 구현 즉, pos 값과는 무관한 일관된 패턴 학습 가능 자연어 특성 상 절대적 위치는 다르지만, 상대적인 구조는 동일하기에 상대적 위치를 학습 상대적 구조를 표현하는 것이 \(T_K\) Why Self-Attention RNN/CNN 대신 Self-Attention을 사용한 이유 비교 기준 layer당 총 계산 복잡도 병렬화할 수 있는 계산의 양 네트워크 장거리 의존성 경로 길이 n : sequence length d : embedding dimension k : kernel size r : restricted attention span Layer Type Complexity per Layer Sequential Operations Maximum Path Length Self-Attention \(O(n² · d)\) \(O(1)\) \(O(n)\) Recurrent \(O(n · d²)\) \(O(n)\) \(O(n)\) Convolutional \(O(k · n · d²)\) \(O(1)\) \(O(log_k(n))\) Self-Attention (restricted) \(O(r · n · d)\) \(O(1)\) \(O(n/r)\) 계산 복잡도 대부분의 경우 임베딩 차원이 sequence 길이 보다 큼 임베딩 차원이 클수록 표현력이 증가 따라서 임베딩 차원이 선형적으로 증가하는 Self-Attention의 계산 복잡도가 CNN,RNN보다 효율적 병렬 처리 능력 RNN : 순차적 계산이 필수적 → \(O(n)\) CNN : 병렬 처리 가능 Self-Attention : 병렬 처리 가능 장거리 의존성 RNN : 순차적으로 계산되어 전달되므로 \(O(n)\) 단일 CNN : sequence 길이 n에 대하여 k개의 커널이 필요함 → \(O(n/k)\) 확장 CNN : 간격을 넓혀서 더 넓은 범위를 연결 → \(O(log_k(n))\) Self-Attention : 모든 위치 쌍이 직접 연결되어 있으므로 \(O(1)\) 매우 긴 sequence를 처리할 때 계산 성능 향상을 위해 출력 위치 중심으로 입력 sequence에서 \(r\)만큼으로 이웃 제한 → 장거리 의존성이 \(O(n/r)\)로 증가]]></summary></entry><entry><title type="html">[AI 부트캠프] PyTorch - Implement Deep Learning Models</title><link href="https://leeinformation.github.io/Leeinformation.github.io/bootcamp/DNN_base/" rel="alternate" type="text/html" title="[AI 부트캠프] PyTorch - Implement Deep Learning Models" /><published>2025-06-18T00:00:00+00:00</published><updated>2025-06-18T00:00:00+00:00</updated><id>https://leeinformation.github.io/Leeinformation.github.io/bootcamp/DNN_base</id><content type="html" xml:base="https://leeinformation.github.io/Leeinformation.github.io/bootcamp/DNN_base/"><![CDATA[<head>
  <style>
    table.dataframe {
      white-space: normal;
      width: 100%;
      height: 240px;
      display: block;
      overflow: auto;
      font-family: Arial, sans-serif;
      font-size: 0.9rem;
      line-height: 20px;
      text-align: center;
      border: 0px !important;
    }

    table.dataframe th {
      text-align: center;
      font-weight: bold;
      padding: 8px;
    }

    table.dataframe td {
      text-align: center;
      padding: 8px;
    }

    table.dataframe tr:hover {
      background: #b8d1f3; 
    }

    .output_prompt {
      overflow: auto;
      font-size: 0.9rem;
      line-height: 1.45;
      border-radius: 0.3rem;
      -webkit-overflow-scrolling: touch;
      padding: 0.8rem;
      margin-top: 0;
      margin-bottom: 15px;
      font: 1rem Consolas, "Liberation Mono", Menlo, Courier, monospace;
      color: $code-text-color;
      border: solid 1px $border-color;
      border-radius: 0.3rem;
      word-break: normal;
      white-space: pre;
    }

  .dataframe tbody tr th:only-of-type {
      vertical-align: middle;
  }

  .dataframe tbody tr th {
      vertical-align: top;
  }

  .dataframe thead th {
      text-align: center !important;
      padding: 8px;
  }

  .page__content p {
      margin: 0 0 0px !important;
  }

  .page__content p > strong {
    font-size: 0.8rem !important;
  }

  </style>
</head>

<p>◆ Pytorch - DNN 구현</p>

<blockquote>
  <p>(25.06.18 위키라이더)</p>
</blockquote>

<p>Pytorch 강의의 <strong>Implement Deep Learning Models</strong> 챕터의 <strong><em>DNN</em></strong> 구현과 <strong><em>CNN, RNN</em></strong> 구현을 정리</p>

<hr />

<h1 id="딥러닝에서의-pytorch">딥러닝에서의 Pytorch</h1>

<p>딥러닝을 구현하기 위한 대표적인 라이브러리로는 <strong>Tensorflow</strong>와 <strong>Pytorch</strong>가 있다.</p>

<p>2010년 중~후반대만 하더라도 Tensorflow의 사용비율이 높았으나 점차 Pytorch의 사용비율이 높아지는데, 연구자들에게 친화적인 라이브러리, 언어 모델의 발전으로 <strong>Hugging Face</strong> 등장, Tensorflow와 비교하여 직관적이고 파이썬적 API 등의 이유가 있다.</p>

<p>두 라이브러리 모두 공부해본 나의 입장에서는 하나를 다룰 줄 알면 비슷한 구조로 짜여있기에 하나를 익히면 다른 라이브러리는 쉽게 익힐 수 있다고 생각한다.</p>

<p>이제 DNN 모델을 구현하기 전에 딥러닝의 학습단계가 어떻게 되는지 살펴보고 가면…</p>

<ul>
  <li>
    <p>Deep Learning의 학습단계</p>

    <blockquote>
      <p><strong>Data</strong> → <strong>Model</strong> → <strong>Output</strong> → <strong>Loss</strong> → <strong>Optimization</strong></p>
    </blockquote>
  </li>
</ul>

<p>간단하게 나누면 위와 같은 과정을 반복한다고 볼 수 있다.</p>

<p>데이터를 가지고 모델을 훈련하여 출력을 만들어내고, 실제 값과 비교하여 손실값을 계산, 역전파를 이용하여 파라미터를 최적화하여 다시 훈련하는 과정을 반복하게 된다.</p>

<hr />

<p>각각의 과정에서 Pytorch의 다음과 같은 라이브러리 밑의 요소들을 이용하여 구현하게 된다.</p>

<ul>
  <li>
    <p><strong>Data</strong> : 데이터셋에서 미니 배치 크기의 데이터 반환</p>

    <ul>
      <li>
        <p><strong><em>Dataset</em></strong> : 단일 데이터를 모델의 입력으로 사용할 수 있는 <strong>Tensor</strong>로 변환</p>
      </li>
      <li>
        <p><strong><em>DataLoader</em></strong> : 데이터셋을 미니 배치 크기의 데이터로 반환</p>
      </li>
      <li>
        <p>Pytorch에서 제공하는 Dataset, DataLoader는 가장 대중적으로 사용하는 기능들만 구현되어있기에, 세부적인 사항들은 직접 커스텀으로 구현해야 함</p>
      </li>
    </ul>
  </li>
  <li>
    <p><strong>Model</strong></p>

    <ul>
      <li>
        <p><strong>Torchvision</strong> : 이미지 분석에 특화된 모델을 제공하는 라이브러리</p>
      </li>
      <li>
        <p><strong>PyTorch Hub</strong> : CV, 음성, 생성형, NLP 등의 모델을 제공하는 라이브러리</p>
      </li>
      <li>
        <p>마찬가지로 Pytorch에 공개된 모델은 제한적이므로 프로젝트의 목표에 맞게 모델을 변형해서 사용해야함</p>
      </li>
    </ul>
  </li>
  <li>
    <p><strong>Optimizer</strong></p>

    <ol>
      <li>
        <p><strong>optimizer.zero_grad()</strong> : 이전 gradient를 0으로 설정</p>
      </li>
      <li>
        <p><strong>model(data)</strong> : 데이터를 모델을 통해 연산</p>
      </li>
      <li>
        <p><strong>loss_function(output, label)</strong> : loss 값 계산</p>
      </li>
      <li>
        <p><strong>loss.backward()</strong> : loss 값에 대한 gradient 계산</p>
      </li>
      <li>
        <p><strong>optimizer.step()</strong> : gradient를 이용하여 모델의 파라미터 업데이트</p>
      </li>
    </ol>
  </li>
  <li>
    <p><strong>Inference &amp; Evaluation</strong></p>

    <ul>
      <li>
        <p><strong>model.eval()</strong> : 모델을 평가 모드로 전환 → 특정 레이어들이 학습과 추론 과정 각각 다르게 작동해야 하기 때문</p>
      </li>
      <li>
        <p><strong>torch.no_grad()</strong> : 추론 과정에서는 gradient 계산이 필요하지 않음</p>
      </li>
      <li>
        <p>Pytorch를 이용하여 평가산식을 직접 구현 or scikit-learn을 이용하여 구현</p>
      </li>
    </ul>
  </li>
</ul>

<p>이제 위의 과정들을 직접 코딩해가면서 진행해 본다.</p>

<hr />

<h1 id="환경설정">환경설정</h1>

<p>먼저 데이터를 불러오고, 모델 정의, 평가 등을 위한 라이브러리를 불러온다.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">numpy</span> <span class="k">as</span> <span class="n">np</span>
<span class="kn">import</span> <span class="nn">matplotlib.pyplot</span> <span class="k">as</span> <span class="n">plt</span>
<span class="kn">from</span> <span class="nn">tqdm</span> <span class="kn">import</span> <span class="n">tqdm</span>

<span class="kn">import</span> <span class="nn">torch</span>
<span class="kn">import</span> <span class="nn">torch.nn</span> <span class="k">as</span> <span class="n">nn</span>
<span class="kn">import</span> <span class="nn">torch.optim</span> <span class="k">as</span> <span class="n">optim</span>
<span class="kn">from</span> <span class="nn">torch.utils.data</span> <span class="kn">import</span> <span class="n">DataLoader</span>

<span class="kn">import</span> <span class="nn">torchvision</span>
<span class="kn">import</span> <span class="nn">torchvision.transforms</span> <span class="k">as</span> <span class="n">T</span>

<span class="kn">from</span> <span class="nn">sklearn.metrics</span> <span class="kn">import</span> <span class="n">precision_score</span><span class="p">,</span> <span class="n">recall_score</span><span class="p">,</span> <span class="n">f1_score</span><span class="p">,</span> <span class="n">roc_auc_score</span>
</code></pre></div></div>

<p>또한, 일관된 훈련을 위한 시드를 고정시켜준다.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">random</span>
<span class="kn">import</span> <span class="nn">torch.backends.cudnn</span> <span class="k">as</span> <span class="n">cudnn</span>

<span class="k">def</span> <span class="nf">random_seed</span><span class="p">(</span><span class="n">seed_num</span><span class="p">)</span> <span class="p">:</span>
    <span class="n">torch</span><span class="p">.</span><span class="n">manual_seed</span><span class="p">(</span><span class="n">seed_num</span><span class="p">)</span>
    <span class="n">torch</span><span class="p">.</span><span class="n">cuda</span><span class="p">.</span><span class="n">manual_seed</span><span class="p">(</span><span class="n">seed_num</span><span class="p">)</span>
    <span class="n">torch</span><span class="p">.</span><span class="n">cuda</span><span class="p">.</span><span class="n">manual_seed_all</span><span class="p">(</span><span class="n">seed_num</span><span class="p">)</span>
    <span class="n">cudnn</span><span class="p">.</span><span class="n">benchmark</span> <span class="o">=</span> <span class="bp">False</span>
    <span class="n">cudnn</span><span class="p">.</span><span class="n">deterministic</span> <span class="o">=</span> <span class="bp">True</span>
    <span class="n">random</span><span class="p">.</span><span class="n">seed</span><span class="p">(</span><span class="n">seed_num</span><span class="p">)</span>

<span class="n">random_seed</span><span class="p">(</span><span class="mi">42</span><span class="p">)</span>
</code></pre></div></div>

<h1 id="데이터셋">데이터셋</h1>

<p>0~9 까지 손글씨로 구성된 데이터셋을 이용하여 DNN을 구현한다.</p>

<p>먼저, 데이터를 불러와 Tensor 형식으로 변환해준다.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">mnist_transform</span> <span class="o">=</span> <span class="n">T</span><span class="p">.</span><span class="n">Compose</span><span class="p">([</span><span class="n">T</span><span class="p">.</span><span class="n">ToTensor</span><span class="p">(),])</span>
</code></pre></div></div>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">download_root</span> <span class="o">=</span> <span class="s">'./MNIST_DATASET'</span>

<span class="n">train_dataset</span> <span class="o">=</span> <span class="n">torchvision</span><span class="p">.</span><span class="n">datasets</span><span class="p">.</span><span class="n">MNIST</span><span class="p">(</span><span class="n">download_root</span><span class="p">,</span> <span class="n">train</span><span class="o">=</span><span class="bp">True</span><span class="p">,</span> <span class="n">transform</span><span class="o">=</span><span class="n">mnist_transform</span><span class="p">,</span> <span class="n">download</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
<span class="n">test_dataset</span> <span class="o">=</span> <span class="n">torchvision</span><span class="p">.</span><span class="n">datasets</span><span class="p">.</span><span class="n">MNIST</span><span class="p">(</span><span class="n">download_root</span><span class="p">,</span> <span class="n">train</span><span class="o">=</span><span class="bp">False</span><span class="p">,</span> <span class="n">transform</span><span class="o">=</span><span class="n">mnist_transform</span><span class="p">,</span> <span class="n">download</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
</code></pre></div></div>

<p>데이터셋의 모양은 <strong>28*28의 이미지</strong>로 구성되어 있고, 채널이 1개이기에 <strong>gray-scale</strong> 이미지임을 알 수 있다.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">for</span> <span class="n">image</span><span class="p">,</span> <span class="n">label</span> <span class="ow">in</span> <span class="n">train_dataset</span> <span class="p">:</span>
    <span class="k">print</span><span class="p">(</span><span class="n">image</span><span class="p">.</span><span class="n">shape</span><span class="p">,</span> <span class="n">label</span><span class="p">)</span>
    <span class="k">break</span>
</code></pre></div></div>

<pre>
torch.Size([1, 28, 28]) 5
</pre>
<p>전체 데이터셋에서 훈련셋과 검증셋을 나눠준다.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">total_size</span> <span class="o">=</span> <span class="nb">len</span><span class="p">(</span><span class="n">train_dataset</span><span class="p">)</span>
<span class="n">train_num</span><span class="p">,</span> <span class="n">valid_num</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">total_size</span> <span class="o">*</span> <span class="mf">0.8</span><span class="p">),</span> <span class="nb">int</span><span class="p">(</span><span class="n">total_size</span> <span class="o">*</span> <span class="mf">0.2</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="s">"Train data size: "</span><span class="p">,</span> <span class="n">train_num</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="s">"Validation data size: "</span><span class="p">,</span> <span class="n">valid_num</span><span class="p">)</span>
<span class="n">train_dataset</span><span class="p">,</span> <span class="n">valid_dataset</span> <span class="o">=</span> <span class="n">torch</span><span class="p">.</span><span class="n">utils</span><span class="p">.</span><span class="n">data</span><span class="p">.</span><span class="n">random_split</span><span class="p">(</span><span class="n">train_dataset</span><span class="p">,</span> <span class="p">[</span><span class="n">train_num</span><span class="p">,</span> <span class="n">valid_num</span><span class="p">])</span>
</code></pre></div></div>

<pre>
Train data size:  48000
Validation data size:  12000
</pre>
<hr />

<h1 id="dataloader-정의">DataLoader 정의</h1>

<p>데이터셋을 텐서 형태로 변환해주었으므로, 지정한 미니 배치 단위로 데이터들을 묶어준다.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">batch_szie</span> <span class="o">=</span> <span class="mi">32</span>

<span class="n">train_dataloader</span> <span class="o">=</span> <span class="n">DataLoader</span><span class="p">(</span><span class="n">train_dataset</span><span class="p">,</span> <span class="n">batch_size</span><span class="o">=</span><span class="n">batch_szie</span><span class="p">,</span> <span class="n">shuffle</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
<span class="n">valid_dataloader</span> <span class="o">=</span> <span class="n">DataLoader</span><span class="p">(</span><span class="n">valid_dataset</span><span class="p">,</span> <span class="n">batch_size</span><span class="o">=</span><span class="n">batch_szie</span><span class="p">,</span> <span class="n">shuffle</span><span class="o">=</span><span class="bp">False</span><span class="p">)</span>
<span class="n">test_dataloader</span> <span class="o">=</span> <span class="n">DataLoader</span><span class="p">(</span><span class="n">test_dataset</span><span class="p">,</span> <span class="n">batch_size</span><span class="o">=</span><span class="n">batch_szie</span><span class="p">,</span> <span class="n">shuffle</span><span class="o">=</span><span class="bp">False</span><span class="p">)</span>
</code></pre></div></div>

<p>위에서 지정하였던 배치 크기가 32이므로 확인해보면 하나의 배치에 32개의 손글씨 데이터가 들어가게 된다.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">for</span> <span class="n">images</span><span class="p">,</span> <span class="n">labels</span> <span class="ow">in</span> <span class="n">train_dataloader</span> <span class="p">:</span>
    <span class="k">print</span><span class="p">(</span><span class="n">images</span><span class="p">.</span><span class="n">shape</span><span class="p">,</span> <span class="n">labels</span><span class="p">.</span><span class="n">shape</span><span class="p">)</span>
    <span class="k">break</span>
</code></pre></div></div>

<pre>
torch.Size([32, 1, 28, 28]) torch.Size([32])
</pre>
<hr />

<h1 id="모델">모델</h1>

<p>다음으로는 Custom Model을 정의한다.</p>

<p>Fully connected layer를 선언하고, for문을 이용하여 은닉층의 개수만큼 반복시켜 다음과 같은 층을 정의한다.</p>

<p>또한, 이를 순전파로 연결시켜주어야 하므로 <strong>foward()</strong> 메서드 부분에서 이를 정의해준다.</p>

<p>그러면 다음과 같은 전체 구조를 가지는 모델이 완성된다.</p>

<p><img src="https://github.com/lIllIlIIIll/Leeinformation.github.io/blob/master/_posts/image/Bootcamp/DNN_구조.jpg?raw=true" alt="DNN 구조" /></p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">DNN</span><span class="p">(</span><span class="n">nn</span><span class="p">.</span><span class="n">Module</span><span class="p">)</span> <span class="p">:</span>
    <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">hidden_dims</span><span class="p">,</span> <span class="n">num_classes</span><span class="p">,</span> <span class="n">dropout_ratio</span><span class="p">,</span> <span class="n">apply_batchnorm</span><span class="p">,</span> <span class="n">apply_dropout</span><span class="p">,</span> <span class="n">apply_activation</span><span class="p">,</span> <span class="n">set_super</span><span class="p">)</span> <span class="p">:</span>
        <span class="k">if</span> <span class="n">set_super</span> <span class="p">:</span>
            <span class="nb">super</span><span class="p">().</span><span class="n">__init__</span><span class="p">()</span>

        <span class="bp">self</span><span class="p">.</span><span class="n">hidden_dims</span> <span class="o">=</span> <span class="n">hidden_dims</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">layers</span> <span class="o">=</span> <span class="n">nn</span><span class="p">.</span><span class="n">ModuleList</span><span class="p">()</span>

        <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="bp">self</span><span class="p">.</span><span class="n">hidden_dims</span><span class="p">)</span><span class="o">-</span><span class="mi">1</span><span class="p">)</span> <span class="p">:</span>
            <span class="bp">self</span><span class="p">.</span><span class="n">layers</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">nn</span><span class="p">.</span><span class="n">Linear</span><span class="p">(</span><span class="bp">self</span><span class="p">.</span><span class="n">hidden_dims</span><span class="p">[</span><span class="n">i</span><span class="p">],</span> <span class="bp">self</span><span class="p">.</span><span class="n">hidden_dims</span><span class="p">[</span><span class="n">i</span><span class="o">+</span><span class="mi">1</span><span class="p">]))</span>

            <span class="k">if</span> <span class="n">apply_batchnorm</span> <span class="p">:</span>
                <span class="bp">self</span><span class="p">.</span><span class="n">layers</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">nn</span><span class="p">.</span><span class="n">BatchNorm1d</span><span class="p">(</span><span class="bp">self</span><span class="p">.</span><span class="n">hidden_dims</span><span class="p">[</span><span class="n">i</span><span class="o">+</span><span class="mi">1</span><span class="p">]))</span>

            <span class="k">if</span> <span class="n">apply_dropout</span> <span class="p">:</span>
                <span class="bp">self</span><span class="p">.</span><span class="n">layers</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">nn</span><span class="p">.</span><span class="n">Dropout</span><span class="p">(</span><span class="n">dropout_ratio</span><span class="p">))</span>

            <span class="k">if</span> <span class="n">apply_activation</span> <span class="p">:</span>
                <span class="bp">self</span><span class="p">.</span><span class="n">layers</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">nn</span><span class="p">.</span><span class="n">ReLU</span><span class="p">())</span>

        <span class="bp">self</span><span class="p">.</span><span class="n">classifier</span> <span class="o">=</span> <span class="n">nn</span><span class="p">.</span><span class="n">Linear</span><span class="p">(</span><span class="bp">self</span><span class="p">.</span><span class="n">hidden_dims</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">],</span> <span class="n">num_classes</span><span class="p">)</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">softmax</span> <span class="o">=</span> <span class="n">nn</span><span class="p">.</span><span class="n">Softmax</span><span class="p">(</span><span class="n">dim</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span>

    <span class="k">def</span> <span class="nf">forward</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">x</span><span class="p">)</span> <span class="p">:</span>
        <span class="n">x</span> <span class="o">=</span> <span class="n">x</span><span class="p">.</span><span class="n">view</span><span class="p">(</span><span class="n">x</span><span class="p">.</span><span class="n">size</span><span class="p">(</span><span class="mi">0</span><span class="p">),</span> <span class="o">-</span><span class="mi">1</span><span class="p">)</span>

        <span class="k">for</span> <span class="n">layer</span> <span class="ow">in</span> <span class="bp">self</span><span class="p">.</span><span class="n">layers</span> <span class="p">:</span>
            <span class="n">x</span> <span class="o">=</span> <span class="n">layer</span><span class="p">(</span><span class="n">x</span><span class="p">)</span>

        <span class="n">output</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">classifier</span><span class="p">(</span><span class="n">x</span><span class="p">)</span>
        <span class="n">output</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">softmax</span><span class="p">(</span><span class="n">output</span><span class="p">)</span>

        <span class="k">return</span> <span class="n">output</span>
</code></pre></div></div>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">hidden_dim</span> <span class="o">=</span> <span class="mi">128</span>
<span class="n">hidden_dims</span> <span class="o">=</span> <span class="p">[</span><span class="mi">784</span><span class="p">,</span> <span class="n">hidden_dim</span><span class="o">*</span><span class="mi">4</span><span class="p">,</span> <span class="n">hidden_dim</span><span class="o">*</span><span class="mi">2</span><span class="p">,</span> <span class="n">hidden_dim</span><span class="p">]</span>
<span class="n">model</span> <span class="o">=</span> <span class="n">DNN</span><span class="p">(</span><span class="n">hidden_dims</span><span class="o">=</span><span class="n">hidden_dims</span><span class="p">,</span> <span class="n">num_classes</span><span class="o">=</span><span class="mi">10</span><span class="p">,</span> <span class="n">dropout_ratio</span><span class="o">=</span><span class="mf">0.2</span><span class="p">,</span> <span class="n">apply_batchnorm</span><span class="o">=</span><span class="bp">True</span><span class="p">,</span> <span class="n">apply_dropout</span><span class="o">=</span><span class="bp">True</span><span class="p">,</span> <span class="n">apply_activation</span><span class="o">=</span><span class="bp">True</span><span class="p">,</span> <span class="n">set_super</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
<span class="n">output</span> <span class="o">=</span> <span class="n">model</span><span class="p">(</span><span class="n">torch</span><span class="p">.</span><span class="n">randn</span><span class="p">((</span><span class="mi">32</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">28</span><span class="p">,</span> <span class="mi">28</span><span class="p">)))</span>
</code></pre></div></div>

<hr />

<h2 id="파라미터-초기화">파라미터 초기화</h2>

<p>모델 훈련을 위한 가중치를 초기화한다.</p>

<p>여러가지 방법들이 존재하며, 아래의 사이트를 참고하여 상황에 맞는 가중치를 사용할 수 있다.</p>

<blockquote>
  <p><a href="https://docs.pytorch.org/docs/stable/nn.init.html">PyTorch 가중치</a></p>
</blockquote>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">weight_initialization</span><span class="p">(</span><span class="n">model</span><span class="p">,</span> <span class="n">weight_init_method</span><span class="p">)</span> <span class="p">:</span>
    <span class="k">for</span> <span class="n">m</span> <span class="ow">in</span> <span class="n">model</span><span class="p">.</span><span class="n">modules</span><span class="p">()</span> <span class="p">:</span>
        <span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">m</span><span class="p">,</span> <span class="n">nn</span><span class="p">.</span><span class="n">Linear</span><span class="p">)</span> <span class="p">:</span>
            <span class="k">if</span> <span class="n">weight_init_method</span> <span class="o">==</span> <span class="s">'gaussian'</span> <span class="p">:</span>
                <span class="n">nn</span><span class="p">.</span><span class="n">init</span><span class="p">.</span><span class="n">normal_</span><span class="p">(</span><span class="n">m</span><span class="p">.</span><span class="n">weight</span><span class="p">)</span>
            <span class="k">elif</span> <span class="n">weight_init_method</span> <span class="o">==</span> <span class="s">'xavier'</span> <span class="p">:</span>
                <span class="n">nn</span><span class="p">.</span><span class="n">init</span><span class="p">.</span><span class="n">xavier_normal_</span><span class="p">(</span><span class="n">m</span><span class="p">.</span><span class="n">weight</span><span class="p">)</span>
            <span class="k">elif</span> <span class="n">weight_init_method</span> <span class="o">==</span> <span class="s">'kaiming'</span> <span class="p">:</span>
                <span class="n">nn</span><span class="p">.</span><span class="n">init</span><span class="p">.</span><span class="n">kaiming_normal_</span><span class="p">(</span><span class="n">m</span><span class="p">.</span><span class="n">weight</span><span class="p">)</span>
            <span class="k">elif</span> <span class="n">weight_init_method</span> <span class="o">==</span> <span class="s">'zeros'</span> <span class="p">:</span>
                <span class="n">nn</span><span class="p">.</span><span class="n">init</span><span class="p">.</span><span class="n">zeros_</span><span class="p">(</span><span class="n">m</span><span class="p">.</span><span class="n">weight</span><span class="p">)</span>

            <span class="n">nn</span><span class="p">.</span><span class="n">init</span><span class="p">.</span><span class="n">zeros_</span><span class="p">(</span><span class="n">m</span><span class="p">.</span><span class="n">bias</span><span class="p">)</span>
    
    <span class="k">return</span> <span class="n">model</span>
</code></pre></div></div>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">init_method</span> <span class="o">=</span> <span class="s">'zeros'</span>
<span class="n">model</span> <span class="o">=</span> <span class="n">weight_initialization</span><span class="p">(</span><span class="n">model</span><span class="p">,</span> <span class="n">init_method</span><span class="p">)</span>

<span class="k">for</span> <span class="n">m</span> <span class="ow">in</span> <span class="n">model</span><span class="p">.</span><span class="n">modules</span><span class="p">()</span> <span class="p">:</span>
    <span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">m</span><span class="p">,</span> <span class="n">nn</span><span class="p">.</span><span class="n">Linear</span><span class="p">)</span> <span class="p">:</span>
        <span class="k">print</span><span class="p">(</span><span class="n">m</span><span class="p">.</span><span class="n">weight</span><span class="p">.</span><span class="n">data</span><span class="p">)</span>
        <span class="k">break</span>
</code></pre></div></div>

<pre>
tensor([[0., 0., 0.,  ..., 0., 0., 0.],
        [0., 0., 0.,  ..., 0., 0., 0.],
        [0., 0., 0.,  ..., 0., 0., 0.],
        ...,
        [0., 0., 0.,  ..., 0., 0., 0.],
        [0., 0., 0.,  ..., 0., 0., 0.],
        [0., 0., 0.,  ..., 0., 0., 0.]])
</pre>
<hr />

<h2 id="최종-모델">최종 모델</h2>

<p>앞서 정의했던 순전파를 진행하는 모델과, 가중치를 초기화하는 메서드를 합쳐 하나의 클래스로 정의한다.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">DNN</span><span class="p">(</span><span class="n">nn</span><span class="p">.</span><span class="n">Module</span><span class="p">)</span> <span class="p">:</span>
    <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">hidden_dims</span><span class="p">,</span> <span class="n">num_classes</span><span class="p">,</span> <span class="n">dropout_ratio</span><span class="p">,</span> <span class="n">apply_batchnorm</span><span class="p">,</span> <span class="n">apply_dropout</span><span class="p">,</span> <span class="n">apply_activation</span><span class="p">,</span> <span class="n">set_super</span><span class="p">)</span> <span class="p">:</span>
        <span class="k">if</span> <span class="n">set_super</span> <span class="p">:</span>
            <span class="nb">super</span><span class="p">().</span><span class="n">__init__</span><span class="p">()</span>

        <span class="bp">self</span><span class="p">.</span><span class="n">hidden_dims</span> <span class="o">=</span> <span class="n">hidden_dims</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">layers</span> <span class="o">=</span> <span class="n">nn</span><span class="p">.</span><span class="n">ModuleList</span><span class="p">()</span>

        <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="bp">self</span><span class="p">.</span><span class="n">hidden_dims</span><span class="p">)</span><span class="o">-</span><span class="mi">1</span><span class="p">)</span> <span class="p">:</span>
            <span class="bp">self</span><span class="p">.</span><span class="n">layers</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">nn</span><span class="p">.</span><span class="n">Linear</span><span class="p">(</span><span class="bp">self</span><span class="p">.</span><span class="n">hidden_dims</span><span class="p">[</span><span class="n">i</span><span class="p">],</span> <span class="bp">self</span><span class="p">.</span><span class="n">hidden_dims</span><span class="p">[</span><span class="n">i</span><span class="o">+</span><span class="mi">1</span><span class="p">]))</span>

            <span class="k">if</span> <span class="n">apply_batchnorm</span> <span class="p">:</span>
                <span class="bp">self</span><span class="p">.</span><span class="n">layers</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">nn</span><span class="p">.</span><span class="n">BatchNorm1d</span><span class="p">(</span><span class="bp">self</span><span class="p">.</span><span class="n">hidden_dims</span><span class="p">[</span><span class="n">i</span><span class="o">+</span><span class="mi">1</span><span class="p">]))</span>

            <span class="k">if</span> <span class="n">apply_dropout</span> <span class="p">:</span>
                <span class="bp">self</span><span class="p">.</span><span class="n">layers</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">nn</span><span class="p">.</span><span class="n">Dropout</span><span class="p">(</span><span class="n">dropout_ratio</span><span class="p">))</span>

            <span class="k">if</span> <span class="n">apply_activation</span> <span class="p">:</span>
                <span class="bp">self</span><span class="p">.</span><span class="n">layers</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">nn</span><span class="p">.</span><span class="n">ReLU</span><span class="p">())</span>

        <span class="bp">self</span><span class="p">.</span><span class="n">classifier</span> <span class="o">=</span> <span class="n">nn</span><span class="p">.</span><span class="n">Linear</span><span class="p">(</span><span class="bp">self</span><span class="p">.</span><span class="n">hidden_dims</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">],</span> <span class="n">num_classes</span><span class="p">)</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">softmax</span> <span class="o">=</span> <span class="n">nn</span><span class="p">.</span><span class="n">Softmax</span><span class="p">(</span><span class="n">dim</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span>

    <span class="k">def</span> <span class="nf">forward</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">x</span><span class="p">)</span> <span class="p">:</span>
        <span class="n">x</span> <span class="o">=</span> <span class="n">x</span><span class="p">.</span><span class="n">view</span><span class="p">(</span><span class="n">x</span><span class="p">.</span><span class="n">size</span><span class="p">(</span><span class="mi">0</span><span class="p">),</span> <span class="o">-</span><span class="mi">1</span><span class="p">)</span>

        <span class="k">for</span> <span class="n">layer</span> <span class="ow">in</span> <span class="bp">self</span><span class="p">.</span><span class="n">layers</span> <span class="p">:</span>
            <span class="n">x</span> <span class="o">=</span> <span class="n">layer</span><span class="p">(</span><span class="n">x</span><span class="p">)</span>

        <span class="n">output</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">classifier</span><span class="p">(</span><span class="n">x</span><span class="p">)</span>
        <span class="n">output</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">softmax</span><span class="p">(</span><span class="n">output</span><span class="p">)</span>

        <span class="k">return</span> <span class="n">output</span>

    <span class="k">def</span> <span class="nf">weight_initialization</span><span class="p">(</span><span class="n">model</span><span class="p">,</span> <span class="n">weight_init_method</span><span class="p">)</span> <span class="p">:</span>
        <span class="k">for</span> <span class="n">m</span> <span class="ow">in</span> <span class="n">model</span><span class="p">.</span><span class="n">modules</span><span class="p">()</span> <span class="p">:</span>
            <span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">m</span><span class="p">,</span> <span class="n">nn</span><span class="p">.</span><span class="n">Linear</span><span class="p">)</span> <span class="p">:</span>
                <span class="k">if</span> <span class="n">weight_init_method</span> <span class="o">==</span> <span class="s">'gaussian'</span> <span class="p">:</span>
                    <span class="n">nn</span><span class="p">.</span><span class="n">init</span><span class="p">.</span><span class="n">normal_</span><span class="p">(</span><span class="n">m</span><span class="p">.</span><span class="n">weight</span><span class="p">)</span>
                <span class="k">elif</span> <span class="n">weight_init_method</span> <span class="o">==</span> <span class="s">'xavier'</span> <span class="p">:</span>
                    <span class="n">nn</span><span class="p">.</span><span class="n">init</span><span class="p">.</span><span class="n">xavier_normal_</span><span class="p">(</span><span class="n">m</span><span class="p">.</span><span class="n">weight</span><span class="p">)</span>
                <span class="k">elif</span> <span class="n">weight_init_method</span> <span class="o">==</span> <span class="s">'kaiming'</span> <span class="p">:</span>
                    <span class="n">nn</span><span class="p">.</span><span class="n">init</span><span class="p">.</span><span class="n">kaiming_normal_</span><span class="p">(</span><span class="n">m</span><span class="p">.</span><span class="n">weight</span><span class="p">)</span>
                <span class="k">elif</span> <span class="n">weight_init_method</span> <span class="o">==</span> <span class="s">'zeros'</span> <span class="p">:</span>
                    <span class="n">nn</span><span class="p">.</span><span class="n">init</span><span class="p">.</span><span class="n">zeros_</span><span class="p">(</span><span class="n">m</span><span class="p">.</span><span class="n">weight</span><span class="p">)</span>

                <span class="n">nn</span><span class="p">.</span><span class="n">init</span><span class="p">.</span><span class="n">zeros_</span><span class="p">(</span><span class="n">m</span><span class="p">.</span><span class="n">bias</span><span class="p">)</span>
        
        <span class="k">return</span> <span class="n">model</span>

    <span class="k">def</span> <span class="nf">count_parameters</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="p">:</span>
        <span class="k">return</span> <span class="nb">sum</span><span class="p">(</span><span class="n">p</span><span class="p">.</span><span class="n">numel</span><span class="p">()</span> <span class="k">for</span> <span class="n">p</span> <span class="ow">in</span> <span class="bp">self</span><span class="p">.</span><span class="n">parameters</span><span class="p">())</span>
</code></pre></div></div>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">model</span> <span class="o">=</span> <span class="n">DNN</span><span class="p">(</span><span class="n">hidden_dims</span><span class="o">=</span><span class="n">hidden_dims</span><span class="p">,</span> <span class="n">num_classes</span><span class="o">=</span><span class="mi">10</span><span class="p">,</span> <span class="n">dropout_ratio</span><span class="o">=</span><span class="mf">0.2</span><span class="p">,</span> <span class="n">apply_batchnorm</span><span class="o">=</span><span class="bp">True</span><span class="p">,</span> <span class="n">apply_dropout</span><span class="o">=</span><span class="bp">True</span><span class="p">,</span> <span class="n">apply_activation</span><span class="o">=</span><span class="bp">True</span><span class="p">,</span> <span class="n">set_super</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
<span class="n">init_method</span> <span class="o">=</span> <span class="s">"gaussian"</span>
<span class="n">model</span><span class="p">.</span><span class="n">weight_initialization</span><span class="p">(</span><span class="n">init_method</span><span class="p">)</span>
</code></pre></div></div>

<pre>
DNN(
  (layers): ModuleList(
    (0): Linear(in_features=784, out_features=128, bias=True)
    (1): BatchNorm1d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    (2): Dropout(p=0.2, inplace=False)
    (3): ReLU()
    (4): Linear(in_features=128, out_features=128, bias=True)
    (5): BatchNorm1d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    (6): Dropout(p=0.2, inplace=False)
    (7): ReLU()
    (8): Linear(in_features=128, out_features=128, bias=True)
    (9): BatchNorm1d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    (10): Dropout(p=0.2, inplace=False)
    (11): ReLU()
  )
  (classifier): Linear(in_features=128, out_features=10, bias=True)
  (softmax): Softmax(dim=1)
)
</pre>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">model</span><span class="p">.</span><span class="n">count_parameters</span><span class="p">()</span>
</code></pre></div></div>

<pre>
135562
</pre>
<hr />

<h1 id="손실-함수-및-최적화-알고리즘">손실 함수 및 최적화 알고리즘</h1>

<p>모델 정의가 되었으므로, 모델이 잘 훈련되는지 확인하기 위한 손실 함수와 이를 이용한 최적화 알고리즘을 정의한다.</p>

<p>먼저, 각 상황에 맞는 손실 함수를 찾아야 한다.</p>

<blockquote>
  <p><a href="https://docs.pytorch.org/docs/stable/nn.html#loss-functions">손실 함수</a></p>
</blockquote>

<p>현재 실습은 손글씨 데이터를 <strong>label</strong>에 맞게 분류하는 것이므로 NLLLoss()를 사용한다.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">criterion</span> <span class="o">=</span> <span class="n">nn</span><span class="p">.</span><span class="n">NLLLoss</span><span class="p">()</span>
</code></pre></div></div>

<p>최적화(optimization) 또한, 일반적으로는 <strong>Adam</strong>을 사용하지만, 상황에 따라 바꿀 수 있다.</p>

<blockquote>
  <p><a href="https://docs.pytorch.org/docs/stable/optim.html#algorithms">Optimizer</a></p>
</blockquote>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">lr</span> <span class="o">=</span> <span class="mf">0.001</span>
<span class="n">hidden_dim</span> <span class="o">=</span> <span class="mi">128</span>
<span class="n">hidden_dims</span> <span class="o">=</span> <span class="p">[</span><span class="mi">784</span><span class="p">,</span> <span class="n">hidden_dim</span><span class="p">,</span> <span class="n">hidden_dim</span><span class="p">,</span> <span class="n">hidden_dim</span><span class="p">]</span>
<span class="n">model</span> <span class="o">=</span> <span class="n">DNN</span><span class="p">(</span><span class="n">hidden_dims</span><span class="o">=</span><span class="n">hidden_dims</span><span class="p">,</span> <span class="n">num_classes</span><span class="o">=</span><span class="mi">10</span><span class="p">,</span> <span class="n">dropout_ratio</span><span class="o">=</span><span class="mf">0.2</span><span class="p">,</span> <span class="n">apply_batchnorm</span><span class="o">=</span><span class="bp">True</span><span class="p">,</span> <span class="n">apply_dropout</span><span class="o">=</span><span class="bp">True</span><span class="p">,</span> <span class="n">apply_activation</span><span class="o">=</span><span class="bp">True</span><span class="p">,</span> <span class="n">set_super</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
<span class="n">optimizer</span> <span class="o">=</span> <span class="n">optim</span><span class="p">.</span><span class="n">Adam</span><span class="p">(</span><span class="n">model</span><span class="p">.</span><span class="n">parameters</span><span class="p">(),</span> <span class="n">lr</span><span class="o">=</span><span class="n">lr</span><span class="p">)</span>
</code></pre></div></div>

<p>이제 학습을 진행하는 코드를 작성한다.</p>

<p><strong>training()</strong></p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>- GPU를 이용하여 학습

- 순전파

- 역전파 및 가중치 업데이트

- 손실값 및 정확도 계산



- 각 epoch마다의 학습 결과를 출력한다.
</code></pre></div></div>

<p><strong>evaluation()</strong></p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>- GPU를 이용하여 평가

- 순전파

- 손실값 및 정확도 계산
</code></pre></div></div>

<p><strong>training_loop()</strong></p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>- Train 데이터로 훈련을 진행하고, valid 데이터로 훈련에 대한 검증

- valid 데이터에 대한 정확도가 이전보다 높다면 업데이트

- 손실값을 기준으로 이전보다 작아진다면 EarlyStopping
</code></pre></div></div>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">training</span><span class="p">(</span><span class="n">model</span><span class="p">,</span> <span class="n">dataloader</span><span class="p">,</span> <span class="n">train_dataset</span><span class="p">,</span> <span class="n">criterion</span><span class="p">,</span> <span class="n">optimizer</span><span class="p">,</span> <span class="n">device</span><span class="p">,</span> <span class="n">epoch</span><span class="p">,</span> <span class="n">num_epochs</span><span class="p">)</span> <span class="p">:</span>
    <span class="n">model</span><span class="p">.</span><span class="n">train</span><span class="p">()</span>
    <span class="n">train_loss</span> <span class="o">=</span> <span class="mf">0.0</span>
    <span class="n">train_accuracy</span> <span class="o">=</span> <span class="mi">0</span>

    <span class="n">tbar</span> <span class="o">=</span> <span class="n">tqdm</span><span class="p">(</span><span class="n">dataloader</span><span class="p">)</span>
    <span class="k">for</span> <span class="n">images</span><span class="p">,</span> <span class="n">labels</span> <span class="ow">in</span> <span class="n">tbar</span> <span class="p">:</span>
        <span class="n">images</span> <span class="o">=</span> <span class="n">images</span><span class="p">.</span><span class="n">to</span><span class="p">(</span><span class="n">device</span><span class="p">)</span>
        <span class="n">labels</span> <span class="o">=</span> <span class="n">labels</span><span class="p">.</span><span class="n">to</span><span class="p">(</span><span class="n">device</span><span class="p">)</span>

        <span class="n">outputs</span> <span class="o">=</span> <span class="n">model</span><span class="p">(</span><span class="n">images</span><span class="p">)</span>
        <span class="n">loss</span> <span class="o">=</span> <span class="n">criterion</span><span class="p">(</span><span class="n">outputs</span><span class="p">,</span> <span class="n">labels</span><span class="p">)</span>

        <span class="n">optimizer</span><span class="p">.</span><span class="n">zero_grad</span><span class="p">()</span>
        <span class="n">loss</span><span class="p">.</span><span class="n">backward</span><span class="p">()</span>
        <span class="n">optimizer</span><span class="p">.</span><span class="n">step</span><span class="p">()</span>

        <span class="n">train_loss</span> <span class="o">+=</span> <span class="n">loss</span><span class="p">.</span><span class="n">item</span><span class="p">()</span>
        <span class="n">_</span><span class="p">,</span> <span class="n">predicted</span> <span class="o">=</span> <span class="n">torch</span><span class="p">.</span><span class="nb">max</span><span class="p">(</span><span class="n">outputs</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
        <span class="n">train_accuracy</span> <span class="o">+=</span> <span class="p">(</span><span class="n">predicted</span> <span class="o">==</span> <span class="n">labels</span><span class="p">).</span><span class="nb">sum</span><span class="p">().</span><span class="n">item</span><span class="p">()</span>

        <span class="n">tbar</span><span class="p">.</span><span class="n">set_description</span><span class="p">(</span><span class="sa">f</span><span class="s">'Epoch </span><span class="si">{</span><span class="n">epoch</span><span class="o">+</span><span class="mi">1</span><span class="si">}</span><span class="s">/</span><span class="si">{</span><span class="n">num_epochs</span><span class="si">}</span><span class="s">, Train_Loss: </span><span class="si">{</span><span class="n">train_loss</span><span class="si">:</span><span class="p">.</span><span class="mi">4</span><span class="n">f</span><span class="si">}</span><span class="s">'</span><span class="p">)</span>

    <span class="n">train_loss</span> <span class="o">/=</span> <span class="nb">len</span><span class="p">(</span><span class="n">dataloader</span><span class="p">)</span>
    <span class="n">train_accuracy</span> <span class="o">/=</span> <span class="nb">len</span><span class="p">(</span><span class="n">train_dataset</span><span class="p">)</span>

    <span class="k">return</span> <span class="n">model</span><span class="p">,</span> <span class="n">train_loss</span><span class="p">,</span> <span class="n">train_accuracy</span>

<span class="k">def</span> <span class="nf">evaluation</span><span class="p">(</span><span class="n">model</span><span class="p">,</span> <span class="n">dataloader</span><span class="p">,</span> <span class="n">valid_dataset</span><span class="p">,</span> <span class="n">criterion</span><span class="p">,</span> <span class="n">device</span><span class="p">,</span> <span class="n">epoch</span><span class="p">,</span> <span class="n">num_epochs</span><span class="p">)</span> <span class="p">:</span>
    <span class="n">model</span><span class="p">.</span><span class="nb">eval</span><span class="p">()</span>
    <span class="n">valid_loss</span> <span class="o">=</span> <span class="mf">0.0</span>
    <span class="n">valid_accuracy</span> <span class="o">=</span> <span class="mi">0</span>

    <span class="k">with</span> <span class="n">torch</span><span class="p">.</span><span class="n">no_grad</span><span class="p">()</span> <span class="p">:</span>
        <span class="n">tbar</span> <span class="o">=</span> <span class="n">tqdm</span><span class="p">(</span><span class="n">dataloader</span><span class="p">)</span>
        <span class="k">for</span> <span class="n">images</span><span class="p">,</span> <span class="n">labels</span> <span class="ow">in</span> <span class="n">tbar</span> <span class="p">:</span>
            <span class="n">images</span> <span class="o">=</span> <span class="n">images</span><span class="p">.</span><span class="n">to</span><span class="p">(</span><span class="n">device</span><span class="p">)</span>
            <span class="n">labels</span> <span class="o">=</span> <span class="n">labels</span><span class="p">.</span><span class="n">to</span><span class="p">(</span><span class="n">device</span><span class="p">)</span>

            <span class="n">outputs</span> <span class="o">=</span> <span class="n">model</span><span class="p">(</span><span class="n">images</span><span class="p">)</span>
            <span class="n">loss</span> <span class="o">=</span> <span class="n">criterion</span><span class="p">(</span><span class="n">outputs</span><span class="p">,</span> <span class="n">labels</span><span class="p">)</span>

            <span class="n">valid_loss</span> <span class="o">+=</span> <span class="n">loss</span><span class="p">.</span><span class="n">item</span><span class="p">()</span>
            <span class="n">_</span><span class="p">,</span> <span class="n">predicted</span> <span class="o">=</span> <span class="n">torch</span><span class="p">.</span><span class="nb">max</span><span class="p">(</span><span class="n">outputs</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
            <span class="n">valid_accuracy</span> <span class="o">+=</span> <span class="p">(</span><span class="n">predicted</span> <span class="o">==</span> <span class="n">labels</span><span class="p">).</span><span class="nb">sum</span><span class="p">().</span><span class="n">item</span><span class="p">()</span>

            <span class="n">tbar</span><span class="p">.</span><span class="n">set_description</span><span class="p">(</span><span class="sa">f</span><span class="s">'Epoch </span><span class="si">{</span><span class="n">epoch</span><span class="o">+</span><span class="mi">1</span><span class="si">}</span><span class="s">/</span><span class="si">{</span><span class="n">num_epochs</span><span class="si">}</span><span class="s">, Valid_Loss: </span><span class="si">{</span><span class="n">valid_loss</span><span class="si">:</span><span class="p">.</span><span class="mi">4</span><span class="n">f</span><span class="si">}</span><span class="s">'</span><span class="p">)</span>

    <span class="n">valid_loss</span> <span class="o">/=</span> <span class="nb">len</span><span class="p">(</span><span class="n">dataloader</span><span class="p">)</span>
    <span class="n">valid_accuracy</span> <span class="o">/=</span> <span class="nb">len</span><span class="p">(</span><span class="n">valid_dataset</span><span class="p">)</span>

    <span class="k">return</span> <span class="n">model</span><span class="p">,</span><span class="n">valid_loss</span><span class="p">,</span> <span class="n">valid_accuracy</span>

<span class="k">def</span> <span class="nf">training_loop</span><span class="p">(</span><span class="n">model</span><span class="p">,</span> <span class="n">train_dataloader</span><span class="p">,</span> <span class="n">valid_dataloader</span><span class="p">,</span> <span class="n">criterion</span><span class="p">,</span> <span class="n">optimizer</span><span class="p">,</span> <span class="n">device</span><span class="p">,</span> <span class="n">num_epochs</span><span class="p">,</span> <span class="n">patience</span><span class="p">,</span> <span class="n">model_name</span><span class="p">)</span> <span class="p">:</span>
    <span class="n">best_valid_loss</span> <span class="o">=</span> <span class="nb">float</span><span class="p">(</span><span class="s">'inf'</span><span class="p">)</span>
    <span class="n">early_stop_counter</span> <span class="o">=</span> <span class="mi">0</span>
    <span class="n">valid_max_accuracy</span> <span class="o">=</span> <span class="o">-</span><span class="mi">1</span>

    <span class="k">for</span> <span class="n">epoch</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">num_epochs</span><span class="p">)</span> <span class="p">:</span>
        <span class="n">model</span><span class="p">,</span> <span class="n">train_loss</span><span class="p">,</span> <span class="n">train_accuracy</span> <span class="o">=</span> <span class="n">training</span><span class="p">(</span><span class="n">model</span><span class="p">,</span> <span class="n">train_dataloader</span><span class="p">,</span> <span class="n">train_dataset</span><span class="p">,</span> <span class="n">criterion</span><span class="p">,</span> <span class="n">optimizer</span><span class="p">,</span> <span class="n">device</span><span class="p">,</span> <span class="n">epoch</span><span class="p">,</span> <span class="n">num_epochs</span><span class="p">)</span>
        <span class="n">model</span><span class="p">,</span> <span class="n">valid_loss</span><span class="p">,</span> <span class="n">valid_accuracy</span> <span class="o">=</span> <span class="n">evaluation</span><span class="p">(</span><span class="n">model</span><span class="p">,</span> <span class="n">valid_dataloader</span><span class="p">,</span> <span class="n">valid_dataset</span><span class="p">,</span> <span class="n">criterion</span><span class="p">,</span> <span class="n">device</span><span class="p">,</span> <span class="n">epoch</span><span class="p">,</span> <span class="n">num_epochs</span><span class="p">)</span>
        
        <span class="k">if</span> <span class="n">valid_accuracy</span> <span class="o">&gt;</span> <span class="n">valid_max_accuracy</span> <span class="p">:</span>
            <span class="n">valid_max_accuracy</span> <span class="o">=</span> <span class="n">valid_accuracy</span>

        <span class="k">if</span> <span class="n">valid_loss</span> <span class="o">&lt;</span> <span class="n">best_valid_loss</span> <span class="p">:</span>
            <span class="n">best_valid_loss</span> <span class="o">=</span> <span class="n">valid_loss</span>
            <span class="n">torch</span><span class="p">.</span><span class="n">save</span><span class="p">(</span><span class="n">model</span><span class="p">.</span><span class="n">state_dict</span><span class="p">()</span><span class="n">i</span><span class="p">,</span> <span class="sa">f</span><span class="s">"./model_</span><span class="si">{</span><span class="n">model_name</span><span class="si">}</span><span class="s">.pt"</span><span class="p">)</span>

        <span class="k">else</span> <span class="p">:</span>
            <span class="n">early_stop_counter</span> <span class="o">+=</span> <span class="mi">1</span>

        <span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"Epoch </span><span class="si">{</span><span class="n">epoch</span><span class="o">+</span><span class="mi">1</span><span class="si">}</span><span class="s">/</span><span class="si">{</span><span class="n">num_epochs</span><span class="si">}</span><span class="s">, Train_Loss: </span><span class="si">{</span><span class="n">train_loss</span><span class="si">:</span><span class="p">.</span><span class="mi">4</span><span class="n">f</span><span class="si">}</span><span class="s">, Train_Accuracy: </span><span class="si">{</span><span class="n">train_accuracy</span><span class="si">:</span><span class="p">.</span><span class="mi">4</span><span class="n">f</span><span class="si">}</span><span class="s">, Valid_Loss: </span><span class="si">{</span><span class="n">valid_loss</span><span class="si">:</span><span class="p">.</span><span class="mi">4</span><span class="n">f</span><span class="si">}</span><span class="s">, Valid_Accuracy: </span><span class="si">{</span><span class="n">valid_accuracy</span><span class="si">:</span><span class="p">.</span><span class="mi">4</span><span class="n">f</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>

        <span class="k">if</span> <span class="n">early_stop_counter</span> <span class="o">&gt;=</span> <span class="n">patience</span> <span class="p">:</span>
            <span class="k">print</span><span class="p">(</span><span class="s">"Early Stopping"</span><span class="p">)</span>
            <span class="k">break</span>

    <span class="k">return</span> <span class="n">model</span><span class="p">,</span> <span class="n">valid_max_accuracy</span>
</code></pre></div></div>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">num_epochs</span> <span class="o">=</span> <span class="mi">100</span>
<span class="n">patience</span> <span class="o">=</span> <span class="mi">3</span>
<span class="n">scores</span> <span class="o">=</span> <span class="nb">dict</span><span class="p">()</span>
<span class="n">device</span> <span class="o">=</span> <span class="s">'cuda:0'</span>
<span class="n">model_name</span> <span class="o">=</span> <span class="s">"exp1"</span>
<span class="n">init_method</span> <span class="o">=</span> <span class="s">'kaiming'</span>

<span class="n">model</span> <span class="o">=</span> <span class="n">DNN</span><span class="p">(</span><span class="n">hidden_dims</span><span class="o">=</span><span class="n">hidden_dims</span><span class="p">,</span> <span class="n">num_classes</span><span class="o">=</span><span class="mi">10</span><span class="p">,</span> <span class="n">dropout_ratio</span><span class="o">=</span><span class="mf">0.2</span><span class="p">,</span> <span class="n">apply_batchnorm</span><span class="o">=</span><span class="bp">True</span><span class="p">,</span> <span class="n">apply_dropout</span><span class="o">=</span><span class="bp">True</span><span class="p">,</span> <span class="n">apply_activation</span><span class="o">=</span><span class="bp">True</span><span class="p">,</span> <span class="n">set_super</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
<span class="n">model</span><span class="p">.</span><span class="n">weight_initialization</span><span class="p">(</span><span class="n">init_method</span><span class="p">)</span>
<span class="n">model</span> <span class="o">=</span> <span class="n">model</span><span class="p">.</span><span class="n">to</span><span class="p">(</span><span class="n">device</span><span class="p">)</span>

<span class="n">criterion</span> <span class="o">=</span> <span class="n">nn</span><span class="p">.</span><span class="n">NLLLoss</span><span class="p">()</span>
<span class="n">optimizer</span> <span class="o">=</span> <span class="n">optim</span><span class="p">.</span><span class="n">Adam</span><span class="p">(</span><span class="n">model</span><span class="p">.</span><span class="n">parameters</span><span class="p">(),</span> <span class="n">lr</span><span class="o">=</span><span class="n">lr</span><span class="p">)</span>

<span class="n">model</span><span class="p">,</span> <span class="n">valid_max_accuracy</span> <span class="o">=</span> <span class="n">training_loop</span><span class="p">(</span><span class="n">model</span><span class="p">,</span> <span class="n">train_dataloader</span><span class="p">,</span> <span class="n">valid_dataloader</span><span class="p">,</span> <span class="n">criterion</span><span class="p">,</span> <span class="n">optimizer</span><span class="p">,</span> <span class="n">device</span><span class="p">,</span> <span class="n">num_epochs</span><span class="p">,</span> <span class="n">patience</span><span class="p">,</span> <span class="n">model_name</span><span class="p">)</span>
<span class="n">scores</span><span class="p">[</span><span class="n">model_name</span><span class="p">]</span> <span class="o">=</span> <span class="n">valid_max_accuracy</span>
</code></pre></div></div>

<pre>
Epoch 1/100, Train_Loss: -1254.1931: 100%|██████████| 1500/1500 [00:08&lt;00:00, 176.03it/s]
Epoch 1/100, Valid_Loss: -350.4877: 100%|██████████| 375/375 [00:01&lt;00:00, 315.23it/s]
</pre>
<pre>
Epoch 1/100, Train_Loss: -0.8361, Train_Accuracy: 0.8579, Valid_Loss: -0.9346, Valid_Accuracy: 0.9382
</pre>
<pre>
Epoch 2/100, Train_Loss: -1365.1130: 100%|██████████| 1500/1500 [00:08&lt;00:00, 177.13it/s]
Epoch 2/100, Valid_Loss: -355.5040: 100%|██████████| 375/375 [00:01&lt;00:00, 324.88it/s]
</pre>
<pre>
Epoch 2/100, Train_Loss: -0.9101, Train_Accuracy: 0.9151, Valid_Loss: -0.9480, Valid_Accuracy: 0.9503
</pre>
<pre>
Epoch 3/100, Train_Loss: -1385.8775: 100%|██████████| 1500/1500 [00:08&lt;00:00, 177.68it/s]
Epoch 3/100, Valid_Loss: -357.5532: 100%|██████████| 375/375 [00:01&lt;00:00, 324.07it/s]
</pre>
<pre>
Epoch 3/100, Train_Loss: -0.9239, Train_Accuracy: 0.9279, Valid_Loss: -0.9535, Valid_Accuracy: 0.9553
</pre>
<pre>
Epoch 4/100, Train_Loss: -1400.4983: 100%|██████████| 1500/1500 [00:08&lt;00:00, 177.52it/s]
Epoch 4/100, Valid_Loss: -358.7989: 100%|██████████| 375/375 [00:01&lt;00:00, 340.53it/s]
</pre>
<pre>
Epoch 4/100, Train_Loss: -0.9337, Train_Accuracy: 0.9376, Valid_Loss: -0.9568, Valid_Accuracy: 0.9581
</pre>
<pre>
Epoch 5/100, Train_Loss: -1405.4431: 100%|██████████| 1500/1500 [00:08&lt;00:00, 184.06it/s]
Epoch 5/100, Valid_Loss: -359.9167: 100%|██████████| 375/375 [00:01&lt;00:00, 339.84it/s]
</pre>
<pre>
Epoch 5/100, Train_Loss: -0.9370, Train_Accuracy: 0.9388, Valid_Loss: -0.9598, Valid_Accuracy: 0.9608
</pre>
<pre>
Epoch 6/100, Train_Loss: -1411.7580: 100%|██████████| 1500/1500 [00:07&lt;00:00, 190.95it/s]
Epoch 6/100, Valid_Loss: -360.8559: 100%|██████████| 375/375 [00:01&lt;00:00, 319.57it/s]
</pre>
<pre>
Epoch 6/100, Train_Loss: -0.9412, Train_Accuracy: 0.9433, Valid_Loss: -0.9623, Valid_Accuracy: 0.9634
</pre>
<pre>
Epoch 7/100, Train_Loss: -1417.0492: 100%|██████████| 1500/1500 [00:08&lt;00:00, 180.71it/s]
Epoch 7/100, Valid_Loss: -361.1227: 100%|██████████| 375/375 [00:01&lt;00:00, 327.59it/s]
</pre>
<pre>
Epoch 7/100, Train_Loss: -0.9447, Train_Accuracy: 0.9467, Valid_Loss: -0.9630, Valid_Accuracy: 0.9639
</pre>
<pre>
Epoch 8/100, Train_Loss: -1422.2407: 100%|██████████| 1500/1500 [00:08&lt;00:00, 181.93it/s]
Epoch 8/100, Valid_Loss: -360.8939: 100%|██████████| 375/375 [00:01&lt;00:00, 323.78it/s]
</pre>
<pre>
Epoch 8/100, Train_Loss: -0.9482, Train_Accuracy: 0.9498, Valid_Loss: -0.9624, Valid_Accuracy: 0.9626
</pre>
<pre>
Epoch 9/100, Train_Loss: -1422.8521: 100%|██████████| 1500/1500 [00:08&lt;00:00, 187.39it/s]
Epoch 9/100, Valid_Loss: -360.7753: 100%|██████████| 375/375 [00:01&lt;00:00, 338.03it/s]
</pre>
<pre>
Epoch 9/100, Train_Loss: -0.9486, Train_Accuracy: 0.9500, Valid_Loss: -0.9621, Valid_Accuracy: 0.9628
</pre>
<pre>
Epoch 10/100, Train_Loss: -1425.4209: 100%|██████████| 1500/1500 [00:08&lt;00:00, 184.94it/s]
Epoch 10/100, Valid_Loss: -363.1457: 100%|██████████| 375/375 [00:01&lt;00:00, 341.63it/s]
</pre>
<pre>
Epoch 10/100, Train_Loss: -0.9503, Train_Accuracy: 0.9517, Valid_Loss: -0.9684, Valid_Accuracy: 0.9695
</pre>
<pre>
Epoch 11/100, Train_Loss: -1429.4238: 100%|██████████| 1500/1500 [00:08&lt;00:00, 185.38it/s]
Epoch 11/100, Valid_Loss: -362.8645: 100%|██████████| 375/375 [00:01&lt;00:00, 333.20it/s]
</pre>
<pre>
Epoch 11/100, Train_Loss: -0.9529, Train_Accuracy: 0.9546, Valid_Loss: -0.9676, Valid_Accuracy: 0.9681
Early Stopping
</pre>
<pre>

</pre>
<hr />

<h1 id="추론과-평가">추론과 평가</h1>

<p>훈련시켰던 모델을 로컬경로에 저장한다.</p>

<p>필요 시 모델을 <strong>load_state_dict</strong>를 이용하여 로드한다.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">model</span> <span class="o">=</span> <span class="n">DNN</span><span class="p">(</span><span class="n">hidden_dims</span><span class="o">=</span><span class="n">hidden_dims</span><span class="p">,</span> <span class="n">num_classes</span><span class="o">=</span><span class="mi">10</span><span class="p">,</span> <span class="n">dropout_ratio</span><span class="o">=</span><span class="mf">0.2</span><span class="p">,</span> <span class="n">apply_batchnorm</span><span class="o">=</span><span class="bp">True</span><span class="p">,</span> <span class="n">apply_dropout</span><span class="o">=</span><span class="bp">True</span><span class="p">,</span> <span class="n">apply_activation</span><span class="o">=</span><span class="bp">True</span><span class="p">,</span> <span class="n">set_super</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
<span class="n">model</span><span class="p">.</span><span class="n">load_state_dict</span><span class="p">(</span><span class="n">torch</span><span class="p">.</span><span class="n">load</span><span class="p">(</span><span class="s">'./model_exp1.pt'</span><span class="p">))</span>
<span class="n">model</span> <span class="o">=</span> <span class="n">model</span><span class="p">.</span><span class="n">to</span><span class="p">(</span><span class="n">device</span><span class="p">)</span>
</code></pre></div></div>

<pre>
/tmp/ipykernel_19749/3655546662.py:2: FutureWarning: You are using `torch.load` with `weights_only=False` (the current default value), which uses the default pickle module implicitly. It is possible to construct malicious pickle data which will execute arbitrary code during unpickling (See https://github.com/pytorch/pytorch/blob/main/SECURITY.md#untrusted-models for more details). In a future release, the default value for `weights_only` will be flipped to `True`. This limits the functions that could be executed during unpickling. Arbitrary objects will no longer be allowed to be loaded via this mode unless they are explicitly allowlisted by the user via `torch.serialization.add_safe_globals`. We recommend you start setting `weights_only=True` for any use case where you don't have full control of the loaded file. Please open an issue on GitHub for any issues related to this experimental feature.
  model.load_state_dict(torch.load('./model_exp1.pt'))
</pre>
<p>모델을 평가 모드로 설정하고 미분 계산을 필요로 하지 않기 때문에 torch.no_grad()를 사용한다.</p>

<ul>
  <li>
    <p>total_preds : 모델이 예측한 값</p>
  </li>
  <li>
    <p>total_labels : 실제 값</p>
  </li>
  <li>
    <p>total_probs : AUC를 구하기 위한 모델이 각 클래스를 예측한 확률값</p>
  </li>
</ul>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">model</span><span class="p">.</span><span class="nb">eval</span><span class="p">()</span>
<span class="n">total_labels</span> <span class="o">=</span> <span class="p">[]</span>
<span class="n">total_preds</span> <span class="o">=</span> <span class="p">[]</span>
<span class="n">total_probs</span> <span class="o">=</span> <span class="p">[]</span>

<span class="k">with</span> <span class="n">torch</span><span class="p">.</span><span class="n">no_grad</span><span class="p">()</span> <span class="p">:</span>
    <span class="k">for</span> <span class="n">images</span><span class="p">,</span> <span class="n">labels</span> <span class="ow">in</span> <span class="n">test_dataloader</span> <span class="p">:</span>
        <span class="n">images</span> <span class="o">=</span> <span class="n">images</span><span class="p">.</span><span class="n">to</span><span class="p">(</span><span class="n">device</span><span class="p">)</span>
        <span class="n">labels</span> <span class="o">=</span> <span class="n">labels</span>

        <span class="n">outputs</span> <span class="o">=</span> <span class="n">model</span><span class="p">(</span><span class="n">images</span><span class="p">)</span>
        <span class="n">_</span><span class="p">,</span> <span class="n">predicted</span> <span class="o">=</span> <span class="n">torch</span><span class="p">.</span><span class="nb">max</span><span class="p">(</span><span class="n">outputs</span><span class="p">.</span><span class="n">data</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>

        <span class="n">total_preds</span><span class="p">.</span><span class="n">extend</span><span class="p">(</span><span class="n">predicted</span><span class="p">.</span><span class="n">detach</span><span class="p">().</span><span class="n">cpu</span><span class="p">().</span><span class="n">tolist</span><span class="p">())</span>
        <span class="n">total_labels</span><span class="p">.</span><span class="n">extend</span><span class="p">(</span><span class="n">labels</span><span class="p">.</span><span class="n">tolist</span><span class="p">())</span>
        <span class="n">total_probs</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">outputs</span><span class="p">.</span><span class="n">detach</span><span class="p">().</span><span class="n">cpu</span><span class="p">().</span><span class="n">numpy</span><span class="p">())</span>

<span class="n">total_preds</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">array</span><span class="p">(</span><span class="n">total_preds</span><span class="p">)</span>
<span class="n">total_labels</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">array</span><span class="p">(</span><span class="n">total_labels</span><span class="p">)</span>
<span class="n">total_probs</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">concatenate</span><span class="p">(</span><span class="n">total_probs</span><span class="p">,</span> <span class="n">axis</span><span class="o">=</span><span class="mi">0</span><span class="p">)</span>
</code></pre></div></div>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">precision</span> <span class="o">=</span> <span class="n">precision_score</span><span class="p">(</span><span class="n">total_labels</span><span class="p">,</span> <span class="n">total_preds</span><span class="p">,</span> <span class="n">average</span><span class="o">=</span><span class="s">'macro'</span><span class="p">)</span>
<span class="n">recall</span> <span class="o">=</span> <span class="n">recall_score</span><span class="p">(</span><span class="n">total_labels</span><span class="p">,</span> <span class="n">total_preds</span><span class="p">,</span> <span class="n">average</span><span class="o">=</span><span class="s">'macro'</span><span class="p">)</span>
<span class="n">f1</span> <span class="o">=</span> <span class="n">f1_score</span><span class="p">(</span><span class="n">total_labels</span><span class="p">,</span> <span class="n">total_preds</span><span class="p">,</span> <span class="n">average</span><span class="o">=</span><span class="s">'macro'</span><span class="p">)</span>

<span class="n">auc</span> <span class="o">=</span> <span class="n">roc_auc_score</span><span class="p">(</span><span class="n">total_labels</span><span class="p">,</span> <span class="n">total_probs</span><span class="p">,</span> <span class="n">average</span><span class="o">=</span><span class="s">'macro'</span><span class="p">,</span> <span class="n">multi_class</span><span class="o">=</span><span class="s">'ovr'</span><span class="p">)</span>

<span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"Precision: </span><span class="si">{</span><span class="n">precision</span><span class="si">}</span><span class="s">, Recall: </span><span class="si">{</span><span class="n">recall</span><span class="si">}</span><span class="s">, F1: </span><span class="si">{</span><span class="n">f1</span><span class="si">}</span><span class="s">, AUC: </span><span class="si">{</span><span class="n">auc</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
</code></pre></div></div>

<pre>
Precision: 0.9711098052403464, Recall: 0.9708028372978227, F1: 0.9709088177178395, AUC: 0.9988558476539848
</pre>
<hr />

<h1 id="cnn">CNN</h1>

<p>위와 같은 이미지 모델은 일반적으로 Convolution 연산을 통해 훈련한다.</p>

<p>아래와 같이 Convolution 연산을 수행하는 레이어를 정의하여 구현한다.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="n">nn</span><span class="p">.</span><span class="n">Conv2d</span><span class="p">(</span><span class="n">in_channels</span><span class="p">,</span> <span class="n">out_channels</span><span class="p">,</span> <span class="n">kernel_size</span><span class="p">,</span> <span class="n">stride</span><span class="p">,</span> <span class="n">padding</span><span class="p">)</span>

</code></pre></div></div>

<ul>
  <li>
    <p>in_channels: 입력 이미지의 채널 수</p>
  </li>
  <li>
    <p>out_channels: 출력 이미지의 채널 수</p>
  </li>
  <li>
    <p>kernel_size: 필터의 크기</p>
  </li>
  <li>
    <p>stride: 필터의 이동 단위</p>
  </li>
  <li>
    <p>padding: 입력 이미지의 테두리에 추가되는 픽셀 수</p>
  </li>
  <li>
    <p>주의 사항</p>

    <ul>
      <li>
        <p>층을 쌓을 때 in_channels의 값은 이미지의 채널과 맞춰주어야 한다.</p>

        <ul>
          <li>예를 들어, RGB 이미지의 경우 “<strong><em>in_channels=3</em></strong>“이 되어야 한다. (gray-scale 이미지의 경우 “<strong><em>in_channels=1</em></strong>”)</li>
        </ul>
      </li>
      <li>
        <p><strong>Pooling</strong>을 사용하지 않아도 되나, feature map의 크기를 줄여서 다음 레이어로 전달되는 데이터의 양을 감소시켜 메모리 사용량을 줄이고 계산 속도를 향상시킬 수 있다.</p>

        <ul>
          <li>만약 Pooling을 사용하지 않는다면, 메모리 사용량이 크게 증가되어 서버가 다운될 수 있다.</li>
        </ul>
      </li>
      <li>
        <p>앞서 nn.Linear()를 이용하여 구현할 때에는 ModuleList를 사용하여 정의하였지만, nn.Sequential()을 사용하여 순차적으로 처리하는 모듈을 사용하여 구현하면 된다.</p>
      </li>
    </ul>
  </li>
</ul>

<p>위에 따라서 전체적인 CNN 모델의 구조를 작성해보면 다음과 같다.</p>

<hr />

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="n">features</span> <span class="o">=</span> <span class="n">nn</span><span class="p">.</span><span class="n">Sequential</span><span class="p">(</span>

    <span class="c1"># 첫 번째 컨볼루션 블록
</span>
    <span class="n">nn</span><span class="p">.</span><span class="n">Conv2d</span><span class="p">(</span><span class="mi">3</span><span class="p">,</span> <span class="mi">32</span><span class="p">,</span> <span class="n">kernel_size</span><span class="o">=</span><span class="mi">3</span><span class="p">,</span> <span class="n">padding</span><span class="o">=</span><span class="mi">1</span><span class="p">),</span>

    <span class="n">nn</span><span class="p">.</span><span class="n">BatchNorm2d</span><span class="p">(</span><span class="mi">32</span><span class="p">),</span>

    <span class="n">nn</span><span class="p">.</span><span class="n">ReLU</span><span class="p">(</span><span class="n">inplace</span><span class="o">=</span><span class="bp">True</span><span class="p">),</span>

    <span class="n">nn</span><span class="p">.</span><span class="n">MaxPool2d</span><span class="p">(</span><span class="n">kernel_size</span><span class="o">=</span><span class="mi">2</span><span class="p">,</span> <span class="n">stride</span><span class="o">=</span><span class="mi">2</span><span class="p">),</span>

    

    <span class="c1"># 두 번째 컨볼루션 블록
</span>
    <span class="n">nn</span><span class="p">.</span><span class="n">Conv2d</span><span class="p">(</span><span class="mi">32</span><span class="p">,</span> <span class="mi">64</span><span class="p">,</span> <span class="n">kernel_size</span><span class="o">=</span><span class="mi">3</span><span class="p">,</span> <span class="n">padding</span><span class="o">=</span><span class="mi">1</span><span class="p">),</span>

    <span class="n">nn</span><span class="p">.</span><span class="n">BatchNorm2d</span><span class="p">(</span><span class="mi">64</span><span class="p">),</span>

    <span class="n">nn</span><span class="p">.</span><span class="n">ReLU</span><span class="p">(</span><span class="n">inplace</span><span class="o">=</span><span class="bp">True</span><span class="p">),</span>

    <span class="n">nn</span><span class="p">.</span><span class="n">MaxPool2d</span><span class="p">(</span><span class="n">kernel_size</span><span class="o">=</span><span class="mi">2</span><span class="p">,</span> <span class="n">stride</span><span class="o">=</span><span class="mi">2</span><span class="p">),</span>

    

    <span class="c1"># 세 번째 컨볼루션 블록
</span>
    <span class="n">nn</span><span class="p">.</span><span class="n">Conv2d</span><span class="p">(</span><span class="mi">64</span><span class="p">,</span> <span class="mi">128</span><span class="p">,</span> <span class="n">kernel_size</span><span class="o">=</span><span class="mi">3</span><span class="p">,</span> <span class="n">padding</span><span class="o">=</span><span class="mi">1</span><span class="p">),</span>

    <span class="n">nn</span><span class="p">.</span><span class="n">BatchNorm2d</span><span class="p">(</span><span class="mi">128</span><span class="p">),</span>

    <span class="n">nn</span><span class="p">.</span><span class="n">ReLU</span><span class="p">(</span><span class="n">inplace</span><span class="o">=</span><span class="bp">True</span><span class="p">),</span>

    <span class="n">nn</span><span class="p">.</span><span class="n">MaxPool2d</span><span class="p">(</span><span class="n">kernel_size</span><span class="o">=</span><span class="mi">2</span><span class="p">,</span> <span class="n">stride</span><span class="o">=</span><span class="mi">2</span><span class="p">),</span>

<span class="p">)</span>



<span class="c1"># 분류 부분 (Fully connected layers)
</span>
<span class="n">classifier</span> <span class="o">=</span> <span class="n">nn</span><span class="p">.</span><span class="n">Sequential</span><span class="p">(</span>

    <span class="n">nn</span><span class="p">.</span><span class="n">Dropout</span><span class="p">(</span><span class="mf">0.5</span><span class="p">),</span>

    <span class="n">nn</span><span class="p">.</span><span class="n">Linear</span><span class="p">(</span><span class="mi">128</span> <span class="o">*</span> <span class="mi">4</span> <span class="o">*</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">512</span><span class="p">),</span>

    <span class="n">nn</span><span class="p">.</span><span class="n">ReLU</span><span class="p">(</span><span class="n">inplace</span><span class="o">=</span><span class="bp">True</span><span class="p">),</span>

    <span class="n">nn</span><span class="p">.</span><span class="n">Dropout</span><span class="p">(</span><span class="mf">0.5</span><span class="p">),</span>

    <span class="n">nn</span><span class="p">.</span><span class="n">Linear</span><span class="p">(</span><span class="mi">512</span><span class="p">,</span> <span class="mi">256</span><span class="p">),</span>

    <span class="n">nn</span><span class="p">.</span><span class="n">ReLU</span><span class="p">(</span><span class="n">inplace</span><span class="o">=</span><span class="bp">True</span><span class="p">),</span>

    <span class="n">nn</span><span class="p">.</span><span class="n">Linear</span><span class="p">(</span><span class="mi">256</span><span class="p">,</span> <span class="n">num_classes</span><span class="p">)</span>

<span class="p">)</span>

</code></pre></div></div>

<p>위의 모델의 흐름을 파악해보면 다음과 같다.</p>

<ol>
  <li>
    <p>[BATCH_SIZE, 3, 32, 32] 크기의 입력 이미지(RGB 이미지)를 입력으로 받음</p>
  </li>
  <li>
    <p>첫 번째 컨볼루션 블록에서 3개의 채널을 32로 확장, 3*3 필터로 특성을 추출, 패딩은 1이므로 이미지 그대로 유지</p>
  </li>
  <li>
    <p>MaxPooling으로 크기가 절반으로 줄어듦 → [BATCH_SIZE, 32, 16, 16]</p>
  </li>
  <li>
    <p>두 번째 컨볼루션 블록에서 32개의 채널을 64로 확장, 3*3 필터로 특성을 추출, 패딩은 1이므로 이미지 그대로 유지</p>
  </li>
  <li>
    <p>MaxPooling으로 크기가 절반으로 줄어듦 → [BATCH_SIZE, 64, 8, 8]</p>
  </li>
  <li>
    <p>세 번째 컨볼루션 블록에서 64개의 채널을 128로 확장, 3*3 필터로 특성을 추출, 패딩은 1이므로 이미지 그대로 유지</p>
  </li>
  <li>
    <p>MaxPooling으로 크기가 절반으로 줄어듦 → [BATCH_SIZE, 128, 4, 4]</p>
  </li>
  <li>
    <p>Fully connected layer(분류 부분)에 입력으로 주기 위해 평탄화 (nn.Linear(128 * 4 * 4, 512))</p>
  </li>
  <li>
    <p>Fully connected layer 추가 및 분류 실행</p>
  </li>
</ol>

<hr />

<p>실제로 위와 같이 모델의 구조를 직접 구현하는 방법이 있지만 좋은 성능을 내는 모델을 구현하기 위해서는 많은 컴퓨터 리소스, 다양한 파라미터를 튜닝하고 실험할 수 있는 충분한 시간 등의 어려움이 존재한다.</p>

<p>이러한 이유로 오픈 소스로 공개되어 있는 다양한 모델들이 존재하는데, VGG, ResNet, GoogLeNet 등의 이미지 모델이 존재한다.</p>

<p>우리는 이러한 모델을 가져와서 <strong><em>Fine-Tunning</em></strong>하는 방식으로 특정 Task에 뛰어난 성능을 보이는 모델을 훈련시킬 수 있고, 실제로 이러한 방식이 많이 사용되고 있다.</p>

<p>다음은 다양한 이미지 관련 모델들을 제공하는 사이트들이다.</p>

<blockquote>
  <p><a href="https://huggingface.co/models">HuggingFace</a></p>
</blockquote>

<blockquote>
  <p><a href="https://github.com/onnx/models">ONNX Model Zoo</a></p>
</blockquote>

<blockquote>
  <p><a href="https://tfhub.dev/">TensorFlow Hub</a> / Tensorflow 및 Keras와 연동</p>
</blockquote>

<blockquote>
  <p><a href="https://pytorch.org/hub/">PyTorch Hub</a> / PyTorch와 연동</p>
</blockquote>

<hr />

<h1 id="rnn">RNN</h1>

<p>DNN은 각 layer의 출력을 다음 layer의 입력으로 받는 구조를 가지고 있다.</p>

<p>즉 지난 layer의 정보를 이용하지 않고 현재 layer에 들어온 정보만을 가지고 계산하게 되는데, <strong><em>RNN</em></strong>은 지난 layer의 정보를 이용하여 계산하게 된다.</p>

<p>이러한 방식은 위치 정보가 필요한 자연어와 같은 데이터를 처리할 때 유용하게 사용된다.</p>

<p>하지만 자연어와 같은 텍스트 데이터 그대로는 모델이 훈련할 수 없는 구조이기에 몇 가지 처리해주어야 하는데</p>

<ol>
  <li>
    <p><strong>Tokenizer</strong>로 텍스트 토큰화</p>
  </li>
  <li>
    <p><strong>Padding</strong>을 이용하여 동일한 크기의 데이터로 변환</p>
  </li>
</ol>

<p>먼저 토큰화의 경우 간단하게(띄어쓰기를 기준으로 생각하면) 단어별 숫자를 부여하여 토큰화를 진행할 수 있다.</p>

<p>(현재 자연어 처리에 사용되는 토크나이저는 위의 방식보다 복잡하지만, 간단하게 생각하기로 한다.)</p>

<ul>
  <li>
    <p>“안녕하세요. 저는 딥러닝을 공부하고 있습니다.”와 같은 문장에서…</p>

    <ul>
      <li>
        <p>0 : 안녕하세요</p>
      </li>
      <li>
        <p>1 : 저는</p>
      </li>
      <li>
        <p>2 : 딥러닝을</p>
      </li>
      <li>
        <p>… 과 같이 토큰화한다고 생각하면 된다.</p>
      </li>
    </ul>
  </li>
</ul>

<p>다음으로 Padding은 크기를 맞추는 작업인데, 자연어의 경우 문장마다 텍스트의 길이가 다르다.</p>

<ul>
  <li>
    <p>“안녕하세요” VS “안녕”</p>

    <ul>
      <li>“<strong>안녕하세요</strong>“는 1단어, “<strong>안녕 반가워</strong>“은 2단어이므로 “안녕하세요”에 1 padding을 추가하여 맞춰준다.</li>
    </ul>
  </li>
</ul>

<p>위와 같은 방식으로 모든 텍스트의 입력 데이터 크기를 맞춰줌으로써 훈련을 진행할 수 있다.</p>

<hr />

<h2 id="구현">구현</h2>

<p>대표적인 순환 신경망 구조의 모델로는 아래 3개가 있다.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="n">torch</span><span class="p">.</span><span class="n">nn</span><span class="p">.</span><span class="n">RNN</span><span class="p">(</span><span class="n">input_size</span><span class="p">,</span> <span class="n">hidden_size</span><span class="p">)</span>

</code></pre></div></div>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="n">torch</span><span class="p">.</span><span class="n">nn</span><span class="p">.</span><span class="n">LSTM</span><span class="p">(</span><span class="n">input_size</span><span class="p">,</span> <span class="n">hidden_size</span><span class="p">)</span>

</code></pre></div></div>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="n">torch</span><span class="p">.</span><span class="n">nn</span><span class="p">.</span><span class="n">GRU</span><span class="p">(</span><span class="n">input_size</span><span class="p">,</span> <span class="n">hidden_size</span><span class="p">)</span>

</code></pre></div></div>

<p>위의 모듈을 통해 구현한다.</p>

<ul>
  <li>
    <p>input_size : 임베딩 차원</p>
  </li>
  <li>
    <p>hidden_size : 은닉층</p>
  </li>
</ul>

<p>이를 통해 <strong>RNN</strong> 모델의 간단한 구조를 짜보면 다음과 같다.</p>

<p>텍스트가 긍정/부정으로 분류되는 모델을 구현한다고 가정했을 때,</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="k">class</span> <span class="nc">RNNSequential</span><span class="p">(</span><span class="n">nn</span><span class="p">.</span><span class="n">Module</span><span class="p">):</span>

    <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>

        <span class="nb">super</span><span class="p">().</span><span class="n">__init__</span><span class="p">()</span>

        <span class="bp">self</span><span class="p">.</span><span class="n">embedding</span> <span class="o">=</span> <span class="n">nn</span><span class="p">.</span><span class="n">Embedding</span><span class="p">(</span><span class="n">vocab_size</span><span class="p">,</span> <span class="n">embed_dim</span><span class="p">)</span>

        <span class="bp">self</span><span class="p">.</span><span class="n">rnn</span> <span class="o">=</span> <span class="n">nn</span><span class="p">.</span><span class="n">RNN</span><span class="p">(</span><span class="n">embed_dim</span><span class="p">,</span> <span class="n">hidden_dim</span><span class="p">,</span> <span class="n">batch_first</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>

        <span class="bp">self</span><span class="p">.</span><span class="n">classifier</span> <span class="o">=</span> <span class="n">nn</span><span class="p">.</span><span class="n">Sequential</span><span class="p">(</span>

            <span class="n">nn</span><span class="p">.</span><span class="n">Dropout</span><span class="p">(</span><span class="mf">0.5</span><span class="p">),</span>

            <span class="n">nn</span><span class="p">.</span><span class="n">Linear</span><span class="p">(</span><span class="n">hidden_dim</span><span class="p">,</span> <span class="n">num_classes</span><span class="p">)</span>

        <span class="p">)</span>

    

    <span class="k">def</span> <span class="nf">forward</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">x</span><span class="p">):</span>

        <span class="n">x</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">embedding</span><span class="p">(</span><span class="n">x</span><span class="p">)</span>

        <span class="n">output</span><span class="p">,</span> <span class="p">(</span><span class="n">hidden</span><span class="p">,</span> <span class="n">_</span><span class="p">)</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">rnn</span><span class="p">(</span><span class="n">x</span><span class="p">)</span>



        <span class="n">last_output</span> <span class="o">=</span> <span class="n">output</span><span class="p">[:,</span> <span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="p">:]</span>

        <span class="k">return</span> <span class="bp">self</span><span class="p">.</span><span class="n">classifier</span><span class="p">(</span><span class="n">last_output</span><span class="p">)</span>

</code></pre></div></div>

<ol>
  <li>
    <p>입력데이터의 크기는 [BATCH_SIZE, SEQUENCE_LENGTH], embed_dim은</p>
  </li>
  <li>
    <p><strong><em>Embedding</em></strong>  → [BATCH_SIZE, SEQUENCE_LENGTH]로 들어온 입력을 [BATCH_SIZE, SEQUENCE_LENGTH, embed_dim] 변환 → 임베딩을 통해 토큰화를 통해 정수 인덱스로 변환된 텍스트를 벡터로 변환</p>
  </li>
  <li>
    <p><strong>RNN</strong>을 통과시켜 훈련, output과 <strong>(hidden, _)</strong>을 통해 이전 은닉층의 정보를 사용하여 문맥 파악</p>
  </li>
  <li>
    <p><strong>classifier</strong>를 통해 분류 작업</p>
  </li>
</ol>

<p>DNN과 다르게 포워딩 과정에서 출력값과, 이전 은닉층을 사용하기에 과거의 정보를 사용하여 위치 정보 및 시간적 정보를 처리할 수 있음을 시사한다.</p>

<hr />

<h2 id="rnn-lstm-gru">RNN, LSTM, GRU</h2>

<p>일반적으로 RNN, LSTM, GRU 세 모델은 <strong>과거의 정보</strong>만을 이용하여 다음 정보를 예측하게 된다.</p>

<p>예를 들어</p>

<ul>
  <li>
    <p>“나는 지금 __이야. 밥 먹는 중.” 라는 문장이 있다고 가정해보자.</p>

    <ul>
      <li>
        <p>이전 순환 신경망 구조에서는 과거의 정보만 이용했기에 “<strong>밥 먹는 중</strong>“이라는 정보를 알 수 없다.</p>
      </li>
      <li>
        <p>이는 저 빈칸에 들어갈 말뭉치가 무엇인지 과거의 정보만을 가지고 예측할 수 없다.</p>

        <ul>
          <li>식당, 집, 호텔 등등이 들어올 수 있고, 무엇이 들어가도 어색하지 않기에 모름</li>
        </ul>
      </li>
      <li>
        <p>뒤의 정보를 이용하여 이를 추론해야함.</p>
      </li>
    </ul>
  </li>
</ul>

<p>이런 점을 해결하기 위하여 미래의 정보도 이용하는 양방향 모델을 구현해야하는데 다음과 같이 인자를 True로 설정하여 구현한다.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="n">torch</span><span class="p">.</span><span class="n">nn</span><span class="p">.</span><span class="n">RNN</span><span class="p">(</span><span class="n">input_size</span><span class="p">,</span> <span class="n">hidden_size</span><span class="p">,</span> <span class="n">bidirectional</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>

</code></pre></div></div>

<p>자연어 처리의 텍스트 생성이라는 도메인에서는 이전 단어들만을 가지고 다음에 어떤 단어를 생성해야 할지 정확하게 예측할 수 없다.</p>

<p>앞뒤 문장의 구조를 파악하여 적절한 단어가 오도록 예측하는 과정이 필요, 양방향 모델로 구현한다.</p>

<h2 id="평가">평가</h2>

<p>자연어 처리에 있어 특정 도메인(긍정/부정 분류 등)에서는 정확도와 같은 위에서 사용했던 평가지표를 사용할 수 있다.</p>

<p>실제로, 위의 예시에서는 정확도 지표를 사용하여 모델의 성능을 평가할 수 있다.</p>

<p>그러나, 일반적으로 번역이라던지, 텍스트 생성의 경우 정확도로 모델을 평가할 수 없다.</p>

<p>“안녕하세요” 라는 한국어 문장을 “Hello”로 번역할 때, 만약 모델이 “Halo”라고 번역하였을 경우 과연 정확도로 평가할 수 있을까?</p>

<p>Hello와 Halo는 언뜻 보기에는 비슷한 단어이지만, 의미는 완전히 다르기에 일반적인 평가지표를 가지고 사용할 수 없는데 이때 사용되는 평가지표가 cosine similarity, bleu score 등이 있다.</p>

<p>자연어 처리에 있어 가장 많이 사용되는 cosine similarity를 살펴보면 다음과 같이 구현된다.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="k">def</span> <span class="nf">cosine_similarity</span><span class="p">(</span><span class="n">vec1</span><span class="p">,</span> <span class="n">vec2</span><span class="p">):</span>

    <span class="c1"># 텐서로 변환
</span>
    <span class="k">if</span> <span class="ow">not</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">vec1</span><span class="p">,</span> <span class="n">torch</span><span class="p">.</span><span class="n">Tensor</span><span class="p">):</span>

        <span class="n">vec1</span> <span class="o">=</span> <span class="n">torch</span><span class="p">.</span><span class="n">tensor</span><span class="p">(</span><span class="n">vec1</span><span class="p">,</span> <span class="n">dtype</span><span class="o">=</span><span class="n">torch</span><span class="p">.</span><span class="n">float32</span><span class="p">)</span>

    <span class="k">if</span> <span class="ow">not</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">vec2</span><span class="p">,</span> <span class="n">torch</span><span class="p">.</span><span class="n">Tensor</span><span class="p">):</span>

        <span class="n">vec2</span> <span class="o">=</span> <span class="n">torch</span><span class="p">.</span><span class="n">tensor</span><span class="p">(</span><span class="n">vec2</span><span class="p">,</span> <span class="n">dtype</span><span class="o">=</span><span class="n">torch</span><span class="p">.</span><span class="n">float32</span><span class="p">)</span>

    

    <span class="c1"># PyTorch의 cosine_similarity 함수 사용
</span>
    <span class="n">similarity</span> <span class="o">=</span> <span class="n">F</span><span class="p">.</span><span class="n">cosine_similarity</span><span class="p">(</span><span class="n">vec1</span><span class="p">.</span><span class="n">unsqueeze</span><span class="p">(</span><span class="mi">0</span><span class="p">),</span> <span class="n">vec2</span><span class="p">.</span><span class="n">unsqueeze</span><span class="p">(</span><span class="mi">0</span><span class="p">))</span>

    <span class="k">return</span> <span class="n">similarity</span><span class="p">.</span><span class="n">item</span><span class="p">()</span>

</code></pre></div></div>

<p>결과를 해석하면 1에 가까울수록 완전히 같은 방향(유사한 텍스트), 0에 가까울수록 수직(유사하지 않은 텍스트)을 의미한다.</p>]]></content><author><name>Lee Jeong Min</name></author><category term="Bootcamp" /><category term="python" /><category term="AI" /><category term="패스트캠퍼스" /><category term="패스트캠퍼스AI부트캠프" /><category term="업스테이지패스트캠퍼스" /><category term="UpstageAILab" /><category term="국비지원" /><category term="패스트캠퍼스업스테이지에이아이랩" /><category term="패스트캠퍼스업스테이지부트캠프" /><summary type="html"><![CDATA[◆ Pytorch - DNN 구현 (25.06.18 위키라이더) Pytorch 강의의 Implement Deep Learning Models 챕터의 DNN 구현과 CNN, RNN 구현을 정리 딥러닝에서의 Pytorch 딥러닝을 구현하기 위한 대표적인 라이브러리로는 Tensorflow와 Pytorch가 있다. 2010년 중~후반대만 하더라도 Tensorflow의 사용비율이 높았으나 점차 Pytorch의 사용비율이 높아지는데, 연구자들에게 친화적인 라이브러리, 언어 모델의 발전으로 Hugging Face 등장, Tensorflow와 비교하여 직관적이고 파이썬적 API 등의 이유가 있다. 두 라이브러리 모두 공부해본 나의 입장에서는 하나를 다룰 줄 알면 비슷한 구조로 짜여있기에 하나를 익히면 다른 라이브러리는 쉽게 익힐 수 있다고 생각한다. 이제 DNN 모델을 구현하기 전에 딥러닝의 학습단계가 어떻게 되는지 살펴보고 가면… Deep Learning의 학습단계 Data → Model → Output → Loss → Optimization 간단하게 나누면 위와 같은 과정을 반복한다고 볼 수 있다. 데이터를 가지고 모델을 훈련하여 출력을 만들어내고, 실제 값과 비교하여 손실값을 계산, 역전파를 이용하여 파라미터를 최적화하여 다시 훈련하는 과정을 반복하게 된다. 각각의 과정에서 Pytorch의 다음과 같은 라이브러리 밑의 요소들을 이용하여 구현하게 된다. Data : 데이터셋에서 미니 배치 크기의 데이터 반환 Dataset : 단일 데이터를 모델의 입력으로 사용할 수 있는 Tensor로 변환 DataLoader : 데이터셋을 미니 배치 크기의 데이터로 반환 Pytorch에서 제공하는 Dataset, DataLoader는 가장 대중적으로 사용하는 기능들만 구현되어있기에, 세부적인 사항들은 직접 커스텀으로 구현해야 함 Model Torchvision : 이미지 분석에 특화된 모델을 제공하는 라이브러리 PyTorch Hub : CV, 음성, 생성형, NLP 등의 모델을 제공하는 라이브러리 마찬가지로 Pytorch에 공개된 모델은 제한적이므로 프로젝트의 목표에 맞게 모델을 변형해서 사용해야함 Optimizer optimizer.zero_grad() : 이전 gradient를 0으로 설정 model(data) : 데이터를 모델을 통해 연산 loss_function(output, label) : loss 값 계산 loss.backward() : loss 값에 대한 gradient 계산 optimizer.step() : gradient를 이용하여 모델의 파라미터 업데이트 Inference &amp; Evaluation model.eval() : 모델을 평가 모드로 전환 → 특정 레이어들이 학습과 추론 과정 각각 다르게 작동해야 하기 때문 torch.no_grad() : 추론 과정에서는 gradient 계산이 필요하지 않음 Pytorch를 이용하여 평가산식을 직접 구현 or scikit-learn을 이용하여 구현 이제 위의 과정들을 직접 코딩해가면서 진행해 본다. 환경설정 먼저 데이터를 불러오고, 모델 정의, 평가 등을 위한 라이브러리를 불러온다. import numpy as np import matplotlib.pyplot as plt from tqdm import tqdm import torch import torch.nn as nn import torch.optim as optim from torch.utils.data import DataLoader import torchvision import torchvision.transforms as T from sklearn.metrics import precision_score, recall_score, f1_score, roc_auc_score 또한, 일관된 훈련을 위한 시드를 고정시켜준다. import random import torch.backends.cudnn as cudnn def random_seed(seed_num) : torch.manual_seed(seed_num) torch.cuda.manual_seed(seed_num) torch.cuda.manual_seed_all(seed_num) cudnn.benchmark = False cudnn.deterministic = True random.seed(seed_num) random_seed(42) 데이터셋 0~9 까지 손글씨로 구성된 데이터셋을 이용하여 DNN을 구현한다. 먼저, 데이터를 불러와 Tensor 형식으로 변환해준다. mnist_transform = T.Compose([T.ToTensor(),]) download_root = './MNIST_DATASET' train_dataset = torchvision.datasets.MNIST(download_root, train=True, transform=mnist_transform, download=True) test_dataset = torchvision.datasets.MNIST(download_root, train=False, transform=mnist_transform, download=True) 데이터셋의 모양은 28*28의 이미지로 구성되어 있고, 채널이 1개이기에 gray-scale 이미지임을 알 수 있다. for image, label in train_dataset : print(image.shape, label) break torch.Size([1, 28, 28]) 5 전체 데이터셋에서 훈련셋과 검증셋을 나눠준다. total_size = len(train_dataset) train_num, valid_num = int(total_size * 0.8), int(total_size * 0.2) print("Train data size: ", train_num) print("Validation data size: ", valid_num) train_dataset, valid_dataset = torch.utils.data.random_split(train_dataset, [train_num, valid_num]) Train data size: 48000 Validation data size: 12000 DataLoader 정의 데이터셋을 텐서 형태로 변환해주었으므로, 지정한 미니 배치 단위로 데이터들을 묶어준다. batch_szie = 32 train_dataloader = DataLoader(train_dataset, batch_size=batch_szie, shuffle=True) valid_dataloader = DataLoader(valid_dataset, batch_size=batch_szie, shuffle=False) test_dataloader = DataLoader(test_dataset, batch_size=batch_szie, shuffle=False) 위에서 지정하였던 배치 크기가 32이므로 확인해보면 하나의 배치에 32개의 손글씨 데이터가 들어가게 된다. for images, labels in train_dataloader : print(images.shape, labels.shape) break torch.Size([32, 1, 28, 28]) torch.Size([32]) 모델 다음으로는 Custom Model을 정의한다. Fully connected layer를 선언하고, for문을 이용하여 은닉층의 개수만큼 반복시켜 다음과 같은 층을 정의한다. 또한, 이를 순전파로 연결시켜주어야 하므로 foward() 메서드 부분에서 이를 정의해준다. 그러면 다음과 같은 전체 구조를 가지는 모델이 완성된다. class DNN(nn.Module) : def __init__(self, hidden_dims, num_classes, dropout_ratio, apply_batchnorm, apply_dropout, apply_activation, set_super) : if set_super : super().__init__() self.hidden_dims = hidden_dims self.layers = nn.ModuleList() for i in range(len(self.hidden_dims)-1) : self.layers.append(nn.Linear(self.hidden_dims[i], self.hidden_dims[i+1])) if apply_batchnorm : self.layers.append(nn.BatchNorm1d(self.hidden_dims[i+1])) if apply_dropout : self.layers.append(nn.Dropout(dropout_ratio)) if apply_activation : self.layers.append(nn.ReLU()) self.classifier = nn.Linear(self.hidden_dims[-1], num_classes) self.softmax = nn.Softmax(dim=1) def forward(self, x) : x = x.view(x.size(0), -1) for layer in self.layers : x = layer(x) output = self.classifier(x) output = self.softmax(output) return output hidden_dim = 128 hidden_dims = [784, hidden_dim*4, hidden_dim*2, hidden_dim] model = DNN(hidden_dims=hidden_dims, num_classes=10, dropout_ratio=0.2, apply_batchnorm=True, apply_dropout=True, apply_activation=True, set_super=True) output = model(torch.randn((32, 1, 28, 28))) 파라미터 초기화 모델 훈련을 위한 가중치를 초기화한다. 여러가지 방법들이 존재하며, 아래의 사이트를 참고하여 상황에 맞는 가중치를 사용할 수 있다. PyTorch 가중치 def weight_initialization(model, weight_init_method) : for m in model.modules() : if isinstance(m, nn.Linear) : if weight_init_method == 'gaussian' : nn.init.normal_(m.weight) elif weight_init_method == 'xavier' : nn.init.xavier_normal_(m.weight) elif weight_init_method == 'kaiming' : nn.init.kaiming_normal_(m.weight) elif weight_init_method == 'zeros' : nn.init.zeros_(m.weight) nn.init.zeros_(m.bias) return model init_method = 'zeros' model = weight_initialization(model, init_method) for m in model.modules() : if isinstance(m, nn.Linear) : print(m.weight.data) break tensor([[0., 0., 0., ..., 0., 0., 0.], [0., 0., 0., ..., 0., 0., 0.], [0., 0., 0., ..., 0., 0., 0.], ..., [0., 0., 0., ..., 0., 0., 0.], [0., 0., 0., ..., 0., 0., 0.], [0., 0., 0., ..., 0., 0., 0.]]) 최종 모델 앞서 정의했던 순전파를 진행하는 모델과, 가중치를 초기화하는 메서드를 합쳐 하나의 클래스로 정의한다. class DNN(nn.Module) : def __init__(self, hidden_dims, num_classes, dropout_ratio, apply_batchnorm, apply_dropout, apply_activation, set_super) : if set_super : super().__init__() self.hidden_dims = hidden_dims self.layers = nn.ModuleList() for i in range(len(self.hidden_dims)-1) : self.layers.append(nn.Linear(self.hidden_dims[i], self.hidden_dims[i+1])) if apply_batchnorm : self.layers.append(nn.BatchNorm1d(self.hidden_dims[i+1])) if apply_dropout : self.layers.append(nn.Dropout(dropout_ratio)) if apply_activation : self.layers.append(nn.ReLU()) self.classifier = nn.Linear(self.hidden_dims[-1], num_classes) self.softmax = nn.Softmax(dim=1) def forward(self, x) : x = x.view(x.size(0), -1) for layer in self.layers : x = layer(x) output = self.classifier(x) output = self.softmax(output) return output def weight_initialization(model, weight_init_method) : for m in model.modules() : if isinstance(m, nn.Linear) : if weight_init_method == 'gaussian' : nn.init.normal_(m.weight) elif weight_init_method == 'xavier' : nn.init.xavier_normal_(m.weight) elif weight_init_method == 'kaiming' : nn.init.kaiming_normal_(m.weight) elif weight_init_method == 'zeros' : nn.init.zeros_(m.weight) nn.init.zeros_(m.bias) return model def count_parameters(self) : return sum(p.numel() for p in self.parameters()) model = DNN(hidden_dims=hidden_dims, num_classes=10, dropout_ratio=0.2, apply_batchnorm=True, apply_dropout=True, apply_activation=True, set_super=True) init_method = "gaussian" model.weight_initialization(init_method) DNN( (layers): ModuleList( (0): Linear(in_features=784, out_features=128, bias=True) (1): BatchNorm1d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (2): Dropout(p=0.2, inplace=False) (3): ReLU() (4): Linear(in_features=128, out_features=128, bias=True) (5): BatchNorm1d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (6): Dropout(p=0.2, inplace=False) (7): ReLU() (8): Linear(in_features=128, out_features=128, bias=True) (9): BatchNorm1d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (10): Dropout(p=0.2, inplace=False) (11): ReLU() ) (classifier): Linear(in_features=128, out_features=10, bias=True) (softmax): Softmax(dim=1) ) model.count_parameters() 135562 손실 함수 및 최적화 알고리즘 모델 정의가 되었으므로, 모델이 잘 훈련되는지 확인하기 위한 손실 함수와 이를 이용한 최적화 알고리즘을 정의한다. 먼저, 각 상황에 맞는 손실 함수를 찾아야 한다. 손실 함수 현재 실습은 손글씨 데이터를 label에 맞게 분류하는 것이므로 NLLLoss()를 사용한다. criterion = nn.NLLLoss() 최적화(optimization) 또한, 일반적으로는 Adam을 사용하지만, 상황에 따라 바꿀 수 있다. Optimizer lr = 0.001 hidden_dim = 128 hidden_dims = [784, hidden_dim, hidden_dim, hidden_dim] model = DNN(hidden_dims=hidden_dims, num_classes=10, dropout_ratio=0.2, apply_batchnorm=True, apply_dropout=True, apply_activation=True, set_super=True) optimizer = optim.Adam(model.parameters(), lr=lr) 이제 학습을 진행하는 코드를 작성한다. training() - GPU를 이용하여 학습 - 순전파 - 역전파 및 가중치 업데이트 - 손실값 및 정확도 계산 - 각 epoch마다의 학습 결과를 출력한다. evaluation() - GPU를 이용하여 평가 - 순전파 - 손실값 및 정확도 계산 training_loop() - Train 데이터로 훈련을 진행하고, valid 데이터로 훈련에 대한 검증 - valid 데이터에 대한 정확도가 이전보다 높다면 업데이트 - 손실값을 기준으로 이전보다 작아진다면 EarlyStopping def training(model, dataloader, train_dataset, criterion, optimizer, device, epoch, num_epochs) : model.train() train_loss = 0.0 train_accuracy = 0 tbar = tqdm(dataloader) for images, labels in tbar : images = images.to(device) labels = labels.to(device) outputs = model(images) loss = criterion(outputs, labels) optimizer.zero_grad() loss.backward() optimizer.step() train_loss += loss.item() _, predicted = torch.max(outputs, 1) train_accuracy += (predicted == labels).sum().item() tbar.set_description(f'Epoch {epoch+1}/{num_epochs}, Train_Loss: {train_loss:.4f}') train_loss /= len(dataloader) train_accuracy /= len(train_dataset) return model, train_loss, train_accuracy def evaluation(model, dataloader, valid_dataset, criterion, device, epoch, num_epochs) : model.eval() valid_loss = 0.0 valid_accuracy = 0 with torch.no_grad() : tbar = tqdm(dataloader) for images, labels in tbar : images = images.to(device) labels = labels.to(device) outputs = model(images) loss = criterion(outputs, labels) valid_loss += loss.item() _, predicted = torch.max(outputs, 1) valid_accuracy += (predicted == labels).sum().item() tbar.set_description(f'Epoch {epoch+1}/{num_epochs}, Valid_Loss: {valid_loss:.4f}') valid_loss /= len(dataloader) valid_accuracy /= len(valid_dataset) return model,valid_loss, valid_accuracy def training_loop(model, train_dataloader, valid_dataloader, criterion, optimizer, device, num_epochs, patience, model_name) : best_valid_loss = float('inf') early_stop_counter = 0 valid_max_accuracy = -1 for epoch in range(num_epochs) : model, train_loss, train_accuracy = training(model, train_dataloader, train_dataset, criterion, optimizer, device, epoch, num_epochs) model, valid_loss, valid_accuracy = evaluation(model, valid_dataloader, valid_dataset, criterion, device, epoch, num_epochs) if valid_accuracy &gt; valid_max_accuracy : valid_max_accuracy = valid_accuracy if valid_loss &lt; best_valid_loss : best_valid_loss = valid_loss torch.save(model.state_dict()i, f"./model_{model_name}.pt") else : early_stop_counter += 1 print(f"Epoch {epoch+1}/{num_epochs}, Train_Loss: {train_loss:.4f}, Train_Accuracy: {train_accuracy:.4f}, Valid_Loss: {valid_loss:.4f}, Valid_Accuracy: {valid_accuracy:.4f}") if early_stop_counter &gt;= patience : print("Early Stopping") break return model, valid_max_accuracy num_epochs = 100 patience = 3 scores = dict() device = 'cuda:0' model_name = "exp1" init_method = 'kaiming' model = DNN(hidden_dims=hidden_dims, num_classes=10, dropout_ratio=0.2, apply_batchnorm=True, apply_dropout=True, apply_activation=True, set_super=True) model.weight_initialization(init_method) model = model.to(device) criterion = nn.NLLLoss() optimizer = optim.Adam(model.parameters(), lr=lr) model, valid_max_accuracy = training_loop(model, train_dataloader, valid_dataloader, criterion, optimizer, device, num_epochs, patience, model_name) scores[model_name] = valid_max_accuracy Epoch 1/100, Train_Loss: -1254.1931: 100%|██████████| 1500/1500 [00:08&lt;00:00, 176.03it/s] Epoch 1/100, Valid_Loss: -350.4877: 100%|██████████| 375/375 [00:01&lt;00:00, 315.23it/s] Epoch 1/100, Train_Loss: -0.8361, Train_Accuracy: 0.8579, Valid_Loss: -0.9346, Valid_Accuracy: 0.9382 Epoch 2/100, Train_Loss: -1365.1130: 100%|██████████| 1500/1500 [00:08&lt;00:00, 177.13it/s] Epoch 2/100, Valid_Loss: -355.5040: 100%|██████████| 375/375 [00:01&lt;00:00, 324.88it/s] Epoch 2/100, Train_Loss: -0.9101, Train_Accuracy: 0.9151, Valid_Loss: -0.9480, Valid_Accuracy: 0.9503 Epoch 3/100, Train_Loss: -1385.8775: 100%|██████████| 1500/1500 [00:08&lt;00:00, 177.68it/s] Epoch 3/100, Valid_Loss: -357.5532: 100%|██████████| 375/375 [00:01&lt;00:00, 324.07it/s] Epoch 3/100, Train_Loss: -0.9239, Train_Accuracy: 0.9279, Valid_Loss: -0.9535, Valid_Accuracy: 0.9553 Epoch 4/100, Train_Loss: -1400.4983: 100%|██████████| 1500/1500 [00:08&lt;00:00, 177.52it/s] Epoch 4/100, Valid_Loss: -358.7989: 100%|██████████| 375/375 [00:01&lt;00:00, 340.53it/s] Epoch 4/100, Train_Loss: -0.9337, Train_Accuracy: 0.9376, Valid_Loss: -0.9568, Valid_Accuracy: 0.9581 Epoch 5/100, Train_Loss: -1405.4431: 100%|██████████| 1500/1500 [00:08&lt;00:00, 184.06it/s] Epoch 5/100, Valid_Loss: -359.9167: 100%|██████████| 375/375 [00:01&lt;00:00, 339.84it/s] Epoch 5/100, Train_Loss: -0.9370, Train_Accuracy: 0.9388, Valid_Loss: -0.9598, Valid_Accuracy: 0.9608 Epoch 6/100, Train_Loss: -1411.7580: 100%|██████████| 1500/1500 [00:07&lt;00:00, 190.95it/s] Epoch 6/100, Valid_Loss: -360.8559: 100%|██████████| 375/375 [00:01&lt;00:00, 319.57it/s] Epoch 6/100, Train_Loss: -0.9412, Train_Accuracy: 0.9433, Valid_Loss: -0.9623, Valid_Accuracy: 0.9634 Epoch 7/100, Train_Loss: -1417.0492: 100%|██████████| 1500/1500 [00:08&lt;00:00, 180.71it/s] Epoch 7/100, Valid_Loss: -361.1227: 100%|██████████| 375/375 [00:01&lt;00:00, 327.59it/s] Epoch 7/100, Train_Loss: -0.9447, Train_Accuracy: 0.9467, Valid_Loss: -0.9630, Valid_Accuracy: 0.9639 Epoch 8/100, Train_Loss: -1422.2407: 100%|██████████| 1500/1500 [00:08&lt;00:00, 181.93it/s] Epoch 8/100, Valid_Loss: -360.8939: 100%|██████████| 375/375 [00:01&lt;00:00, 323.78it/s] Epoch 8/100, Train_Loss: -0.9482, Train_Accuracy: 0.9498, Valid_Loss: -0.9624, Valid_Accuracy: 0.9626 Epoch 9/100, Train_Loss: -1422.8521: 100%|██████████| 1500/1500 [00:08&lt;00:00, 187.39it/s] Epoch 9/100, Valid_Loss: -360.7753: 100%|██████████| 375/375 [00:01&lt;00:00, 338.03it/s] Epoch 9/100, Train_Loss: -0.9486, Train_Accuracy: 0.9500, Valid_Loss: -0.9621, Valid_Accuracy: 0.9628 Epoch 10/100, Train_Loss: -1425.4209: 100%|██████████| 1500/1500 [00:08&lt;00:00, 184.94it/s] Epoch 10/100, Valid_Loss: -363.1457: 100%|██████████| 375/375 [00:01&lt;00:00, 341.63it/s] Epoch 10/100, Train_Loss: -0.9503, Train_Accuracy: 0.9517, Valid_Loss: -0.9684, Valid_Accuracy: 0.9695 Epoch 11/100, Train_Loss: -1429.4238: 100%|██████████| 1500/1500 [00:08&lt;00:00, 185.38it/s] Epoch 11/100, Valid_Loss: -362.8645: 100%|██████████| 375/375 [00:01&lt;00:00, 333.20it/s] Epoch 11/100, Train_Loss: -0.9529, Train_Accuracy: 0.9546, Valid_Loss: -0.9676, Valid_Accuracy: 0.9681 Early Stopping 추론과 평가 훈련시켰던 모델을 로컬경로에 저장한다. 필요 시 모델을 load_state_dict를 이용하여 로드한다. model = DNN(hidden_dims=hidden_dims, num_classes=10, dropout_ratio=0.2, apply_batchnorm=True, apply_dropout=True, apply_activation=True, set_super=True) model.load_state_dict(torch.load('./model_exp1.pt')) model = model.to(device) /tmp/ipykernel_19749/3655546662.py:2: FutureWarning: You are using `torch.load` with `weights_only=False` (the current default value), which uses the default pickle module implicitly. It is possible to construct malicious pickle data which will execute arbitrary code during unpickling (See https://github.com/pytorch/pytorch/blob/main/SECURITY.md#untrusted-models for more details). In a future release, the default value for `weights_only` will be flipped to `True`. This limits the functions that could be executed during unpickling. Arbitrary objects will no longer be allowed to be loaded via this mode unless they are explicitly allowlisted by the user via `torch.serialization.add_safe_globals`. We recommend you start setting `weights_only=True` for any use case where you don't have full control of the loaded file. Please open an issue on GitHub for any issues related to this experimental feature. model.load_state_dict(torch.load('./model_exp1.pt')) 모델을 평가 모드로 설정하고 미분 계산을 필요로 하지 않기 때문에 torch.no_grad()를 사용한다. total_preds : 모델이 예측한 값 total_labels : 실제 값 total_probs : AUC를 구하기 위한 모델이 각 클래스를 예측한 확률값 model.eval() total_labels = [] total_preds = [] total_probs = [] with torch.no_grad() : for images, labels in test_dataloader : images = images.to(device) labels = labels outputs = model(images) _, predicted = torch.max(outputs.data, 1) total_preds.extend(predicted.detach().cpu().tolist()) total_labels.extend(labels.tolist()) total_probs.append(outputs.detach().cpu().numpy()) total_preds = np.array(total_preds) total_labels = np.array(total_labels) total_probs = np.concatenate(total_probs, axis=0) precision = precision_score(total_labels, total_preds, average='macro') recall = recall_score(total_labels, total_preds, average='macro') f1 = f1_score(total_labels, total_preds, average='macro') auc = roc_auc_score(total_labels, total_probs, average='macro', multi_class='ovr') print(f"Precision: {precision}, Recall: {recall}, F1: {f1}, AUC: {auc}") Precision: 0.9711098052403464, Recall: 0.9708028372978227, F1: 0.9709088177178395, AUC: 0.9988558476539848 CNN 위와 같은 이미지 모델은 일반적으로 Convolution 연산을 통해 훈련한다. 아래와 같이 Convolution 연산을 수행하는 레이어를 정의하여 구현한다. nn.Conv2d(in_channels, out_channels, kernel_size, stride, padding) in_channels: 입력 이미지의 채널 수 out_channels: 출력 이미지의 채널 수 kernel_size: 필터의 크기 stride: 필터의 이동 단위 padding: 입력 이미지의 테두리에 추가되는 픽셀 수 주의 사항 층을 쌓을 때 in_channels의 값은 이미지의 채널과 맞춰주어야 한다. 예를 들어, RGB 이미지의 경우 “in_channels=3“이 되어야 한다. (gray-scale 이미지의 경우 “in_channels=1”) Pooling을 사용하지 않아도 되나, feature map의 크기를 줄여서 다음 레이어로 전달되는 데이터의 양을 감소시켜 메모리 사용량을 줄이고 계산 속도를 향상시킬 수 있다. 만약 Pooling을 사용하지 않는다면, 메모리 사용량이 크게 증가되어 서버가 다운될 수 있다. 앞서 nn.Linear()를 이용하여 구현할 때에는 ModuleList를 사용하여 정의하였지만, nn.Sequential()을 사용하여 순차적으로 처리하는 모듈을 사용하여 구현하면 된다. 위에 따라서 전체적인 CNN 모델의 구조를 작성해보면 다음과 같다. features = nn.Sequential( # 첫 번째 컨볼루션 블록 nn.Conv2d(3, 32, kernel_size=3, padding=1), nn.BatchNorm2d(32), nn.ReLU(inplace=True), nn.MaxPool2d(kernel_size=2, stride=2), # 두 번째 컨볼루션 블록 nn.Conv2d(32, 64, kernel_size=3, padding=1), nn.BatchNorm2d(64), nn.ReLU(inplace=True), nn.MaxPool2d(kernel_size=2, stride=2), # 세 번째 컨볼루션 블록 nn.Conv2d(64, 128, kernel_size=3, padding=1), nn.BatchNorm2d(128), nn.ReLU(inplace=True), nn.MaxPool2d(kernel_size=2, stride=2), ) # 분류 부분 (Fully connected layers) classifier = nn.Sequential( nn.Dropout(0.5), nn.Linear(128 * 4 * 4, 512), nn.ReLU(inplace=True), nn.Dropout(0.5), nn.Linear(512, 256), nn.ReLU(inplace=True), nn.Linear(256, num_classes) ) 위의 모델의 흐름을 파악해보면 다음과 같다. [BATCH_SIZE, 3, 32, 32] 크기의 입력 이미지(RGB 이미지)를 입력으로 받음 첫 번째 컨볼루션 블록에서 3개의 채널을 32로 확장, 3*3 필터로 특성을 추출, 패딩은 1이므로 이미지 그대로 유지 MaxPooling으로 크기가 절반으로 줄어듦 → [BATCH_SIZE, 32, 16, 16] 두 번째 컨볼루션 블록에서 32개의 채널을 64로 확장, 3*3 필터로 특성을 추출, 패딩은 1이므로 이미지 그대로 유지 MaxPooling으로 크기가 절반으로 줄어듦 → [BATCH_SIZE, 64, 8, 8] 세 번째 컨볼루션 블록에서 64개의 채널을 128로 확장, 3*3 필터로 특성을 추출, 패딩은 1이므로 이미지 그대로 유지 MaxPooling으로 크기가 절반으로 줄어듦 → [BATCH_SIZE, 128, 4, 4] Fully connected layer(분류 부분)에 입력으로 주기 위해 평탄화 (nn.Linear(128 * 4 * 4, 512)) Fully connected layer 추가 및 분류 실행 실제로 위와 같이 모델의 구조를 직접 구현하는 방법이 있지만 좋은 성능을 내는 모델을 구현하기 위해서는 많은 컴퓨터 리소스, 다양한 파라미터를 튜닝하고 실험할 수 있는 충분한 시간 등의 어려움이 존재한다. 이러한 이유로 오픈 소스로 공개되어 있는 다양한 모델들이 존재하는데, VGG, ResNet, GoogLeNet 등의 이미지 모델이 존재한다. 우리는 이러한 모델을 가져와서 Fine-Tunning하는 방식으로 특정 Task에 뛰어난 성능을 보이는 모델을 훈련시킬 수 있고, 실제로 이러한 방식이 많이 사용되고 있다. 다음은 다양한 이미지 관련 모델들을 제공하는 사이트들이다. HuggingFace ONNX Model Zoo TensorFlow Hub / Tensorflow 및 Keras와 연동 PyTorch Hub / PyTorch와 연동 RNN DNN은 각 layer의 출력을 다음 layer의 입력으로 받는 구조를 가지고 있다. 즉 지난 layer의 정보를 이용하지 않고 현재 layer에 들어온 정보만을 가지고 계산하게 되는데, RNN은 지난 layer의 정보를 이용하여 계산하게 된다. 이러한 방식은 위치 정보가 필요한 자연어와 같은 데이터를 처리할 때 유용하게 사용된다. 하지만 자연어와 같은 텍스트 데이터 그대로는 모델이 훈련할 수 없는 구조이기에 몇 가지 처리해주어야 하는데 Tokenizer로 텍스트 토큰화 Padding을 이용하여 동일한 크기의 데이터로 변환 먼저 토큰화의 경우 간단하게(띄어쓰기를 기준으로 생각하면) 단어별 숫자를 부여하여 토큰화를 진행할 수 있다. (현재 자연어 처리에 사용되는 토크나이저는 위의 방식보다 복잡하지만, 간단하게 생각하기로 한다.) “안녕하세요. 저는 딥러닝을 공부하고 있습니다.”와 같은 문장에서… 0 : 안녕하세요 1 : 저는 2 : 딥러닝을 … 과 같이 토큰화한다고 생각하면 된다. 다음으로 Padding은 크기를 맞추는 작업인데, 자연어의 경우 문장마다 텍스트의 길이가 다르다. “안녕하세요” VS “안녕” “안녕하세요“는 1단어, “안녕 반가워“은 2단어이므로 “안녕하세요”에 1 padding을 추가하여 맞춰준다. 위와 같은 방식으로 모든 텍스트의 입력 데이터 크기를 맞춰줌으로써 훈련을 진행할 수 있다. 구현 대표적인 순환 신경망 구조의 모델로는 아래 3개가 있다. torch.nn.RNN(input_size, hidden_size) torch.nn.LSTM(input_size, hidden_size) torch.nn.GRU(input_size, hidden_size) 위의 모듈을 통해 구현한다. input_size : 임베딩 차원 hidden_size : 은닉층 이를 통해 RNN 모델의 간단한 구조를 짜보면 다음과 같다. 텍스트가 긍정/부정으로 분류되는 모델을 구현한다고 가정했을 때, class RNNSequential(nn.Module): def __init__(self): super().__init__() self.embedding = nn.Embedding(vocab_size, embed_dim) self.rnn = nn.RNN(embed_dim, hidden_dim, batch_first=True) self.classifier = nn.Sequential( nn.Dropout(0.5), nn.Linear(hidden_dim, num_classes) ) def forward(self, x): x = self.embedding(x) output, (hidden, _) = self.rnn(x) last_output = output[:, -1, :] return self.classifier(last_output) 입력데이터의 크기는 [BATCH_SIZE, SEQUENCE_LENGTH], embed_dim은 Embedding → [BATCH_SIZE, SEQUENCE_LENGTH]로 들어온 입력을 [BATCH_SIZE, SEQUENCE_LENGTH, embed_dim] 변환 → 임베딩을 통해 토큰화를 통해 정수 인덱스로 변환된 텍스트를 벡터로 변환 RNN을 통과시켜 훈련, output과 (hidden, _)을 통해 이전 은닉층의 정보를 사용하여 문맥 파악 classifier를 통해 분류 작업 DNN과 다르게 포워딩 과정에서 출력값과, 이전 은닉층을 사용하기에 과거의 정보를 사용하여 위치 정보 및 시간적 정보를 처리할 수 있음을 시사한다. RNN, LSTM, GRU 일반적으로 RNN, LSTM, GRU 세 모델은 과거의 정보만을 이용하여 다음 정보를 예측하게 된다. 예를 들어 “나는 지금 __이야. 밥 먹는 중.” 라는 문장이 있다고 가정해보자. 이전 순환 신경망 구조에서는 과거의 정보만 이용했기에 “밥 먹는 중“이라는 정보를 알 수 없다. 이는 저 빈칸에 들어갈 말뭉치가 무엇인지 과거의 정보만을 가지고 예측할 수 없다. 식당, 집, 호텔 등등이 들어올 수 있고, 무엇이 들어가도 어색하지 않기에 모름 뒤의 정보를 이용하여 이를 추론해야함. 이런 점을 해결하기 위하여 미래의 정보도 이용하는 양방향 모델을 구현해야하는데 다음과 같이 인자를 True로 설정하여 구현한다. torch.nn.RNN(input_size, hidden_size, bidirectional=True) 자연어 처리의 텍스트 생성이라는 도메인에서는 이전 단어들만을 가지고 다음에 어떤 단어를 생성해야 할지 정확하게 예측할 수 없다. 앞뒤 문장의 구조를 파악하여 적절한 단어가 오도록 예측하는 과정이 필요, 양방향 모델로 구현한다. 평가 자연어 처리에 있어 특정 도메인(긍정/부정 분류 등)에서는 정확도와 같은 위에서 사용했던 평가지표를 사용할 수 있다. 실제로, 위의 예시에서는 정확도 지표를 사용하여 모델의 성능을 평가할 수 있다. 그러나, 일반적으로 번역이라던지, 텍스트 생성의 경우 정확도로 모델을 평가할 수 없다. “안녕하세요” 라는 한국어 문장을 “Hello”로 번역할 때, 만약 모델이 “Halo”라고 번역하였을 경우 과연 정확도로 평가할 수 있을까? Hello와 Halo는 언뜻 보기에는 비슷한 단어이지만, 의미는 완전히 다르기에 일반적인 평가지표를 가지고 사용할 수 없는데 이때 사용되는 평가지표가 cosine similarity, bleu score 등이 있다. 자연어 처리에 있어 가장 많이 사용되는 cosine similarity를 살펴보면 다음과 같이 구현된다. def cosine_similarity(vec1, vec2): # 텐서로 변환 if not isinstance(vec1, torch.Tensor): vec1 = torch.tensor(vec1, dtype=torch.float32) if not isinstance(vec2, torch.Tensor): vec2 = torch.tensor(vec2, dtype=torch.float32) # PyTorch의 cosine_similarity 함수 사용 similarity = F.cosine_similarity(vec1.unsqueeze(0), vec2.unsqueeze(0)) return similarity.item() 결과를 해석하면 1에 가까울수록 완전히 같은 방향(유사한 텍스트), 0에 가까울수록 수직(유사하지 않은 텍스트)을 의미한다.]]></summary></entry><entry><title type="html">Building Ethics into Artificial Intelligence</title><link href="https://leeinformation.github.io/Leeinformation.github.io/thesis/Ethics_AI/" rel="alternate" type="text/html" title="Building Ethics into Artificial Intelligence" /><published>2025-06-05T00:00:00+00:00</published><updated>2025-06-05T00:00:00+00:00</updated><id>https://leeinformation.github.io/Leeinformation.github.io/thesis/Ethics_AI</id><content type="html" xml:base="https://leeinformation.github.io/Leeinformation.github.io/thesis/Ethics_AI/"><![CDATA[<head>
  <style>
    table.dataframe {
      white-space: normal;
      width: 100%;
      height: 240px;
      display: block;
      overflow: auto;
      font-family: Arial, sans-serif;
      font-size: 0.9rem;
      line-height: 20px;
      text-align: center;
      border: 0px !important;
    }

    table.dataframe th {
      text-align: center;
      font-weight: bold;
      padding: 8px;
    }

    table.dataframe td {
      text-align: center;
      padding: 8px;
    }

    table.dataframe tr:hover {
      background: #b8d1f3; 
    }

    .output_prompt {
      overflow: auto;
      font-size: 0.9rem;
      line-height: 1.45;
      border-radius: 0.3rem;
      -webkit-overflow-scrolling: touch;
      padding: 0.8rem;
      margin-top: 0;
      margin-bottom: 15px;
      font: 1rem Consolas, "Liberation Mono", Menlo, Courier, monospace;
      color: $code-text-color;
      border: solid 1px $border-color;
      border-radius: 0.3rem;
      word-break: normal;
      white-space: pre;
    }

  .dataframe tbody tr th:only-of-type {
      vertical-align: middle;
  }

  .dataframe tbody tr th {
      vertical-align: top;
  }

  .dataframe thead th {
      text-align: center !important;
      padding: 8px;
  }

  .page__content p {
      margin: 0 0 0px !important;
  }

  .page__content p > strong {
    font-size: 0.8rem !important;
  }

  </style>
</head>

<h1 id="연구의-필요성-및-목적">연구의 필요성 및 목적</h1>

<p>AI 시스템이 일상생활에 보편화 되면서 <strong><em>AI 거버넌스</em></strong>, 즉 AI를 이용한 국정운영에 사람들의 관심이 증가하면서 AI의 윤리적 의사 결정에 대한 관심도 증가하고 있다.</p>

<p>하지만, 이는 AI 연구자들 사이에서는 아직 익숙치 않은 주제인데, 이는 기존에 연구들이 주로 심리학적, 사회적, 법적 측면에 초점을 맞추고 있고, 기술적 해결책에 대한 연구가 부족하기 때문이다.</p>

<p>이를 해결하기 위해 주요 AI 학회의 논문을 이용하여 AI 윤리적 의사 결정을 네 가지 영역으로 구분하는 분류법을 제안한다.</p>

<p>[정리]</p>

<ul>
  <li>논문의 저자가 해결하려는 문제</li>
</ul>

<blockquote>
  <p>AI 윤리적 의사 결정에 대한 관심이 증가했으나, AI 연구자 사이에서는 익숙치 않은 주제</p>
</blockquote>

<blockquote>

</blockquote>

<blockquote>
  <p>기존 연구가 주로 심리학적, 사회적, 법적 측면에 집중됨 → 기술적 해결책에 대한 연구가 부족</p>
</blockquote>

<ul>
  <li>논문의 저자가 제시하는 해결책</li>
</ul>

<blockquote>
  <p>AI 윤리적 의사 결정을 네 가지 영역으로 구분하는 분류법</p>
</blockquote>

<blockquote>

</blockquote>

<blockquote>
  <p>1) 윤리적 딜레마 탐구</p>
</blockquote>

<blockquote>

</blockquote>

<blockquote>
  <p>2) 개인 윤리적 의사결정 프레임워크</p>
</blockquote>

<blockquote>

</blockquote>

<blockquote>
  <p>3) 집단 윤리적 의사결정 프레임워크</p>
</blockquote>

<blockquote>

</blockquote>

<blockquote>
  <p>4) 인간-AI 상호작용에서의 윤리</p>
</blockquote>

<h1 id="윤리적-딜레마-탐구">윤리적 딜레마 탐구</h1>

<ul>
  <li>
    <p>윤리적으로 작동하는 AI 시스템 구축을 위한 첫 단계 : 목표 응용 시나리오에서의 윤리적 딜레마를 탐구하는 것</p>

    <blockquote>
      <p>특정 상황에서 AI가 무엇을 선택할지 파악하고 분석하는 것으로 해석</p>
    </blockquote>

    <blockquote>

    </blockquote>

    <blockquote>
      <p>ex) 바다에 노인과 어린이가 빠졌을 때 누구를 먼저 구해야 하는가?</p>
    </blockquote>
  </li>
  <li>
    <p>이를 위해 <strong>전문가 검토</strong>, <strong>크라우드 소싱 기반 소프트웨어 도구</strong>들이 등장</p>
  </li>
</ul>

<h2 id="geneth">GenEth</h2>

<ul>
  <li>
    <p>등장 배경 : 지능형 시스템과 관련된 윤리적 문제는 시스템 설계자의 이해를 넘어설 수 있음</p>

    <blockquote>
      <p>윤리학자들을 논의 과정에 포함시켜 애플리케이션 영역에서 윤리적 원칙을 체계화</p>
    </blockquote>
  </li>
  <li>
    <p>표현 스키마</p>

    <ul>
      <li>
        <p>Features : 요소의 존재 유무를 정수 값으로 표현</p>
      </li>
      <li>
        <p>Duties : 주어진 기능을 최소화/최대화 하기 위한 에이전트의 책임</p>
      </li>
      <li>
        <p>Actions : 특정 행동이 특정 의무를 충족하는지 위반하는지 튜플로 표현</p>
      </li>
      <li>
        <p>Cases : 윤리적 영향에 대한 행동 쌍을 비교</p>
      </li>
      <li>
        <p>Principles : 윤리적 선호도</p>
      </li>
    </ul>
  </li>
  <li>
    <p>특징</p>

    <ul>
      <li>
        <p>GUI 제공 : 윤리적 딜레마를 논의</p>
      </li>
      <li>
        <p>귀납 논리 프로그래밍 : 윤리적 행동 원칙 추론</p>
      </li>
    </ul>
  </li>
  <li>
    <p>정리</p>

    <blockquote>
      <p>GenEth는 <strong>전문가 검토(윤리학자)를 기반</strong>으로 윤리적 딜레마를 탐색하고, 귀납 논리 프로그래밍을 사용하여 윤리적 행동 원칙을 추론하는 시스템</p>
    </blockquote>
  </li>
</ul>

<h2 id="moral-machine-프로젝트">Moral Machine 프로젝트</h2>

<ul>
  <li>
    <p>접근법 : 군중의 지혜를 기반으로 윤리적 딜레마를 탐구</p>
  </li>
  <li>
    <p>프로젝트 목적 : AI로 제어되는 자동차 연구</p>
  </li>
  <li>
    <p>윤리적 관점에서의 인간 운전자 vs AI 운전자</p>

    <blockquote>
      <p>인간의 경우 주의를 기울였음에도 사고 발생 시 자기 보호 본능, 제한된 의사결정 시간으로 인해 다른 사람을 해치는 것에 대해 윤리적으로 비난 X</p>
    </blockquote>

    <blockquote>
      <p>AI의 경우 설계자의 여러가지의 사고 시나리오에서 의사결정을 위한 논리를 프로그래밍할 시간이 있기에 윤리는 AV에서 중요한 초점</p>
    </blockquote>
  </li>
  <li>
    <p>실험 설계</p>

    <blockquote>
      <p>고장난 AV가 맞닥뜨리는 다양한 윤리적 딜레마를 판단하고 선호하는 결과 선택, 또한 참가자가 직접 윤리적 딜레마를 설게하여 다른 사람들의 의견을 이끌어내는 UI 제공</p>
    </blockquote>

    <ul>
      <li>
        <p>참가자 : 300만명</p>
      </li>
      <li>
        <p>일반적 선호 : 사람들은 일반적으로 AV가 더 많은 생명을 구하는 방향으로 선호 (소수의 희생)</p>
      </li>
      <li>
        <p>이기주의적 패러독스 : AV가 탑승자를 죽임으로 더 많은 사람을 구한다면 자신의 AV 보다는 다른사람의 AV에 이런 로직이 있는 것을 선호</p>
      </li>
    </ul>
  </li>
  <li>
    <p>연구 한계점</p>

    <ul>
      <li>
        <p>자기 보고 선호도 한계 : 스스로 보고된 선호도는 실제 행동과 일치하지 않는 경우가 있음</p>

        <p>→ 이에 따라 실제 선택을 얼마나 반영하는지는 모름</p>
      </li>
    </ul>
  </li>
  <li>
    <p>해결 방안</p>

    <ul>
      <li>
        <p>무작위적 방식으로 결정 (운명에 맡김)</p>
      </li>
      <li>
        <p>AV를 인간 트래픽과 분리</p>
      </li>
    </ul>
  </li>
</ul>

<h2 id="정리">정리</h2>

<blockquote>
  <p>윤리적 딜레마를 탐구하기 위한 두 가지의 상반된 접근법을 통해 각 연구의 한계를 파악하고 AI 윤리 구현을 위한 방법론 제공, 향후 더 정교한 윤리적 의사결정 시스템 개발에 근간이 됨.</p>
</blockquote>

<h1 id="개인-윤리적-의사결정-프레임워크">개인 윤리적 의사결정 프레임워크</h1>

<ul>
  <li>
    <p>기본 전제</p>

    <blockquote>
      <p>AI 시스템의 윤리적 의사결정에는 일반화된 프레임워크를 선호</p>
    </blockquote>

    <blockquote>

    </blockquote>

    <blockquote>
      <p>윤리적 경계가 상황에 따라 달라질 수 있기에 <strong>규범</strong>이 필요</p>
    </blockquote>

    <blockquote>

    </blockquote>

    <blockquote>
      <p>인간이 업데이터를 제공할 경우 <strong>검토 메커니즘</strong> 필요(남용 방지)</p>
    </blockquote>
  </li>
</ul>

<hr />

<h2 id="moraldm-시스템">MoralDM 시스템</h2>

<ul>
  <li>
    <p>인간의 도덕적 의사결정은 공리주의적 고려사항뿐만 아니라 도덕적 규칙도 포함</p>

    <ul>
      <li>이러한 규칙들은 과거로부터 습득, 문화적으로 민감, 보호 가치(특정 행동을 도덕적으로 금지)가 포함</li>
    </ul>
  </li>
  <li>
    <p>핵심 메커니즘</p>

    <ul>
      <li>
        <p>제 1원리 추론 : 잘 확립된 윤리적 규칙에 기반한 의사 결정 (법)</p>
      </li>
      <li>
        <p>유추 추론 : 과거에 해결된 유사한 사례와 비교 (재판)</p>
      </li>
    </ul>
  </li>
  <li>
    <p>문제점 : 해결된 사례 수가 증가함에 따라 계산적으로 다루기가 힘듦</p>
  </li>
  <li>
    <p>해결책</p>

    <ul>
      <li>구조 매핑 : 사례들 사이 대응 관계, 후보 추론, 유사성 점수 계산 → 유추 일반화의 효율성 개선</li>
    </ul>
  </li>
</ul>

<hr />

<h2 id="bdi신념-욕구-의도-기반-윤리적-판단-프레임워크">BDI(신념-욕구-의도) 기반 윤리적 판단 프레임워크</h2>

<ul>
  <li>
    <p>시스템의 목적 : 에이전트가 다른 에이전트의 행동의 윤리성을 판단</p>
  </li>
  <li>
    <p>프로세스</p>

    <ol>
      <li>
        <p>인식 : 에이전트가 직면한 현재 상황과 목표를 설명하는 신념 생성</p>
      </li>
      <li>
        <p>평가 : 신념과 목표를 기반으로 가능한 행동들과 바람직한 행동들을 생성</p>
      </li>
      <li>
        <p>선함 : 에이전트의 신념, 욕구, 행동, 도덕적 가치 규칙을 기반으로 윤리적 행동들 계산</p>
      </li>
      <li>
        <p>정당성 : 현재 상황에서 가능한 행동의 실행이 옳은지를 평가하여 행동 선택</p>
      </li>
    </ol>
  </li>
  <li>
    <p>타 에이전트 판단 조건</p>

    <ul>
      <li>
        <p><strong>맹목적 윤리적 판단</strong> : 주어진 에이전트의 상태, 지식이 알려지지 않은 경우</p>
      </li>
      <li>
        <p><strong>부분적 정보 윤리적 판단</strong> : 주어진 에이전트의 상태, 지식에 대해 일부 정보가 있는 경우</p>
      </li>
      <li>
        <p><strong>완전 정보 윤리적 판단</strong> : 주어진 에이전트의 상태, 지식이 완전히 알려진 경우</p>
      </li>
    </ul>
  </li>
  <li>
    <p>한계점 : 어떤 행동이 정당 or 선함에서 얼마나 벗어나는지에 대한 정량정 측정 방법이 없음</p>
  </li>
</ul>

<hr />

<h2 id="게임-이론--머신러닝-기반-접근">게임 이론 &amp; 머신러닝 기반 접근</h2>

<ul>
  <li>
    <p>게임 이론 기반 프레임 워크</p>

    <ul>
      <li>
        <p>게임 이론 : 여러 선택지가 있을 때 최적의 결정을 내리는 수학적 방법</p>
      </li>
      <li>
        <p>딜레마를 표현하기 위해 광범위한 형태를 제안 → 확장형 게임 (의사결정을 트리로 표현)</p>

        <ul>
          <li>ex) 브레이크 고장 → <strong>직진(5명 사망) / 좌회전(1명 사망)</strong> 선택지가 존재할 때 이를 트리로 표현함</li>
        </ul>
      </li>
      <li>
        <p>이러한 방식은 어떤 행동이 과정 자체가 비윤리적인것으로 취급되는 보호된 가치를 고려하지 못함</p>

        <ul>
          <li>ex) 환자의 치료 → <strong>치료 A(실험) / 치료 B(표준)</strong>이 있을 때 치료 A가 생존율이 높더라도 환자의 동의없이 진행하는 것은 윤리적으로 금지됨</li>
        </ul>
      </li>
      <li>
        <p>수동적 행동을 추가하여 해결</p>

        <ul>
          <li>ex) 윤리적이기 위해 아무것도 하지 않는 선택지를 추가 → 다른 방법 모색</li>
        </ul>
      </li>
    </ul>
  </li>
</ul>

<hr />

<ul>
  <li>
    <p>머신러닝 기반 접근법</p>

    <ul>
      <li>
        <p>주어진 행동이 특정 상황에서 도덕적으로 옳은지 그른지를 분류</p>
      </li>
      <li>
        <p>요구사항 : 잘 라벨링된 데이터, 데이터 소스, 문화적 배경 등의 잠재적 불일치 고려</p>
      </li>
      <li>
        <p>주요 도전 과제</p>

        <ul>
          <li>
            <p>윤리적 딜레마를 일반화할 수 있는가?</p>
          </li>
          <li>
            <p>특정 상황에 대한 통찰을 바탕으로 어떤 특징을 식별하는 기존의 방식은 부족함(라벨링 등)</p>
          </li>
        </ul>
      </li>
      <li>
        <p>해결책 : 도덕적 기반 심리학적 프레임워크를 활용 (해로움/이로움, 공정/상호, 충성, 권위, 순수 등)</p>
      </li>
    </ul>
  </li>
</ul>

<hr />

<ul>
  <li>
    <p>통합 접근법</p>
  </li>
  <li>
    <p>윤리에 대한 게임 이론적 분석을 머신러닝 접근법 훈련의 특성으로 사용</p>
  </li>
  <li>
    <p>머신러닝이 게임이론에서 간과된 윤리적 측면을 식별하는데 사용</p>
  </li>
</ul>

<hr />

<h2 id="cp-net-기반-선호도-조정">CP-net 기반 선호도 조정</h2>

<ul>
  <li>
    <p>등장 배경</p>

    <ul>
      <li>
        <p>윤리적 요건은 AI 에이전트에게 외부적인 경우가 대부분</p>
      </li>
      <li>
        <p>윤리적 결정을 내리기 위해 <strong>윤리적 요건 + 에이전트의 내재적인 주관적 선호</strong>의 조화가 필요함</p>
      </li>
    </ul>
  </li>
  <li>
    <p>해결 접근법</p>

    <ul>
      <li>
        <p>외부 윤리 우선순위와 내재적인 주관적 선호를 표현</p>
      </li>
      <li>
        <p>CP-net 사이 거리 개념을 확립</p>

        <ul>
          <li>AI 에이전트가 윤리적 원칙에 충분히 가까우면 주관적 선호도 사용 가능</li>
        </ul>
      </li>
    </ul>
  </li>
</ul>

<hr />

<h2 id="고수준-액션-언어-프레임워크">고수준 액션 언어 프레임워크</h2>

<ul>
  <li>
    <p>기존 접근법</p>

    <ul>
      <li>
        <p>지금까지 검토된 프레임워크들은 윤리 판단의 체계화의 부담을 AI 시스템 개발자에게 전가</p>
      </li>
      <li>
        <p>따라서 윤리 판단의 정보를 개발 단계에서 AI 엔진에 통합 → 고수준의 액션 언어 제안</p>
      </li>
    </ul>
  </li>
  <li>
    <p>프로세스</p>

    <ol>
      <li>
        <p>정보 수집 : 행동, 이벤트 및 상황 정보를 수집</p>
      </li>
      <li>
        <p>시뮬레이션 : 수집된 정보로 다양한 행동 과정의 결과를 시뮬레이션</p>
      </li>
      <li>
        <p>인과 관계 추적</p>
      </li>
      <li>
        <p>결과 평가 : 윤리적 명세 및 윤리적 고려사항의 우선순위를 사용하여 정당성 평가</p>
      </li>
    </ol>
  </li>
  <li>
    <p>구현</p>

    <ul>
      <li><strong>답변 집합 프레임워크</strong> : 규칙 생성, 행동 설명, 타 에이전트 추론</li>
    </ul>
  </li>
</ul>

<hr />

<h2 id="강화학습에서의-윤리-통합">강화학습에서의 윤리 통합</h2>

<ul>
  <li>
    <p>접근법</p>

    <ul>
      <li>
        <p>보상 함수에 사전 지식을 통합하는 <strong>보상 형성 기법</strong>을 활용하여 윤리적 가치를 강화학습에 통합하는 윤리 형성 접근법 제안</p>
      </li>
      <li>
        <p>이를 통해 학습 과정 가속</p>
      </li>
    </ul>
  </li>
  <li>
    <p>방법론</p>

    <ul>
      <li>
        <p>관찰된 인간 행동의 대부분이 윤리적으로 가정</p>
      </li>
      <li>
        <p>주어진 영역 내에서 인간 행동의 데이터로부터 윤리적 형성 정책 학습</p>
      </li>
      <li>
        <p>윤리 형성 함수</p>

        <ul>
          <li>
            <p>긍정적 윤리 결정 → 보상</p>
          </li>
          <li>
            <p>부정적 윤리 결정 → 처벌</p>
          </li>
          <li>
            <p>윤리적 고려 대상 X → 중립 유지</p>
          </li>
        </ul>
      </li>
    </ul>
  </li>
</ul>

<blockquote>
  <p>윤리적 의사 결정에 정통하지 않아도(윤리학자와 같이) 윤리적 강화학습 시스템을 개발할 수 있도록 윤리를 코드화하는 부담을 경감</p>
</blockquote>

<hr />

<h2 id="정리-1">정리</h2>

<blockquote>
  <p>개인 윤리적 의사결정 프레임워크는 아래와 같이 공통적으로 발전하는 추세이다.</p>
</blockquote>

<ul>
  <li>
    <p>윤리 시스템 개발의 부담이 개발자 → 시스템으로 책임이 이전되는 추세</p>
  </li>
  <li>
    <p>다양한 이론들의 융합 (심리학, 게임이론, 머신러닝 등)</p>
  </li>
  <li>
    <p>이론 완성도 &lt; 실용성 (게임이론에서 아무것도 하지 않는 선택지와 같이)</p>
  </li>
  <li>
    <p>현재 주어진 상황에 맞게 유연한 프레임워크 (CP-net에서의 주관적 선호 사용 가능의 경우와 같이)</p>
  </li>
</ul>

<hr />

<h1 id="집단-윤리적-의사결정-프레임워크">집단 윤리적 의사결정 프레임워크</h1>

<ul>
  <li>개인 윤리적 의사결정의 문제 제기</li>
</ul>

<blockquote>
  <p>개별 에이전트가 윤리적으로 행동하고 다른 에이전트의 행동의 윤리성을 판단하는 것으로 인간 복지를 주요 관심사로 하며 잘 조정되고 협력적인 사회를 만들기에 충분한가?</p>
</blockquote>

<p>▶ 무언가 더 필요함</p>

<ul>
  <li>
    <p>필요한 추가 요소들</p>

    <ul>
      <li>
        <p>기본 규칙 : 사회적 규범을 관리</p>
      </li>
      <li>
        <p>보조 규칙 : 상황이 변함에 따라 기본 규칙을 생성, 수정, 삭제를 허용하는 규칙</p>
      </li>
    </ul>
  </li>
</ul>

<blockquote>
  <p>이를 위해 집단이 윤리적 행동을 선택하도록하는 프레임워크가 필요</p>
</blockquote>

<hr />

<h2 id="사회적-규범-기반-프레임워크">사회적 규범 기반 프레임워크</h2>

<ul>
  <li>
    <p>기본 전제</p>

    <ul>
      <li>
        <p>중앙 기관에 의존 X</p>
      </li>
      <li>
        <p>개인은 자신의 의사 결정 정책에 따라 실행</p>
      </li>
      <li>
        <p>단, 사회적 규범의 적용을 받아 잘못된 행동 시 처벌</p>
      </li>
    </ul>
  </li>
  <li>
    <p>사회적 규범 : 글로 표현된 약속, 승인, 금지, 제재 및 권한…</p>
  </li>
  <li>
    <p>신뢰 네트워크 : 개인들이 신뢰 기반 네트워크를 형성으로 상호작용을 통해 집단 자치 달성</p>
  </li>
</ul>

<hr />

<h2 id="인간-에이전트-집단-의사결정-프레임워크">인간 에이전트 집단 의사결정 프레임워크</h2>

<ul>
  <li>
    <p>아이디어 : 개별 에이전트에 윤리적 의사결정 메커니즘 부여 후 집단 차원에서 활용</p>
  </li>
  <li>
    <p>역할 분담</p>

    <ul>
      <li>
        <p>의무론적 윤리 에이전트 : 규칙과 의무에 기반(거짓말 혀용 X)</p>
      </li>
      <li>
        <p>결과주의 윤리 에이전트 : 결과에 기반(더 확률이 높은쪽으로 치료 결정)</p>
      </li>
      <li>
        <p>덕윤리 에이전트 : 품성과 덕목에 기반(용기있고 정의로운 행동)</p>
      </li>
    </ul>
  </li>
  <li>
    <p>학습 및 집계</p>

    <ul>
      <li>
        <p>규칙 학습 : 기본 윤리 규칙 → 학습을 통해 점차 복잡한 규칙 습득</p>
      </li>
      <li>
        <p>평가 : 역할 분담에서의 각 에이전트의 평가를 선호도 형태로 표현</p>
      </li>
      <li>
        <p>결정 : 선호도 집계 및 에이전트 투표를 활용</p>
      </li>
    </ul>
  </li>
  <li>
    <p>해결 과제</p>

    <ul>
      <li>
        <p>의사결정 : 행동들이 역할 분담에서의 에이전트 수보다 훨씬 많음</p>
      </li>
      <li>
        <p>일반적인 투표 : 후보자 &lt; 투표자</p>
      </li>
      <li>
        <p>독립성 X : 각 행동들이 서로 의존적</p>
      </li>
      <li>
        <p>공통 특성 : 일부 행동들은 윤리적 딜레마 상황 설명에 있어 특정 특성이 공유(겹침)</p>
      </li>
      <li>
        <p>불확실성 : 행동에 대한 선호정보가 누락될 수 있음(특정 행동에 대한 에이전트의 선호 정보 누락) → 불확실성 증가</p>
      </li>
    </ul>
  </li>
</ul>

<hr />

<h2 id="투표-기반-집단-윤리-결정-시스템">투표 기반 집단 윤리 결정 시스템</h2>

<ul>
  <li>
    <p>Moral Machine 프로젝트 데이터 활용</p>

    <ul>
      <li>
        <p>다양한 윤리적 딜레마 상황에서 자기보고 선호도 데이터 활용</p>
      </li>
      <li>
        <p>서로 다른 결과에 대한 인간의 선호 모델 학습</p>
      </li>
    </ul>
  </li>
  <li>
    <p>개별 모델 → 집단 모델</p>

    <ul>
      <li>
        <p>개별 선호 모델을 요약하여 집단 선호 모델 형성</p>
      </li>
      <li>
        <p>모든 투표자들의 집단적 선호를 근사</p>
      </li>
    </ul>
  </li>
  <li>
    <p>Swap-Dominance 개념 도입</p>

    <ul>
      <li>
        <p>대안들의 순위를 매길 때 사용하는 개념으로, 윤리적 선호 모델 형성에 활용</p>
      </li>
      <li>
        <p>어떤 결정을 내릴 때 요약된 모델로 집단 결정을 계산하고 최선의 결과를 도출(도출 과정에서 Swap-Dominance를 사용)</p>
      </li>
    </ul>
  </li>
  <li>
    <p>특징</p>

    <ul>
      <li>
        <p>결과주의적 : 최선의 결과를 도출하기에 집단의 결정을 추구 → 결과 중심</p>
      </li>
      <li>
        <p>계산 효율성 : Swap-Dominance를 사용하여 계산 효율성 향상</p>
      </li>
      <li>
        <p>데이터 기반 : 인간의 윤리적 선호 데이터에 기반 (경험적 데이터)</p>
      </li>
    </ul>
  </li>
</ul>

<hr />

<h2 id="정리-2">정리</h2>

<ul>
  <li>
    <p>중앙 집권적 → 분산형(개별) 의사결정으로 전환되는 추세</p>
  </li>
  <li>
    <p>복수의 윤리이론 통합 활용 (역할 분담과 같이)</p>
  </li>
  <li>
    <p>적응성 (머신러닝 모델을 이용하여 학습을 통해 발전)</p>
  </li>
  <li>
    <p>미해결 과제</p>

    <ul>
      <li>
        <p>선호도 표현 한계 : 복잡한 윤리적 판단을 선호도로 표현하는 것의 한계</p>
      </li>
      <li>
        <p>문화적 차이 : 서로 다른 문화에 있어 윤리적 합의가 필요</p>
      </li>
    </ul>
  </li>
  <li>
    <p>발전 방향</p>

    <ul>
      <li>
        <p>여러 프레임워크의 결합</p>
      </li>
      <li>
        <p>학습을 통한 발전 하는 윤리적 판단 능력</p>
      </li>
    </ul>
  </li>
</ul>

<hr />

<h1 id="인간-ai-상호작용에서의-윤리">인간-AI 상호작용에서의 윤리</h1>

<blockquote>
  <p>배경 : AI가 사람의 행동에 영향을 미치려는 분야에서 윤리 확보 필요</p>
</blockquote>

<ul>
  <li>
    <p>핵심 요구 사항</p>

    <ul>
      <li>
        <p>개인 자율성 보호 : 기술과의 상호작용에서 개인의 자유의지 보장 및 AI가 사람의 선택을 강요 및 조작 X</p>
      </li>
      <li>
        <p>위험-편익 균형 : 기술이 가져오는 편익 &gt; 위험 → 실질적 도움이 되어야 함</p>
      </li>
      <li>
        <p>공정한 분배 : 개인적 배경에 휘둘리지 않는, 사용자들 사이에서 공정하게 위험과 편익이 분배되어야 함</p>
      </li>
    </ul>
  </li>
  <li>
    <p>어려움</p>

    <ul>
      <li>
        <p>편익 및 위험 측정 (아마 AI가 도출하는 답이 어떻게 될지 모르기에)</p>

        <ul>
          <li>이를 해결하기 위해 인간 중심 가치의 계산적 공식화를 제안(집단 웰빙, 일-생활 균형)</li>
        </ul>
      </li>
    </ul>
  </li>
  <li>
    <p><strong>설득 에이전트 연구</strong></p>

    <blockquote>
      <p>AI의 설득에 대한 인간의 윤리적 인식을 조사하여 설득 전략의 효과성 및 윤리성을 평가</p>
    </blockquote>

    <ul>
      <li>
        <p>트롤리 딜레마</p>

        <blockquote>
          <p>기차 선로에 5명과 1명이 각각 놓여 있는 상황에서 ..</p>
        </blockquote>

        <ul>
          <li>
            <p>다수를 위해 한 사람을 적극적으로 해침</p>
          </li>
          <li>
            <p>결과주의 vs 보호 가치의 충돌</p>
          </li>
          <li>
            <p>참가자들의 능동적 참여</p>
          </li>
        </ul>
      </li>
      <li>
        <p>설득 전략</p>

        <ol>
          <li>
            <p>감정적 호소</p>
          </li>
          <li>
            <p>공리주의적 논증</p>
          </li>
          <li>
            <p>거짓말</p>
          </li>
        </ol>
      </li>
      <li>
        <p>결과</p>

        <ul>
          <li>
            <p>참가자들이 AI보다 강한 부정적 선입견 보유</p>

            <ul>
              <li>
                <p>인간은 AI에게 “기계 따위가, “AI가 인간의 생명을 논하다니..” 등</p>
              </li>
              <li>
                <p>AI는 인간에게 “맞는 판단인 것 같아요”, “어려운 결정이지만 고려해볼만 한 상황이네요” 등</p>
              </li>
            </ul>
          </li>
          <li>
            <p>이는 인간의 특성 상 설득하는데 있어 인간의 어법이 좀 더 설득적 → 효과적 측면에 있어 인간 &gt; AI</p>
          </li>
          <li>
            <p>전략별 효과 순위 : 공리주의적 논증 &gt; 거짓말 &gt; 감정적 호소</p>
          </li>
          <li>
            <p>윤리적 딜레마 상황에서 감정적 호소는 그닥 좋은 설득 전략 X</p>
          </li>
        </ul>
      </li>
    </ul>
  </li>
  <li>
    <p>감정 기반 윤리적 상호작용</p>

    <ul>
      <li>비록 감정적 호소가 설득에는 비효과적이나 윤리적으로 적절한 감정 반응은 인간과 AI의 상호작용을 향상시킬 수 있음</li>
    </ul>
  </li>
  <li>
    <p>대처 이론 기반 시스템</p>

    <ul>
      <li>AI 에이전트가 상황 평가를 변경하여 강한 부정적 감정을 처리(인간 5명을 살리는 쪽이 더 좋음을 가중치 등으로 평가)</li>
    </ul>
  </li>
  <li>
    <p>윤리적 감정 반응 메커니즘</p>

    <ol>
      <li>
        <p>자기 행동 평가 : AI가 자신의 행동의 윤리적 효과를 평가하여 도덕적 가치 위반 시 수치심 감정 발생</p>
      </li>
      <li>
        <p>타 에이전트 행동 평가 : 다른 에이전트가 도덕적 가치 위반 시 비난 감정 발생</p>
      </li>
    </ol>
  </li>
  <li>
    <p>시스템 작동 과정</p>

    <ol>
      <li>
        <p>윤리적 의사 결정 : 개별 윤리적 의사결정 프레임워크와 유사</p>
      </li>
      <li>
        <p>감정 반응 : 수치심 or 비난</p>
      </li>
      <li>
        <p>행동 조정 : 감정 반응을 통한 에이전트에게 암묵적 보상</p>
      </li>
    </ol>
  </li>
  <li>
    <p>특징</p>

    <ul>
      <li>
        <p>감정은 <strong>윤리적 학습 도구</strong> / <strong>자기 규제 및 사회적 조정</strong></p>
      </li>
      <li>
        <p>인간이 기대하는 윤리적 감정 반응을 구현하여 신뢰성 및 친밀감 향상</p>
      </li>
      <li>
        <p>감정 반응을 통해 지속적인 윤리적 판단 개선</p>
      </li>
    </ul>
  </li>
</ul>

<h2 id="정리-3">정리</h2>

<ul>
  <li>
    <p>인간은 AI 시스템에 선천적으로 불신</p>
  </li>
  <li>
    <p>감정을 통해 윤리적 추론을 더 정교하게 수행</p>
  </li>
</ul>

<p>인간-AI 상호작용에서 윤리 연구는 AI가 기능뿐만 아니라 인간의 가치 및 감정을 이해하는 과정을 학습하여 인간과의 상호작용을 더욱 자연스럽게 수행할 수 있도록 함</p>]]></content><author><name>Lee Jeong Min</name></author><category term="Thesis" /><category term="blog" /><category term="jekyll" /><category term="AI" /><summary type="html"><![CDATA[연구의 필요성 및 목적 AI 시스템이 일상생활에 보편화 되면서 AI 거버넌스, 즉 AI를 이용한 국정운영에 사람들의 관심이 증가하면서 AI의 윤리적 의사 결정에 대한 관심도 증가하고 있다. 하지만, 이는 AI 연구자들 사이에서는 아직 익숙치 않은 주제인데, 이는 기존에 연구들이 주로 심리학적, 사회적, 법적 측면에 초점을 맞추고 있고, 기술적 해결책에 대한 연구가 부족하기 때문이다. 이를 해결하기 위해 주요 AI 학회의 논문을 이용하여 AI 윤리적 의사 결정을 네 가지 영역으로 구분하는 분류법을 제안한다. [정리] 논문의 저자가 해결하려는 문제 AI 윤리적 의사 결정에 대한 관심이 증가했으나, AI 연구자 사이에서는 익숙치 않은 주제 기존 연구가 주로 심리학적, 사회적, 법적 측면에 집중됨 → 기술적 해결책에 대한 연구가 부족 논문의 저자가 제시하는 해결책 AI 윤리적 의사 결정을 네 가지 영역으로 구분하는 분류법 1) 윤리적 딜레마 탐구 2) 개인 윤리적 의사결정 프레임워크 3) 집단 윤리적 의사결정 프레임워크 4) 인간-AI 상호작용에서의 윤리 윤리적 딜레마 탐구 윤리적으로 작동하는 AI 시스템 구축을 위한 첫 단계 : 목표 응용 시나리오에서의 윤리적 딜레마를 탐구하는 것 특정 상황에서 AI가 무엇을 선택할지 파악하고 분석하는 것으로 해석 ex) 바다에 노인과 어린이가 빠졌을 때 누구를 먼저 구해야 하는가? 이를 위해 전문가 검토, 크라우드 소싱 기반 소프트웨어 도구들이 등장 GenEth 등장 배경 : 지능형 시스템과 관련된 윤리적 문제는 시스템 설계자의 이해를 넘어설 수 있음 윤리학자들을 논의 과정에 포함시켜 애플리케이션 영역에서 윤리적 원칙을 체계화 표현 스키마 Features : 요소의 존재 유무를 정수 값으로 표현 Duties : 주어진 기능을 최소화/최대화 하기 위한 에이전트의 책임 Actions : 특정 행동이 특정 의무를 충족하는지 위반하는지 튜플로 표현 Cases : 윤리적 영향에 대한 행동 쌍을 비교 Principles : 윤리적 선호도 특징 GUI 제공 : 윤리적 딜레마를 논의 귀납 논리 프로그래밍 : 윤리적 행동 원칙 추론 정리 GenEth는 전문가 검토(윤리학자)를 기반으로 윤리적 딜레마를 탐색하고, 귀납 논리 프로그래밍을 사용하여 윤리적 행동 원칙을 추론하는 시스템 Moral Machine 프로젝트 접근법 : 군중의 지혜를 기반으로 윤리적 딜레마를 탐구 프로젝트 목적 : AI로 제어되는 자동차 연구 윤리적 관점에서의 인간 운전자 vs AI 운전자 인간의 경우 주의를 기울였음에도 사고 발생 시 자기 보호 본능, 제한된 의사결정 시간으로 인해 다른 사람을 해치는 것에 대해 윤리적으로 비난 X AI의 경우 설계자의 여러가지의 사고 시나리오에서 의사결정을 위한 논리를 프로그래밍할 시간이 있기에 윤리는 AV에서 중요한 초점 실험 설계 고장난 AV가 맞닥뜨리는 다양한 윤리적 딜레마를 판단하고 선호하는 결과 선택, 또한 참가자가 직접 윤리적 딜레마를 설게하여 다른 사람들의 의견을 이끌어내는 UI 제공 참가자 : 300만명 일반적 선호 : 사람들은 일반적으로 AV가 더 많은 생명을 구하는 방향으로 선호 (소수의 희생) 이기주의적 패러독스 : AV가 탑승자를 죽임으로 더 많은 사람을 구한다면 자신의 AV 보다는 다른사람의 AV에 이런 로직이 있는 것을 선호 연구 한계점 자기 보고 선호도 한계 : 스스로 보고된 선호도는 실제 행동과 일치하지 않는 경우가 있음 → 이에 따라 실제 선택을 얼마나 반영하는지는 모름 해결 방안 무작위적 방식으로 결정 (운명에 맡김) AV를 인간 트래픽과 분리 정리 윤리적 딜레마를 탐구하기 위한 두 가지의 상반된 접근법을 통해 각 연구의 한계를 파악하고 AI 윤리 구현을 위한 방법론 제공, 향후 더 정교한 윤리적 의사결정 시스템 개발에 근간이 됨. 개인 윤리적 의사결정 프레임워크 기본 전제 AI 시스템의 윤리적 의사결정에는 일반화된 프레임워크를 선호 윤리적 경계가 상황에 따라 달라질 수 있기에 규범이 필요 인간이 업데이터를 제공할 경우 검토 메커니즘 필요(남용 방지) MoralDM 시스템 인간의 도덕적 의사결정은 공리주의적 고려사항뿐만 아니라 도덕적 규칙도 포함 이러한 규칙들은 과거로부터 습득, 문화적으로 민감, 보호 가치(특정 행동을 도덕적으로 금지)가 포함 핵심 메커니즘 제 1원리 추론 : 잘 확립된 윤리적 규칙에 기반한 의사 결정 (법) 유추 추론 : 과거에 해결된 유사한 사례와 비교 (재판) 문제점 : 해결된 사례 수가 증가함에 따라 계산적으로 다루기가 힘듦 해결책 구조 매핑 : 사례들 사이 대응 관계, 후보 추론, 유사성 점수 계산 → 유추 일반화의 효율성 개선 BDI(신념-욕구-의도) 기반 윤리적 판단 프레임워크 시스템의 목적 : 에이전트가 다른 에이전트의 행동의 윤리성을 판단 프로세스 인식 : 에이전트가 직면한 현재 상황과 목표를 설명하는 신념 생성 평가 : 신념과 목표를 기반으로 가능한 행동들과 바람직한 행동들을 생성 선함 : 에이전트의 신념, 욕구, 행동, 도덕적 가치 규칙을 기반으로 윤리적 행동들 계산 정당성 : 현재 상황에서 가능한 행동의 실행이 옳은지를 평가하여 행동 선택 타 에이전트 판단 조건 맹목적 윤리적 판단 : 주어진 에이전트의 상태, 지식이 알려지지 않은 경우 부분적 정보 윤리적 판단 : 주어진 에이전트의 상태, 지식에 대해 일부 정보가 있는 경우 완전 정보 윤리적 판단 : 주어진 에이전트의 상태, 지식이 완전히 알려진 경우 한계점 : 어떤 행동이 정당 or 선함에서 얼마나 벗어나는지에 대한 정량정 측정 방법이 없음 게임 이론 &amp; 머신러닝 기반 접근 게임 이론 기반 프레임 워크 게임 이론 : 여러 선택지가 있을 때 최적의 결정을 내리는 수학적 방법 딜레마를 표현하기 위해 광범위한 형태를 제안 → 확장형 게임 (의사결정을 트리로 표현) ex) 브레이크 고장 → 직진(5명 사망) / 좌회전(1명 사망) 선택지가 존재할 때 이를 트리로 표현함 이러한 방식은 어떤 행동이 과정 자체가 비윤리적인것으로 취급되는 보호된 가치를 고려하지 못함 ex) 환자의 치료 → 치료 A(실험) / 치료 B(표준)이 있을 때 치료 A가 생존율이 높더라도 환자의 동의없이 진행하는 것은 윤리적으로 금지됨 수동적 행동을 추가하여 해결 ex) 윤리적이기 위해 아무것도 하지 않는 선택지를 추가 → 다른 방법 모색 머신러닝 기반 접근법 주어진 행동이 특정 상황에서 도덕적으로 옳은지 그른지를 분류 요구사항 : 잘 라벨링된 데이터, 데이터 소스, 문화적 배경 등의 잠재적 불일치 고려 주요 도전 과제 윤리적 딜레마를 일반화할 수 있는가? 특정 상황에 대한 통찰을 바탕으로 어떤 특징을 식별하는 기존의 방식은 부족함(라벨링 등) 해결책 : 도덕적 기반 심리학적 프레임워크를 활용 (해로움/이로움, 공정/상호, 충성, 권위, 순수 등) 통합 접근법 윤리에 대한 게임 이론적 분석을 머신러닝 접근법 훈련의 특성으로 사용 머신러닝이 게임이론에서 간과된 윤리적 측면을 식별하는데 사용 CP-net 기반 선호도 조정 등장 배경 윤리적 요건은 AI 에이전트에게 외부적인 경우가 대부분 윤리적 결정을 내리기 위해 윤리적 요건 + 에이전트의 내재적인 주관적 선호의 조화가 필요함 해결 접근법 외부 윤리 우선순위와 내재적인 주관적 선호를 표현 CP-net 사이 거리 개념을 확립 AI 에이전트가 윤리적 원칙에 충분히 가까우면 주관적 선호도 사용 가능 고수준 액션 언어 프레임워크 기존 접근법 지금까지 검토된 프레임워크들은 윤리 판단의 체계화의 부담을 AI 시스템 개발자에게 전가 따라서 윤리 판단의 정보를 개발 단계에서 AI 엔진에 통합 → 고수준의 액션 언어 제안 프로세스 정보 수집 : 행동, 이벤트 및 상황 정보를 수집 시뮬레이션 : 수집된 정보로 다양한 행동 과정의 결과를 시뮬레이션 인과 관계 추적 결과 평가 : 윤리적 명세 및 윤리적 고려사항의 우선순위를 사용하여 정당성 평가 구현 답변 집합 프레임워크 : 규칙 생성, 행동 설명, 타 에이전트 추론 강화학습에서의 윤리 통합 접근법 보상 함수에 사전 지식을 통합하는 보상 형성 기법을 활용하여 윤리적 가치를 강화학습에 통합하는 윤리 형성 접근법 제안 이를 통해 학습 과정 가속 방법론 관찰된 인간 행동의 대부분이 윤리적으로 가정 주어진 영역 내에서 인간 행동의 데이터로부터 윤리적 형성 정책 학습 윤리 형성 함수 긍정적 윤리 결정 → 보상 부정적 윤리 결정 → 처벌 윤리적 고려 대상 X → 중립 유지 윤리적 의사 결정에 정통하지 않아도(윤리학자와 같이) 윤리적 강화학습 시스템을 개발할 수 있도록 윤리를 코드화하는 부담을 경감 정리 개인 윤리적 의사결정 프레임워크는 아래와 같이 공통적으로 발전하는 추세이다. 윤리 시스템 개발의 부담이 개발자 → 시스템으로 책임이 이전되는 추세 다양한 이론들의 융합 (심리학, 게임이론, 머신러닝 등) 이론 완성도 &lt; 실용성 (게임이론에서 아무것도 하지 않는 선택지와 같이) 현재 주어진 상황에 맞게 유연한 프레임워크 (CP-net에서의 주관적 선호 사용 가능의 경우와 같이) 집단 윤리적 의사결정 프레임워크 개인 윤리적 의사결정의 문제 제기 개별 에이전트가 윤리적으로 행동하고 다른 에이전트의 행동의 윤리성을 판단하는 것으로 인간 복지를 주요 관심사로 하며 잘 조정되고 협력적인 사회를 만들기에 충분한가? ▶ 무언가 더 필요함 필요한 추가 요소들 기본 규칙 : 사회적 규범을 관리 보조 규칙 : 상황이 변함에 따라 기본 규칙을 생성, 수정, 삭제를 허용하는 규칙 이를 위해 집단이 윤리적 행동을 선택하도록하는 프레임워크가 필요 사회적 규범 기반 프레임워크 기본 전제 중앙 기관에 의존 X 개인은 자신의 의사 결정 정책에 따라 실행 단, 사회적 규범의 적용을 받아 잘못된 행동 시 처벌 사회적 규범 : 글로 표현된 약속, 승인, 금지, 제재 및 권한… 신뢰 네트워크 : 개인들이 신뢰 기반 네트워크를 형성으로 상호작용을 통해 집단 자치 달성 인간 에이전트 집단 의사결정 프레임워크 아이디어 : 개별 에이전트에 윤리적 의사결정 메커니즘 부여 후 집단 차원에서 활용 역할 분담 의무론적 윤리 에이전트 : 규칙과 의무에 기반(거짓말 혀용 X) 결과주의 윤리 에이전트 : 결과에 기반(더 확률이 높은쪽으로 치료 결정) 덕윤리 에이전트 : 품성과 덕목에 기반(용기있고 정의로운 행동) 학습 및 집계 규칙 학습 : 기본 윤리 규칙 → 학습을 통해 점차 복잡한 규칙 습득 평가 : 역할 분담에서의 각 에이전트의 평가를 선호도 형태로 표현 결정 : 선호도 집계 및 에이전트 투표를 활용 해결 과제 의사결정 : 행동들이 역할 분담에서의 에이전트 수보다 훨씬 많음 일반적인 투표 : 후보자 &lt; 투표자 독립성 X : 각 행동들이 서로 의존적 공통 특성 : 일부 행동들은 윤리적 딜레마 상황 설명에 있어 특정 특성이 공유(겹침) 불확실성 : 행동에 대한 선호정보가 누락될 수 있음(특정 행동에 대한 에이전트의 선호 정보 누락) → 불확실성 증가 투표 기반 집단 윤리 결정 시스템 Moral Machine 프로젝트 데이터 활용 다양한 윤리적 딜레마 상황에서 자기보고 선호도 데이터 활용 서로 다른 결과에 대한 인간의 선호 모델 학습 개별 모델 → 집단 모델 개별 선호 모델을 요약하여 집단 선호 모델 형성 모든 투표자들의 집단적 선호를 근사 Swap-Dominance 개념 도입 대안들의 순위를 매길 때 사용하는 개념으로, 윤리적 선호 모델 형성에 활용 어떤 결정을 내릴 때 요약된 모델로 집단 결정을 계산하고 최선의 결과를 도출(도출 과정에서 Swap-Dominance를 사용) 특징 결과주의적 : 최선의 결과를 도출하기에 집단의 결정을 추구 → 결과 중심 계산 효율성 : Swap-Dominance를 사용하여 계산 효율성 향상 데이터 기반 : 인간의 윤리적 선호 데이터에 기반 (경험적 데이터) 정리 중앙 집권적 → 분산형(개별) 의사결정으로 전환되는 추세 복수의 윤리이론 통합 활용 (역할 분담과 같이) 적응성 (머신러닝 모델을 이용하여 학습을 통해 발전) 미해결 과제 선호도 표현 한계 : 복잡한 윤리적 판단을 선호도로 표현하는 것의 한계 문화적 차이 : 서로 다른 문화에 있어 윤리적 합의가 필요 발전 방향 여러 프레임워크의 결합 학습을 통한 발전 하는 윤리적 판단 능력 인간-AI 상호작용에서의 윤리 배경 : AI가 사람의 행동에 영향을 미치려는 분야에서 윤리 확보 필요 핵심 요구 사항 개인 자율성 보호 : 기술과의 상호작용에서 개인의 자유의지 보장 및 AI가 사람의 선택을 강요 및 조작 X 위험-편익 균형 : 기술이 가져오는 편익 &gt; 위험 → 실질적 도움이 되어야 함 공정한 분배 : 개인적 배경에 휘둘리지 않는, 사용자들 사이에서 공정하게 위험과 편익이 분배되어야 함 어려움 편익 및 위험 측정 (아마 AI가 도출하는 답이 어떻게 될지 모르기에) 이를 해결하기 위해 인간 중심 가치의 계산적 공식화를 제안(집단 웰빙, 일-생활 균형) 설득 에이전트 연구 AI의 설득에 대한 인간의 윤리적 인식을 조사하여 설득 전략의 효과성 및 윤리성을 평가 트롤리 딜레마 기차 선로에 5명과 1명이 각각 놓여 있는 상황에서 .. 다수를 위해 한 사람을 적극적으로 해침 결과주의 vs 보호 가치의 충돌 참가자들의 능동적 참여 설득 전략 감정적 호소 공리주의적 논증 거짓말 결과 참가자들이 AI보다 강한 부정적 선입견 보유 인간은 AI에게 “기계 따위가, “AI가 인간의 생명을 논하다니..” 등 AI는 인간에게 “맞는 판단인 것 같아요”, “어려운 결정이지만 고려해볼만 한 상황이네요” 등 이는 인간의 특성 상 설득하는데 있어 인간의 어법이 좀 더 설득적 → 효과적 측면에 있어 인간 &gt; AI 전략별 효과 순위 : 공리주의적 논증 &gt; 거짓말 &gt; 감정적 호소 윤리적 딜레마 상황에서 감정적 호소는 그닥 좋은 설득 전략 X 감정 기반 윤리적 상호작용 비록 감정적 호소가 설득에는 비효과적이나 윤리적으로 적절한 감정 반응은 인간과 AI의 상호작용을 향상시킬 수 있음 대처 이론 기반 시스템 AI 에이전트가 상황 평가를 변경하여 강한 부정적 감정을 처리(인간 5명을 살리는 쪽이 더 좋음을 가중치 등으로 평가) 윤리적 감정 반응 메커니즘 자기 행동 평가 : AI가 자신의 행동의 윤리적 효과를 평가하여 도덕적 가치 위반 시 수치심 감정 발생 타 에이전트 행동 평가 : 다른 에이전트가 도덕적 가치 위반 시 비난 감정 발생 시스템 작동 과정 윤리적 의사 결정 : 개별 윤리적 의사결정 프레임워크와 유사 감정 반응 : 수치심 or 비난 행동 조정 : 감정 반응을 통한 에이전트에게 암묵적 보상 특징 감정은 윤리적 학습 도구 / 자기 규제 및 사회적 조정 인간이 기대하는 윤리적 감정 반응을 구현하여 신뢰성 및 친밀감 향상 감정 반응을 통해 지속적인 윤리적 판단 개선 정리 인간은 AI 시스템에 선천적으로 불신 감정을 통해 윤리적 추론을 더 정교하게 수행 인간-AI 상호작용에서 윤리 연구는 AI가 기능뿐만 아니라 인간의 가치 및 감정을 이해하는 과정을 학습하여 인간과의 상호작용을 더욱 자연스럽게 수행할 수 있도록 함]]></summary></entry><entry><title type="html">[AI 부트캠프] ML 경진대회 - 아파트 실거래가 예측</title><link href="https://leeinformation.github.io/Leeinformation.github.io/bootcamp/ML_congress/" rel="alternate" type="text/html" title="[AI 부트캠프] ML 경진대회 - 아파트 실거래가 예측" /><published>2025-05-19T00:00:00+00:00</published><updated>2025-05-19T00:00:00+00:00</updated><id>https://leeinformation.github.io/Leeinformation.github.io/bootcamp/ML_congress</id><content type="html" xml:base="https://leeinformation.github.io/Leeinformation.github.io/bootcamp/ML_congress/"><![CDATA[<head>
  <style>
    table.dataframe {
      white-space: normal;
      width: 100%;
      height: 240px;
      display: block;
      overflow: auto;
      font-family: Arial, sans-serif;
      font-size: 0.9rem;
      line-height: 20px;
      text-align: center;
      border: 0px !important;
    }

    table.dataframe th {
      text-align: center;
      font-weight: bold;
      padding: 8px;
    }

    table.dataframe td {
      text-align: center;
      padding: 8px;
    }

    table.dataframe tr:hover {
      background: #b8d1f3; 
    }

    .output_prompt {
      overflow: auto;
      font-size: 0.9rem;
      line-height: 1.45;
      border-radius: 0.3rem;
      -webkit-overflow-scrolling: touch;
      padding: 0.8rem;
      margin-top: 0;
      margin-bottom: 15px;
      font: 1rem Consolas, "Liberation Mono", Menlo, Courier, monospace;
      color: $code-text-color;
      border: solid 1px $border-color;
      border-radius: 0.3rem;
      word-break: normal;
      white-space: pre;
    }

  .dataframe tbody tr th:only-of-type {
      vertical-align: middle;
  }

  .dataframe tbody tr th {
      vertical-align: top;
  }

  .dataframe thead th {
      text-align: center !important;
      padding: 8px;
  }

  .page__content p {
      margin: 0 0 0px !important;
  }

  .page__content p > strong {
    font-size: 0.8rem !important;
  }

  </style>
</head>

<h1 id="1-경진대회-개요">1. 경진대회 개요</h1>

<hr />

<p>5월이 시작되고, 서울의 아파트 실거래가를 예측하는 경진대회가 시작되었다.</p>

<p>첫 회의 때 서로가 알고있던 도메인을 공유하여 주어진 데이터 내에서 feature들의 중요도를 파악하고, 추가적으로 어떤 데이터를 추가하여 모델의 성능을 향상시킬 수 있는지에 대해 논의하였다.</p>

<p>이전 여러 경진대회에서 경험했던 바와 같이 물론 직접 훈련에 들어갔을 때 우리가 중요하게 생각했던 feature가 필요성이 떨어질 수 있고, 반대의 경우도 가능했기에 전체적으로 어떻게 경진대회를 진행할지에 대해서 논의하였다.</p>

<hr />

<h1 id="2-eda">2. EDA</h1>

<hr />

<p>머신러닝 파이프라인에 있어 가장 어렵고 중요한 부분이라고 생각하는 파트이다.</p>

<p>주어진 데이터를 시각화 or 통계적으로 분석하여 변수들간의 상관관계를 파악하고, 파생 변수 생성 및 필요 없는 변수 제거 등을 수행하는데 근거가 되기에 각 feature가 어떤 분포를 가지는지 확인하는데 집중했다.</p>

<p>데이터를 전체적으로 살펴보았을 때, 굉장히 결측치가 많았다.</p>

<p><img src="https://github.com/lIllIlIIIll/Leeinformation.github.io/blob/master/_posts/image/Bootcamp/train_missing.png?raw=true" alt="image.png" /></p>

<p><img src="https://github.com/lIllIlIIIll/Leeinformation.github.io/blob/master/_posts/image/Bootcamp/test_missing.png?raw=true" alt="image.png" /></p>

<p>전체 52개의 feature 중 5~6개를 제외하면 70% 이상의 결측치를 가졌고, 심지어 각 feature를 target과 비교하였을 때 이상치로 구분되는 값도 많았다.</p>

<p>또한, train 데이터와 test 데이터의 분포가 너무 달랐다.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>- train 데이터 : 근 30년간의 아파트 거래가격을 나타냄



- test 데이터 : 2023년의 아파트 거래가격을 나타냄
</code></pre></div></div>

<blockquote>
  <p>이는 물가, 경제 상황 등의 외부적 요인에 따라 <strong>target(금액)</strong>의 가치가 달랐다.</p>
</blockquote>

<blockquote>

</blockquote>

<blockquote>
  <p>검증셋을 2023년 데이터만으로 구성하는 방법을 사용 (물론 나중 모델 훈련시에는 전체를 검증셋으로 사용하여 RMSE를 낮추는 방향으로 진행하는 것이 더 좋았다.)</p>
</blockquote>

<hr />

<p>위에서 기술하였듯이 결측치가 많았기에 52개의 feature를 전부 사용하는 것은 모델 훈련에 있어 성능을 저하시킨다고 판단하였고, 실제로 데이터를 전처리 시키기 전, 정말 필요없다고 생각하는 feature들을 제거하였다.</p>

<p>그리하여 52개의 feature 중 남긴 feature는 다음과 같았다.</p>

<blockquote>
  <p>전용면적(㎡), 건축년도, 해제사유발생일, k-연면적, k-전용면적별세대현황(60㎡~85㎡이하),k-전용면적별세대현황(85㎡~135㎡이하), k-전체세대수, 주차대수, 계약(연), 좌표X, 좌표Y, 아파트명, 도로명, 등기신청일자, k-복도유형, k-단지분류(아파트,주상복합등등)</p>
</blockquote>

<hr />

<h1 id="3-preprocessing">3. Preprocessing</h1>

<hr />

<p>앞서 진행하였던 <strong>EDA</strong>를 기반으로 전처리를 진행하였다.</p>

<ul>
  <li>
    <p>train 데이터</p>

    <ul>
      <li>
        <p>중요도가 떨어진다고 생각하는 특성 Drop</p>
      </li>
      <li>
        <p>문자열 columns을 찾아서 좌우 공백 제거(같은 데이터인데 다르게 해석할 가능성 제거)</p>
      </li>
      <li>
        <p>데이터 중 중복으로 같은 값을 나타내는 feature Drop (번지, 본번, 부번)</p>
      </li>
      <li>
        <p><strong>계약년월</strong> → <strong>계약(연), 계약(월)</strong> 로 분해</p>
      </li>
      <li>
        <p><strong>해제사유발생일</strong> → <strong>해제사유발생여부</strong> 로 변환</p>
      </li>
      <li>
        <p><strong>등기신청일자</strong> → <strong>등기신청여부</strong> 로 변환</p>
      </li>
      <li>
        <p><strong>세대당 주차대수</strong> 특성 생성 <strong>(주차대수 / 전체 새대수)</strong></p>
      </li>
      <li>
        <p>좌표값을 이용한 <strong>군집화</strong></p>
      </li>
    </ul>
  </li>
</ul>

<hr />

<hr />

<ul>
  <li>
    <p>bus, subway 및 외부  데이터</p>

    <ul>
      <li>
        <p>좌표값을 이용하여 아파트와 각 버스정류장, 지하철역까지의 거리 계산</p>
      </li>
      <li>
        <p>한국은행 기준금리 추가</p>
      </li>
    </ul>
  </li>
</ul>

<hr />

<hr />

<p>전처리 과정에서 가장 신경을 쏟았던 부분인 군집화 부분을 살펴보면…</p>

<ol>
  <li>
    <p>좌표의 결측치 처리</p>

    <p>※ 데이터 내 <strong>도로명</strong>을 이용하여 카카오 API로 좌표를 추출</p>

    <p>※ 그래도 남아있던 결측치(재개발 등의 이유로)는 이상치로 간주하고 제거</p>
  </li>
  <li>
    <p>Kmeans Clustering</p>

    <p>※ <strong>Kmeans Clustering</strong>을 이용하여 좌표별 군집화 수행</p>

    <p>※ 첫 군집화 시 다음과 같이 추출됨</p>

    <p>※ <img src="https://github.com/lIllIlIIIll/Leeinformation.github.io/blob/master/_posts/image/Bootcamp/first_cluster.png?raw=true" alt="image.png" /></p>

    <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> → 이상치로 보이는 것 들이 존재하기에, 이를 제거하고 다시 군집화
</code></pre></div>    </div>

    <p>※ <img src="https://github.com/lIllIlIIIll/Leeinformation.github.io/blob/master/_posts/image/Bootcamp/second_cluster.png?raw=true" alt="image.png" /></p>
  </li>
  <li>
    <p>feature 생성</p>

    <p>※ 군집화 모델로 추출한 <strong>군집</strong>을 <strong>cluster</strong>라는 새로운 feature로 생성</p>
  </li>
</ol>

<hr />

<hr />

<p>이러한 전처리 과정들을 거친 데이터는 다음과 같다.</p>

<p><img src="https://github.com/lIllIlIIIll/Leeinformation.github.io/blob/master/_posts/image/Bootcamp/df_head.PNG?raw=true" alt="image.png" /></p>

<hr />

<h1 id="4-lgbm-training">4. LGBM Training</h1>

<hr />

<p>처음 훈련시킬 모델로는 SOTA 모델인 <strong>LightGBM Regressor</strong>를 사용하였다.</p>

<p>모델을 훈련할 때는 <strong>Baysian Optimization</strong>을 사용하여 다음의 탐색공간 내에서 최적의 파라미터를 찾도록 하였다.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="n">param_space</span> <span class="o">=</span> <span class="p">{</span>

    <span class="s">'n_estimators'</span><span class="p">:</span> <span class="n">scope</span><span class="p">.</span><span class="nb">int</span><span class="p">(</span><span class="n">hp</span><span class="p">.</span><span class="n">quniform</span><span class="p">(</span><span class="s">'n_estimators'</span><span class="p">,</span> <span class="mi">300</span><span class="p">,</span> <span class="mi">3000</span><span class="p">,</span> <span class="mi">10</span><span class="p">)),</span>

    <span class="s">'learning_rate'</span><span class="p">:</span> <span class="n">hp</span><span class="p">.</span><span class="n">uniform</span><span class="p">(</span><span class="s">'learning_rate'</span><span class="p">,</span> <span class="mf">0.001</span><span class="p">,</span> <span class="mf">0.2</span><span class="p">),</span>

    <span class="s">'num_leaves'</span> <span class="p">:</span> <span class="n">scope</span><span class="p">.</span><span class="nb">int</span><span class="p">(</span><span class="n">hp</span><span class="p">.</span><span class="n">quniform</span><span class="p">(</span><span class="s">'num_leaves'</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">50</span><span class="p">,</span> <span class="mi">1</span><span class="p">)),</span>

    <span class="s">'max_depth'</span><span class="p">:</span> <span class="n">scope</span><span class="p">.</span><span class="nb">int</span><span class="p">(</span><span class="n">hp</span><span class="p">.</span><span class="n">quniform</span><span class="p">(</span><span class="s">'max_depth'</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">40</span><span class="p">,</span> <span class="mi">1</span><span class="p">)),</span>

    <span class="s">'min_data_in_leaf'</span> <span class="p">:</span> <span class="n">scope</span><span class="p">.</span><span class="nb">int</span><span class="p">(</span><span class="n">hp</span><span class="p">.</span><span class="n">quniform</span><span class="p">(</span><span class="s">'min_data_in_leaf'</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">50</span><span class="p">,</span> <span class="mi">1</span><span class="p">)),</span>

    <span class="s">'feature_fraction_bynode'</span> <span class="p">:</span> <span class="n">hp</span><span class="p">.</span><span class="n">uniform</span><span class="p">(</span><span class="s">'feature_fraction_bynode'</span><span class="p">,</span> <span class="mf">0.001</span><span class="p">,</span> <span class="mf">1.0</span><span class="p">),</span>

    <span class="s">'bagging_fraction'</span> <span class="p">:</span> <span class="n">hp</span><span class="p">.</span><span class="n">uniform</span><span class="p">(</span><span class="s">'bagging_fraction'</span><span class="p">,</span> <span class="mf">0.001</span><span class="p">,</span> <span class="mf">1.0</span><span class="p">),</span>

    <span class="s">'bagging_freq'</span> <span class="p">:</span> <span class="n">scope</span><span class="p">.</span><span class="nb">int</span><span class="p">(</span><span class="n">hp</span><span class="p">.</span><span class="n">quniform</span><span class="p">(</span><span class="s">'bagging_freq'</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">30</span><span class="p">,</span> <span class="mi">1</span><span class="p">)),</span>

    <span class="s">'min_child_weight'</span><span class="p">:</span> <span class="n">hp</span><span class="p">.</span><span class="n">uniform</span><span class="p">(</span><span class="s">'min_child_weight'</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">10</span><span class="p">),</span>

    <span class="s">'reg_alpha'</span><span class="p">:</span> <span class="n">hp</span><span class="p">.</span><span class="n">uniform</span><span class="p">(</span><span class="s">'reg_alpha'</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">),</span>

    <span class="s">'reg_lambda'</span><span class="p">:</span> <span class="n">hp</span><span class="p">.</span><span class="n">uniform</span><span class="p">(</span><span class="s">'reg_lambda'</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">),</span>

    <span class="s">'drop_rate'</span> <span class="p">:</span> <span class="n">hp</span><span class="p">.</span><span class="n">uniform</span><span class="p">(</span><span class="s">'drop_rate'</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>

<span class="p">}</span>

</code></pre></div></div>

<hr />

<hr />

<p>최적의 하이퍼파라미터를 찾고, 최적의 모델로 검증셋에 대한 예측을 수행하였을 때, 다음과 같은 <strong>RMSE</strong>가 나왔다.</p>

<p><img src="https://github.com/lIllIlIIIll/Leeinformation.github.io/blob/master/_posts/image/Bootcamp/LGBM_result.PNG?raw=true" alt="image.png" /></p>

<p>또한 첫 submission을 제출하였고 다음과 같은 public score를 기록하였다.</p>

<p><img src="https://github.com/lIllIlIIIll/Leeinformation.github.io/blob/master/_posts/image/Bootcamp/first_sub.PNG?raw=true" alt="image.png" /></p>

<hr />

<h1 id="5-automl---autogluon">5. AutoML - Autogluon</h1>

<hr />

<p>LGBM으로 모델을 선정하고 데이터 전처리 및 모델 학습을 계속해서 하고 있을 때 문득 생각이 난 패키지였다.</p>

<p>예전에 데이콘에서 주최했던 대회에서 사용 경험이 있었기에 AutoML을 사용해보는 것도 좋을 것 같았다.</p>

<p>그리고 바로 <strong>Autogluon</strong>을 사용해 보았다.</p>

<p>먼저 훈련 당시 validation set에 대한 RMSE는 다음과 같이 나왔다.</p>

<p><img src="https://github.com/lIllIlIIIll/Leeinformation.github.io/blob/master/_posts/image/Bootcamp/AutoML_RMSE.PNG?raw=true" alt="image.png" /></p>

<p>확실히 낮아진 것을 볼 수 있었다. (스스로 KFold Cross Validation을 사용하여 validation set을 나누었다.)</p>

<p>다음으로 submission을 제출하여 Public RMSE를 확인해보았다.</p>

<p><img src="https://github.com/lIllIlIIIll/Leeinformation.github.io/blob/master/_posts/image/Bootcamp/AutoML.PNG?raw=true" alt="image.png" /></p>

<p>바로 LGBM을 사용한 것 보다 Public RMSE가 4000이나 낮게 나온다…</p>

<p>심지어 Autogluon의 파라미터를 지정할 때 잘못 지정해버려서 모든 feature를 사용하는 모델을 학습시켜버렸었다.</p>

<p>그런데 저 정도의 퍼포먼스를 보여줬고, 바로 모델을 LGBM에서 Autogluon으로 교체하였다.</p>

<hr />

<h2 id="51-추가-전처리">5.1 추가 전처리</h2>

<hr />

<p>사실 AutoML 패키지에 포함된 모델들은 별 다른 전처리를 필요로 하지 않는다.</p>

<p>추후 알게된 내용인데, 내부적으로 데이터 전처리를 자동으로 해주고, 모델 훈련과정에서 스스로 적합한 파생 변수를 추가해주며, 검증셋도 나눠주기 때문이다.</p>

<p>하지만 우리팀은 ‘여기에 추가로 전처리를 해준다면 모델의 성능이 더 좋아지지 않을까?’ 라고 접근하였다.</p>

<ul>
  <li>
    <p>LGBM에서 사용하였던 feature engineering 적용</p>
  </li>
  <li>
    <p>AutoML 내 TimeSeries를 이용하여 시계열 데이터로 분석</p>
  </li>
  <li>
    <p>적용했던 feature engineering에서 여러 조합들 시도</p>
  </li>
</ul>

<p>AutoML을 사용하기로 결정한 이후부터 대회 마감까지 굉장히 여러가지 시도들을 많이 해보았었다.</p>

<hr />

<h1 id="6-결과">6. 결과</h1>

<p>결론적으로는 Raw Data 상태 그대로 AutoML을 사용하는 것이 성능이 가장 뛰어났다.</p>

<p>AutoML Documents를 살펴보니, 위에서 기술했듯이 내부적으로 스스로 feature를 생성하는 등의 전처리를 하기에 그랬던 것 같았다.</p>

<p>사실 이전 데이콘 경진대회에서는 데이터를 사람이 직접 어느정도 정제해주어야 성능이 좋아졌었고, 그에 맞게 이번 대회에서도 그러한 방식을 사용하였던 것이었는데, 도무지 성능이 오를 생각을 하지 않았었다.</p>

<p>결국 팀원이 Raw Data로 훈련시켰을 때 Public이 가장 낮았던 모델 하나와 Public은 다소 낮으나 전처리가 어느정도 이루어진 모델 하나를 제출하였다.</p>

<p><img src="https://github.com/lIllIlIIIll/Leeinformation.github.io/blob/master/_posts/image/Bootcamp/대회결과.PNG?raw=true" alt="image.png" /></p>

<p>3등이라는 좋은 결과를 달성할 수 있었다.</p>

<p>실제로 Private 결과가 더 좋았던 모델이 있었기에 더 아쉬웠었다.</p>

<h1 id="7-회고">7. 회고</h1>

<p>대회 결과로써는 팀원들 모두 만족하는 결과였다.</p>

<p>마지막으로 Public 등수는 4등이었고, Private 등수는 3등으로 올랐다.</p>

<p>결과적으로는 좋았으나, 팀원들 간 협업 부분에 있어서는 부족했던 것 같다. 이부분을 정리해보면…</p>

<ul>
  <li>
    <p>잘했던 점</p>

    <blockquote>
      <p>팀원들 간 소통에 있어서는 서로 진행했던 전처리, 훈련 등을 구두적으로 공유하는 것은 순조롭게 진행되었다.</p>
    </blockquote>
  </li>
  <li>
    <p>부족했던 점</p>

    <blockquote>
      <p>소통은 잘 되었으나 내용적으로 Github에 공유하는 부분이 부족했다. 즉, 코드 공유는 잘 안되었다.</p>
    </blockquote>
  </li>
</ul>

<p>나에게는 이런 머신러닝 대회를 팀으로 나가는 건 처음이었고 팀장을 맡아 진행하는 것도 처음이었다.</p>

<p>이후 MLOps 관련 팀 프로젝트도 같은 팀으로 진행하게 되는데, 다음 프로젝트에서는 체계적으로 코드 버전 관리를 통해 이번에 제대로 하지 못했던 부분들을 채워나갈 수 있도록 노력해보겠다.</p>

<ul>
  <li>
    <p>향후 개선 방향</p>

    <blockquote>
      <p>Git 및 Github를 이용하여 코드 버전 관리를 체계적으로 실시</p>
    </blockquote>
  </li>
</ul>]]></content><author><name>Lee Jeong Min</name></author><category term="Bootcamp" /><category term="패스트캠퍼스" /><category term="패스트캠퍼스AI부트캠프" /><category term="업스테이지패스트캠퍼스" /><category term="UpstageAILab" /><category term="국비지원" /><category term="패스트캠퍼스업스테이지에이아이랩" /><category term="패스트캠퍼스업스테이지부트캠프" /><summary type="html"><![CDATA[1. 경진대회 개요 5월이 시작되고, 서울의 아파트 실거래가를 예측하는 경진대회가 시작되었다. 첫 회의 때 서로가 알고있던 도메인을 공유하여 주어진 데이터 내에서 feature들의 중요도를 파악하고, 추가적으로 어떤 데이터를 추가하여 모델의 성능을 향상시킬 수 있는지에 대해 논의하였다. 이전 여러 경진대회에서 경험했던 바와 같이 물론 직접 훈련에 들어갔을 때 우리가 중요하게 생각했던 feature가 필요성이 떨어질 수 있고, 반대의 경우도 가능했기에 전체적으로 어떻게 경진대회를 진행할지에 대해서 논의하였다. 2. EDA 머신러닝 파이프라인에 있어 가장 어렵고 중요한 부분이라고 생각하는 파트이다. 주어진 데이터를 시각화 or 통계적으로 분석하여 변수들간의 상관관계를 파악하고, 파생 변수 생성 및 필요 없는 변수 제거 등을 수행하는데 근거가 되기에 각 feature가 어떤 분포를 가지는지 확인하는데 집중했다. 데이터를 전체적으로 살펴보았을 때, 굉장히 결측치가 많았다. 전체 52개의 feature 중 5~6개를 제외하면 70% 이상의 결측치를 가졌고, 심지어 각 feature를 target과 비교하였을 때 이상치로 구분되는 값도 많았다. 또한, train 데이터와 test 데이터의 분포가 너무 달랐다. - train 데이터 : 근 30년간의 아파트 거래가격을 나타냄 - test 데이터 : 2023년의 아파트 거래가격을 나타냄 이는 물가, 경제 상황 등의 외부적 요인에 따라 target(금액)의 가치가 달랐다. 검증셋을 2023년 데이터만으로 구성하는 방법을 사용 (물론 나중 모델 훈련시에는 전체를 검증셋으로 사용하여 RMSE를 낮추는 방향으로 진행하는 것이 더 좋았다.) 위에서 기술하였듯이 결측치가 많았기에 52개의 feature를 전부 사용하는 것은 모델 훈련에 있어 성능을 저하시킨다고 판단하였고, 실제로 데이터를 전처리 시키기 전, 정말 필요없다고 생각하는 feature들을 제거하였다. 그리하여 52개의 feature 중 남긴 feature는 다음과 같았다. 전용면적(㎡), 건축년도, 해제사유발생일, k-연면적, k-전용면적별세대현황(60㎡~85㎡이하),k-전용면적별세대현황(85㎡~135㎡이하), k-전체세대수, 주차대수, 계약(연), 좌표X, 좌표Y, 아파트명, 도로명, 등기신청일자, k-복도유형, k-단지분류(아파트,주상복합등등) 3. Preprocessing 앞서 진행하였던 EDA를 기반으로 전처리를 진행하였다. train 데이터 중요도가 떨어진다고 생각하는 특성 Drop 문자열 columns을 찾아서 좌우 공백 제거(같은 데이터인데 다르게 해석할 가능성 제거) 데이터 중 중복으로 같은 값을 나타내는 feature Drop (번지, 본번, 부번) 계약년월 → 계약(연), 계약(월) 로 분해 해제사유발생일 → 해제사유발생여부 로 변환 등기신청일자 → 등기신청여부 로 변환 세대당 주차대수 특성 생성 (주차대수 / 전체 새대수) 좌표값을 이용한 군집화 bus, subway 및 외부 데이터 좌표값을 이용하여 아파트와 각 버스정류장, 지하철역까지의 거리 계산 한국은행 기준금리 추가 전처리 과정에서 가장 신경을 쏟았던 부분인 군집화 부분을 살펴보면… 좌표의 결측치 처리 ※ 데이터 내 도로명을 이용하여 카카오 API로 좌표를 추출 ※ 그래도 남아있던 결측치(재개발 등의 이유로)는 이상치로 간주하고 제거 Kmeans Clustering ※ Kmeans Clustering을 이용하여 좌표별 군집화 수행 ※ 첫 군집화 시 다음과 같이 추출됨 ※ → 이상치로 보이는 것 들이 존재하기에, 이를 제거하고 다시 군집화 ※ feature 생성 ※ 군집화 모델로 추출한 군집을 cluster라는 새로운 feature로 생성 이러한 전처리 과정들을 거친 데이터는 다음과 같다. 4. LGBM Training 처음 훈련시킬 모델로는 SOTA 모델인 LightGBM Regressor를 사용하였다. 모델을 훈련할 때는 Baysian Optimization을 사용하여 다음의 탐색공간 내에서 최적의 파라미터를 찾도록 하였다. param_space = { 'n_estimators': scope.int(hp.quniform('n_estimators', 300, 3000, 10)), 'learning_rate': hp.uniform('learning_rate', 0.001, 0.2), 'num_leaves' : scope.int(hp.quniform('num_leaves', 2, 50, 1)), 'max_depth': scope.int(hp.quniform('max_depth', 0, 40, 1)), 'min_data_in_leaf' : scope.int(hp.quniform('min_data_in_leaf', 0, 50, 1)), 'feature_fraction_bynode' : hp.uniform('feature_fraction_bynode', 0.001, 1.0), 'bagging_fraction' : hp.uniform('bagging_fraction', 0.001, 1.0), 'bagging_freq' : scope.int(hp.quniform('bagging_freq', 0, 30, 1)), 'min_child_weight': hp.uniform('min_child_weight', 0, 10), 'reg_alpha': hp.uniform('reg_alpha', 0, 1), 'reg_lambda': hp.uniform('reg_lambda', 0, 1), 'drop_rate' : hp.uniform('drop_rate', 0, 1) } 최적의 하이퍼파라미터를 찾고, 최적의 모델로 검증셋에 대한 예측을 수행하였을 때, 다음과 같은 RMSE가 나왔다. 또한 첫 submission을 제출하였고 다음과 같은 public score를 기록하였다. 5. AutoML - Autogluon LGBM으로 모델을 선정하고 데이터 전처리 및 모델 학습을 계속해서 하고 있을 때 문득 생각이 난 패키지였다. 예전에 데이콘에서 주최했던 대회에서 사용 경험이 있었기에 AutoML을 사용해보는 것도 좋을 것 같았다. 그리고 바로 Autogluon을 사용해 보았다. 먼저 훈련 당시 validation set에 대한 RMSE는 다음과 같이 나왔다. 확실히 낮아진 것을 볼 수 있었다. (스스로 KFold Cross Validation을 사용하여 validation set을 나누었다.) 다음으로 submission을 제출하여 Public RMSE를 확인해보았다. 바로 LGBM을 사용한 것 보다 Public RMSE가 4000이나 낮게 나온다… 심지어 Autogluon의 파라미터를 지정할 때 잘못 지정해버려서 모든 feature를 사용하는 모델을 학습시켜버렸었다. 그런데 저 정도의 퍼포먼스를 보여줬고, 바로 모델을 LGBM에서 Autogluon으로 교체하였다. 5.1 추가 전처리 사실 AutoML 패키지에 포함된 모델들은 별 다른 전처리를 필요로 하지 않는다. 추후 알게된 내용인데, 내부적으로 데이터 전처리를 자동으로 해주고, 모델 훈련과정에서 스스로 적합한 파생 변수를 추가해주며, 검증셋도 나눠주기 때문이다. 하지만 우리팀은 ‘여기에 추가로 전처리를 해준다면 모델의 성능이 더 좋아지지 않을까?’ 라고 접근하였다. LGBM에서 사용하였던 feature engineering 적용 AutoML 내 TimeSeries를 이용하여 시계열 데이터로 분석 적용했던 feature engineering에서 여러 조합들 시도 AutoML을 사용하기로 결정한 이후부터 대회 마감까지 굉장히 여러가지 시도들을 많이 해보았었다. 6. 결과 결론적으로는 Raw Data 상태 그대로 AutoML을 사용하는 것이 성능이 가장 뛰어났다. AutoML Documents를 살펴보니, 위에서 기술했듯이 내부적으로 스스로 feature를 생성하는 등의 전처리를 하기에 그랬던 것 같았다. 사실 이전 데이콘 경진대회에서는 데이터를 사람이 직접 어느정도 정제해주어야 성능이 좋아졌었고, 그에 맞게 이번 대회에서도 그러한 방식을 사용하였던 것이었는데, 도무지 성능이 오를 생각을 하지 않았었다. 결국 팀원이 Raw Data로 훈련시켰을 때 Public이 가장 낮았던 모델 하나와 Public은 다소 낮으나 전처리가 어느정도 이루어진 모델 하나를 제출하였다. 3등이라는 좋은 결과를 달성할 수 있었다. 실제로 Private 결과가 더 좋았던 모델이 있었기에 더 아쉬웠었다. 7. 회고 대회 결과로써는 팀원들 모두 만족하는 결과였다. 마지막으로 Public 등수는 4등이었고, Private 등수는 3등으로 올랐다. 결과적으로는 좋았으나, 팀원들 간 협업 부분에 있어서는 부족했던 것 같다. 이부분을 정리해보면… 잘했던 점 팀원들 간 소통에 있어서는 서로 진행했던 전처리, 훈련 등을 구두적으로 공유하는 것은 순조롭게 진행되었다. 부족했던 점 소통은 잘 되었으나 내용적으로 Github에 공유하는 부분이 부족했다. 즉, 코드 공유는 잘 안되었다. 나에게는 이런 머신러닝 대회를 팀으로 나가는 건 처음이었고 팀장을 맡아 진행하는 것도 처음이었다. 이후 MLOps 관련 팀 프로젝트도 같은 팀으로 진행하게 되는데, 다음 프로젝트에서는 체계적으로 코드 버전 관리를 통해 이번에 제대로 하지 못했던 부분들을 채워나갈 수 있도록 노력해보겠다. 향후 개선 방향 Git 및 Github를 이용하여 코드 버전 관리를 체계적으로 실시]]></summary></entry><entry><title type="html">[프로그래머스] k진수에서 소수 개수 구하기</title><link href="https://leeinformation.github.io/Leeinformation.github.io/coding_test/Decimals_Counts/" rel="alternate" type="text/html" title="[프로그래머스] k진수에서 소수 개수 구하기" /><published>2025-04-24T00:00:00+00:00</published><updated>2025-04-24T00:00:00+00:00</updated><id>https://leeinformation.github.io/Leeinformation.github.io/coding_test/Decimals_Counts</id><content type="html" xml:base="https://leeinformation.github.io/Leeinformation.github.io/coding_test/Decimals_Counts/"><![CDATA[<head>
  <style>
    table.dataframe {
      white-space: normal;
      width: 100%;
      height: 240px;
      display: block;
      overflow: auto;
      font-family: Arial, sans-serif;
      font-size: 0.9rem;
      line-height: 20px;
      text-align: center;
      border: 0px !important;
    }

    table.dataframe th {
      text-align: center;
      font-weight: bold;
      padding: 8px;
    }

    table.dataframe td {
      text-align: center;
      padding: 8px;
    }

    table.dataframe tr:hover {
      background: #b8d1f3; 
    }

    .output_prompt {
      overflow: auto;
      font-size: 0.9rem;
      line-height: 1.45;
      border-radius: 0.3rem;
      -webkit-overflow-scrolling: touch;
      padding: 0.8rem;
      margin-top: 0;
      margin-bottom: 15px;
      font: 1rem Consolas, "Liberation Mono", Menlo, Courier, monospace;
      color: $code-text-color;
      border: solid 1px $border-color;
      border-radius: 0.3rem;
      word-break: normal;
      white-space: pre;
    }

  .dataframe tbody tr th:only-of-type {
      vertical-align: middle;
  }

  .dataframe tbody tr th {
      vertical-align: top;
  }

  .dataframe thead th {
      text-align: center !important;
      padding: 8px;
  }

  .page__content p {
      margin: 0 0 0px !important;
  }

  .page__content p > strong {
    font-size: 0.8rem !important;
  }

  </style>
</head>

<h1 id="1-생각의-흐름">1. 생각의 흐름</h1>

<p>그냥 문제 설명을 보고 그대로 코드를 구현했다.</p>

<p>먼저 작업해 준 것은 k진수 변환 함수와 소수를 검사하는 함수 두 개를 구현했다.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># 10진수 → k진수 변환 함수
</span>
<span class="k">def</span> <span class="nf">convert_k</span><span class="p">(</span><span class="n">n</span><span class="p">,</span> <span class="n">k</span><span class="p">)</span> <span class="p">:</span>
    <span class="n">k_number</span> <span class="o">=</span> <span class="s">''</span>
    
    <span class="k">while</span> <span class="n">n</span> <span class="o">&gt;</span> <span class="mi">0</span> <span class="p">:</span>
        <span class="n">quotient</span> <span class="o">=</span> <span class="n">n</span> <span class="o">//</span> <span class="n">k</span>
        <span class="n">reminder</span> <span class="o">=</span> <span class="n">n</span> <span class="o">%</span> <span class="n">k</span>
        <span class="n">n</span> <span class="o">=</span> <span class="n">quotient</span>
        <span class="n">k_number</span> <span class="o">+=</span> <span class="nb">str</span><span class="p">(</span><span class="n">reminder</span><span class="p">)</span>
    
    <span class="k">return</span> <span class="n">k_number</span><span class="p">[::</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span>
</code></pre></div></div>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># 소수 검사 함수
</span>
<span class="kn">import</span> <span class="nn">math</span>

<span class="k">def</span> <span class="nf">check_prime</span><span class="p">(</span><span class="n">n</span><span class="p">)</span> <span class="p">:</span>
    <span class="k">if</span> <span class="n">n</span> <span class="o">==</span> <span class="mi">1</span> <span class="p">:</span>
        <span class="k">return</span> <span class="bp">False</span>
    
    <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="nb">int</span><span class="p">(</span><span class="n">math</span><span class="p">.</span><span class="n">sqrt</span><span class="p">(</span><span class="n">n</span><span class="p">))</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span> <span class="p">:</span>
        <span class="k">if</span> <span class="n">n</span> <span class="o">%</span> <span class="n">i</span> <span class="o">==</span> <span class="mi">0</span> <span class="p">:</span>
            <span class="k">return</span> <span class="bp">False</span>
    <span class="k">return</span> <span class="bp">True</span>
</code></pre></div></div>

<p>10진수를 k진수로 변환하는 과정은 주어진 값을 k로 나누어 나머지를 계속 더하면 된다.</p>

<p>소수 검사 함수는 제곱근을 활용하여 시간복잡도를 줄였다. (n이 최대 1,000,000이라 k진수 변환 후 전부 검사하면 시간 초과날 것 같았다.)</p>

<hr />

<p>그리고서 문제에서 주어진 대로…</p>

<ol>
  <li>
    <p>0이 아니면 <strong>number_string</strong>에 추가</p>
  </li>
  <li>
    <p>0이 나오면 지금까지 <strong>number_string</strong>에 있던 값이 소수인지 검사</p>
  </li>
  <li>
    <p>1, 2 과정 반복</p>
  </li>
  <li>
    <p>마지막이 만약 0 이 아니라면(EX: 912093) <strong>number_string</strong>에 있던 값 소수 검사</p>
  </li>
</ol>

<h1 id="2-코드-구현">2. 코드 구현</h1>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">math</span>

<span class="k">def</span> <span class="nf">convert_k</span><span class="p">(</span><span class="n">n</span><span class="p">,</span> <span class="n">k</span><span class="p">)</span> <span class="p">:</span>
    <span class="n">k_number</span> <span class="o">=</span> <span class="s">''</span>
    
    <span class="k">while</span> <span class="n">n</span> <span class="o">&gt;</span> <span class="mi">0</span> <span class="p">:</span>
        <span class="n">quotient</span> <span class="o">=</span> <span class="n">n</span> <span class="o">//</span> <span class="n">k</span>
        <span class="n">reminder</span> <span class="o">=</span> <span class="n">n</span> <span class="o">%</span> <span class="n">k</span>
        <span class="n">n</span> <span class="o">=</span> <span class="n">quotient</span>
        <span class="n">k_number</span> <span class="o">+=</span> <span class="nb">str</span><span class="p">(</span><span class="n">reminder</span><span class="p">)</span>
    
    <span class="k">return</span> <span class="n">k_number</span><span class="p">[::</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span>

<span class="k">def</span> <span class="nf">check_prime</span><span class="p">(</span><span class="n">n</span><span class="p">)</span> <span class="p">:</span>
    <span class="k">if</span> <span class="n">n</span> <span class="o">==</span> <span class="mi">1</span> <span class="p">:</span>
        <span class="k">return</span> <span class="bp">False</span>
    
    <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="nb">int</span><span class="p">(</span><span class="n">math</span><span class="p">.</span><span class="n">sqrt</span><span class="p">(</span><span class="n">n</span><span class="p">))</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span> <span class="p">:</span>
        <span class="k">if</span> <span class="n">n</span> <span class="o">%</span> <span class="n">i</span> <span class="o">==</span> <span class="mi">0</span> <span class="p">:</span>
            <span class="k">return</span> <span class="bp">False</span>
    <span class="k">return</span> <span class="bp">True</span>
    

<span class="k">def</span> <span class="nf">solution</span><span class="p">(</span><span class="n">n</span><span class="p">,</span> <span class="n">k</span><span class="p">):</span>
    <span class="n">answer</span> <span class="o">=</span> <span class="mi">0</span>
    <span class="n">number_string</span> <span class="o">=</span> <span class="s">''</span>
    
    <span class="n">converted_number</span> <span class="o">=</span> <span class="n">convert_k</span><span class="p">(</span><span class="n">n</span><span class="p">,</span> <span class="n">k</span><span class="p">)</span>
    
    <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">converted_number</span><span class="p">))</span> <span class="p">:</span>
        <span class="k">if</span> <span class="n">converted_number</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">!=</span> <span class="s">'0'</span> <span class="p">:</span>
            <span class="n">number_string</span> <span class="o">+=</span> <span class="n">converted_number</span><span class="p">[</span><span class="n">i</span><span class="p">]</span>
        <span class="k">else</span> <span class="p">:</span>
            <span class="k">if</span> <span class="n">number_string</span> <span class="o">!=</span> <span class="s">""</span> <span class="p">:</span>   
                <span class="k">if</span> <span class="n">check_prime</span><span class="p">(</span><span class="nb">int</span><span class="p">(</span><span class="n">number_string</span><span class="p">))</span> <span class="p">:</span>
                    <span class="n">answer</span> <span class="o">+=</span> <span class="mi">1</span>
                    <span class="n">number_string</span> <span class="o">=</span> <span class="s">''</span>
                    <span class="k">continue</span>
                <span class="k">else</span> <span class="p">:</span>
                    <span class="n">number_string</span> <span class="o">=</span> <span class="s">''</span>
                    <span class="k">continue</span>
    
    <span class="k">if</span> <span class="s">'0'</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">number_string</span> <span class="p">:</span>            
        <span class="k">if</span> <span class="n">number_string</span> <span class="o">!=</span> <span class="s">""</span> <span class="ow">and</span> <span class="n">check_prime</span><span class="p">(</span><span class="nb">int</span><span class="p">(</span><span class="n">number_string</span><span class="p">))</span> <span class="p">:</span>
            <span class="n">answer</span> <span class="o">+=</span> <span class="mi">1</span>
    
    <span class="k">return</span> <span class="n">answer</span>
</code></pre></div></div>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">solution</span><span class="p">(</span><span class="mi">50</span><span class="p">,</span> <span class="mi">10</span><span class="p">)</span>
</code></pre></div></div>

<pre>
1
</pre>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">convert_k</span><span class="p">(</span><span class="mi">1_000_000</span><span class="p">,</span> <span class="mi">3</span><span class="p">)</span>
</code></pre></div></div>

<pre>
'1212210202001'
</pre>]]></content><author><name>Lee Jeong Min</name></author><category term="Coding_Test" /><category term="python" /><category term="coding" /><summary type="html"><![CDATA[1. 생각의 흐름 그냥 문제 설명을 보고 그대로 코드를 구현했다. 먼저 작업해 준 것은 k진수 변환 함수와 소수를 검사하는 함수 두 개를 구현했다. # 10진수 → k진수 변환 함수 def convert_k(n, k) : k_number = '' while n &gt; 0 : quotient = n // k reminder = n % k n = quotient k_number += str(reminder) return k_number[::-1] # 소수 검사 함수 import math def check_prime(n) : if n == 1 : return False for i in range(2, int(math.sqrt(n)) + 1) : if n % i == 0 : return False return True 10진수를 k진수로 변환하는 과정은 주어진 값을 k로 나누어 나머지를 계속 더하면 된다. 소수 검사 함수는 제곱근을 활용하여 시간복잡도를 줄였다. (n이 최대 1,000,000이라 k진수 변환 후 전부 검사하면 시간 초과날 것 같았다.) 그리고서 문제에서 주어진 대로… 0이 아니면 number_string에 추가 0이 나오면 지금까지 number_string에 있던 값이 소수인지 검사 1, 2 과정 반복 마지막이 만약 0 이 아니라면(EX: 912093) number_string에 있던 값 소수 검사 2. 코드 구현 import math def convert_k(n, k) : k_number = '' while n &gt; 0 : quotient = n // k reminder = n % k n = quotient k_number += str(reminder) return k_number[::-1] def check_prime(n) : if n == 1 : return False for i in range(2, int(math.sqrt(n)) + 1) : if n % i == 0 : return False return True def solution(n, k): answer = 0 number_string = '' converted_number = convert_k(n, k) for i in range(len(converted_number)) : if converted_number[i] != '0' : number_string += converted_number[i] else : if number_string != "" : if check_prime(int(number_string)) : answer += 1 number_string = '' continue else : number_string = '' continue if '0' not in number_string : if number_string != "" and check_prime(int(number_string)) : answer += 1 return answer solution(50, 10) 1 convert_k(1_000_000, 3) '1212210202001']]></summary></entry><entry><title type="html">[AI 부트캠프] Git을 이용한 프로젝트 관리</title><link href="https://leeinformation.github.io/Leeinformation.github.io/bootcamp/Git/" rel="alternate" type="text/html" title="[AI 부트캠프] Git을 이용한 프로젝트 관리" /><published>2025-04-24T00:00:00+00:00</published><updated>2025-04-24T00:00:00+00:00</updated><id>https://leeinformation.github.io/Leeinformation.github.io/bootcamp/Git</id><content type="html" xml:base="https://leeinformation.github.io/Leeinformation.github.io/bootcamp/Git/"><![CDATA[<head>
  <style>
    table.dataframe {
      white-space: normal;
      width: 100%;
      height: 240px;
      display: block;
      overflow: auto;
      font-family: Arial, sans-serif;
      font-size: 0.9rem;
      line-height: 20px;
      text-align: center;
      border: 0px !important;
    }

    table.dataframe th {
      text-align: center;
      font-weight: bold;
      padding: 8px;
    }

    table.dataframe td {
      text-align: center;
      padding: 8px;
    }

    table.dataframe tr:hover {
      background: #b8d1f3; 
    }

    .output_prompt {
      overflow: auto;
      font-size: 0.9rem;
      line-height: 1.45;
      border-radius: 0.3rem;
      -webkit-overflow-scrolling: touch;
      padding: 0.8rem;
      margin-top: 0;
      margin-bottom: 15px;
      font: 1rem Consolas, "Liberation Mono", Menlo, Courier, monospace;
      color: $code-text-color;
      border: solid 1px $border-color;
      border-radius: 0.3rem;
      word-break: normal;
      white-space: pre;
    }

  .dataframe tbody tr th:only-of-type {
      vertical-align: middle;
  }

  .dataframe tbody tr th {
      vertical-align: top;
  }

  .dataframe thead th {
      text-align: center !important;
      padding: 8px;
  }

  .page__content p {
      margin: 0 0 0px !important;
  }

  .page__content p > strong {
    font-size: 0.8rem !important;
  }

  </style>
</head>

<p>이틀에 걸쳐 <strong><em>Git</em></strong>을 이용하여 Github 저장소를 관리하고 팀 협업의 전체적인 흐름을 배웠다.</p>

<p>이전 프로젝트를 진행할 때는 Github Desktop을 이용하여 GUI로 간편하게 작업했었는데, git을 이용하여 작업하는 방법도 배웠다.</p>

<p>기본적인 명령어(Ex: add, commit, push, pull)는 어느 정도 알고 있었는데, 충돌이 일어났을 때의 대처법과 잘못 push한 파일들을 되돌리는 방법 등 몰랐던 부분들을 강사분께서 알려주셨다.</p>

<p>내가 몰랐던 부분을 배우는지라 확실히 집중이 더 잘되었다.</p>

<hr />

<h1 id="1-git">1. Git</h1>

<hr />

<p>리눅스 커널 관리를 위해 만든 분산형 버전 관리 시스템으로 구조가 단순하고 빠른 속도를 자랑한다.</p>

<p>Branch를 이용하여 비선형적 개발을 할 수 있으며 소스 코드를 주고받지 않고 동시 작업이 가능하여 생산성이 증가하고, 인터넷 연결없이 개발하다가 인터넷 연결 시 한번에 push가 가능하다.</p>

<hr />

<h2 id="11-기본-명령어">1.1 기본 명령어</h2>

<hr />

<ul>
  <li>
    <p><strong><em>Clone</em></strong></p>

    <p>git bash에서 가장 먼저 작업해주어야 하는 일은 github repository를 내 로컬과 연결해주어야 한다.</p>

    <p>그러기 위해서 repository를 생성하고 url 주소를 가져와 다음의 명령어를 실행한다.</p>

    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
  <span class="nv">$ </span>git clone <span class="o">[</span>REPO_ADDRESS]

</code></pre></div>    </div>

    <p>clone이 완료되면 다음과 같이 각각의 저장소가 복제되어 로컬에서 접속할 수 있게 된다.</p>

    <p><img src="https://github.com/lIllIlIIIll/Leeinformation.github.io/blob/master/_posts/image/Bootcamp/clone.PNG?raw=true" alt="image.png" /></p>
  </li>
</ul>

<hr />

<hr />

<ul>
  <li>
    <p><strong><em>Status</em></strong></p>

    <p>현재 git이 add하고 있었는지, commit하고 있었는지 등 사용자가 진행하던 작업의 상태를 볼 수 있는 명령어이다.</p>

    <p>git add 및 commit, push 등 어떤 작업을 수행해도 되는지 확인 후 진행한다.</p>

    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
  <span class="nv">$ </span>git status

</code></pre></div>    </div>

    <p>다음과 같이 더 이상 commit할 것이 없고 working tree가 비어있다면 add 작업을 진행해도 된다.</p>

    <p><img src="https://github.com/lIllIlIIIll/Leeinformation.github.io/blob/master/_posts/image/Bootcamp/status.PNG?raw=true" alt="image.png" /></p>
  </li>
</ul>

<hr />

<hr />

<ul>
  <li>
    <p><strong><em>add</em></strong></p>

    <p>작업 디렉토리 상의 변경 내용을 스테이징 영역에 추가하는 명령어이다.</p>

    <p>자신이 작업했던 내용은 commit 하기 전 까지는 git 저장소의 변경 이력에는 아무것도 남지 않는다.</p>

    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
  <span class="nv">$ </span>git add <span class="o">[</span>FILE_NAME]

</code></pre></div>    </div>

    <p>다음과 같이 example.py를 add 할 수 있다.</p>

    <p><img src="https://github.com/lIllIlIIIll/Leeinformation.github.io/blob/master/_posts/image/Bootcamp/add.PNG?raw=true" alt="image.png" /></p>
  </li>
</ul>

<hr />

<hr />

<ul>
  <li>
    <p><strong><em>commit</em></strong></p>

    <p>변경된 코드(작업했던 내용)를 기록으로 남긴다.</p>

    <p>commit message를 통해 어떤 내용을 작업했는지 간단한 설명을 남기고, Conventional Commits를 참고하여 작성한다.</p>

    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
  <span class="nv">$ </span>git commit

</code></pre></div>    </div>

    <p>Conventional Commits는 다음과 같다.</p>

    <table>
      <thead>
        <tr>
          <th>Prefix</th>
          <th>설명</th>
        </tr>
      </thead>
      <tbody>
        <tr>
          <td><code class="language-plaintext highlighter-rouge">feat</code></td>
          <td>기능 개발 관련</td>
        </tr>
        <tr>
          <td><code class="language-plaintext highlighter-rouge">fix</code></td>
          <td>오류 개선 혹은 버그 패치</td>
        </tr>
        <tr>
          <td><code class="language-plaintext highlighter-rouge">docs</code></td>
          <td>문서화 작업</td>
        </tr>
        <tr>
          <td><code class="language-plaintext highlighter-rouge">test</code></td>
          <td>테스트 코드 관련</td>
        </tr>
        <tr>
          <td><code class="language-plaintext highlighter-rouge">conf</code></td>
          <td>환경설정 관련</td>
        </tr>
        <tr>
          <td><code class="language-plaintext highlighter-rouge">build</code></td>
          <td>빌드 작업 관련</td>
        </tr>
        <tr>
          <td><code class="language-plaintext highlighter-rouge">ci</code></td>
          <td>Continuous Integration 관련</td>
        </tr>
        <tr>
          <td><code class="language-plaintext highlighter-rouge">chore</code></td>
          <td>패키지 매니저, 스크립트 등</td>
        </tr>
        <tr>
          <td><code class="language-plaintext highlighter-rouge">style</code></td>
          <td>코드 포매팅 (세미콜론, 들여쓰기 등) 관련</td>
        </tr>
      </tbody>
    </table>

    <p>명령어를 실행하면서 -m 명령어를 통해 바로 commit message를 작성할 수 있으나 해당 작업이 무슨 작업인지 헷갈릴 때에는 vi로 직접 확인한 뒤 변경하는 것이 옳다.</p>

    <p><img src="https://github.com/lIllIlIIIll/Leeinformation.github.io/blob/master/_posts/image/Bootcamp/commit.PNG?raw=true" alt="image.png" /></p>
  </li>
</ul>

<hr />

<hr />

<ul>
  <li>
    <p><strong><em>push</em></strong></p>

    <p>add 및 commit으로 기록했던 내용들을 Repository에 올리는 명령어이다.</p>

    <p>다음과 같은 명령어로 실행할 수 있다.</p>

    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
  <span class="nv">$ </span>git push <span class="o">[</span>REMOTE] <span class="o">[</span>BRANCH]

</code></pre></div>    </div>

    <p>다음과 같이 push 한 다음 Repository를 확인해보면 파일이 업로드된 것을 볼 수 있다.</p>

    <p><img src="https://github.com/lIllIlIIIll/Leeinformation.github.io/blob/master/_posts/image/Bootcamp/push.PNG?raw=true" alt="image.png" /></p>

    <p><img src="https://github.com/lIllIlIIIll/Leeinformation.github.io/blob/master/_posts/image/Bootcamp/repo_check.PNG?raw=true" alt="image.png" /></p>
  </li>
</ul>

<hr />

<h2 id="12-gitignore--conflict-해결">1.2 .gitignore / Conflict 해결</h2>

<hr />

<ul>
  <li>
    <p><strong><em>.gitignore</em></strong></p>

    <p><strong><em>.gitignore</em></strong> 파일은 특정 파일 or 특정 디렉토리를 추적하지 않도록 명시하기 위한 파일이다.</p>

    <p>즉, 어떤 파일을 repository에 올릴 때, 불필요한 파일들이 올라가는 것을 방지할 때 사용하는 필수적인 파일이다.</p>

    <p><a href="https://www.toptal.com/developers/gitignore/">.gitignore.io</a></p>

    <p>위의 사이트에서 프로젝트에 사용되는 운영체제, 프로그래밍 언어, 개발 환경 등에 맞춰 생성된 내용을 붙여넣기만 해주면 된다.</p>
  </li>
</ul>

<hr />

<hr />

<ul>
  <li>
    <p><strong><em>branch</em></strong></p>

    <p>branch는 초기 코드에서 분기점을 생성하여 독립적으로 코드를 변경하도록 하는 모델이다.</p>

    <p>다음의 명령어를 통해 현재 어떤 branch가 존재하는지 확인할 수 있다.</p>

    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
  <span class="nv">$ </span>git branch

</code></pre></div>    </div>

    <p>또한, 다음의 명령어를 통해 새로운 브랜치를 생성한 다음</p>

    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
  <span class="nv">$ </span>git branch <span class="o">[</span>BRANCH_NAME]

</code></pre></div>    </div>

    <p>해당 명령어를 통해 원하는 branch로 전환할 수 있다.</p>

    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
  <span class="nv">$ </span>git switch <span class="o">[</span>BRANCH_NAME]

</code></pre></div>    </div>

    <p>만약 branch를 삭제할 때에는 다음과 같이 작성해 주면 된다.</p>

    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
  <span class="nv">$ </span>git branch <span class="nt">-D</span> <span class="o">[</span>BRANCH_NAME]

</code></pre></div>    </div>
  </li>
</ul>

<hr />

<ul>
  <li>
    <p><strong><em>merge</em></strong></p>

    <p>초기 코드에서 branch를 이용하여 분기점이 생성된 후 다시 합칠 때 충돌이 일어날 수 있다.</p>

    <p>예를 들어 초기 코드에서 for 반복문이 다음과 같이 작성되어 있고</p>

    <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
  <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">20</span><span class="p">)</span> <span class="p">:</span>

      <span class="k">print</span><span class="p">(</span><span class="n">i</span><span class="p">)</span>

</code></pre></div>    </div>

    <p>branch로 분기점이 생성된 다음 해당 branch에서 작업한 내용이 다음과 같다면</p>

    <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
  <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">30</span><span class="p">)</span> <span class="p">:</span>

      <span class="k">print</span><span class="p">(</span><span class="n">i</span><span class="p">)</span>

</code></pre></div>    </div>

    <p>for 문의 범위가 같지 않기에 충돌이 발생할 것이다.</p>

    <p>예를 들어 main에서 작성된 처음 파일이 다음과 같을 때,</p>

    <hr />

    <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
  <span class="k">def</span> <span class="nf">test</span><span class="p">()</span> <span class="p">:</span>

      <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">20</span><span class="p">)</span> <span class="p">:</span>

          <span class="k">print</span><span class="p">(</span><span class="n">i</span><span class="p">)</span>



  <span class="n">test</span><span class="p">()</span>

</code></pre></div>    </div>

    <hr />

    <p>해당 내용을 add 후 commit한 뒤 branch를 생성하여 해당 브랜치에서 다음과 같은 작업을 수행했다고 치자.</p>

    <hr />

    <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
  <span class="k">def</span> <span class="nf">test</span><span class="p">()</span> <span class="p">:</span>

      <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">30</span><span class="p">)</span> <span class="p">:</span>

          <span class="k">print</span><span class="p">(</span><span class="n">i</span><span class="p">)</span>



  <span class="n">test</span><span class="p">()</span>

</code></pre></div>    </div>

    <hr />

    <p>다시 main에서 다음과 같이 파일을 수정한다.</p>

    <hr />

    <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
  <span class="k">def</span> <span class="nf">test</span><span class="p">()</span> <span class="p">:</span>

      <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">40</span><span class="p">)</span> <span class="p">:</span>

          <span class="k">print</span><span class="p">(</span><span class="n">i</span><span class="p">)</span>



  <span class="n">test</span><span class="p">()</span>

</code></pre></div>    </div>

    <hr />

    <p>이제 main에서 두 파일을 합병한다면 다음과 같이 충돌이 발생한다.</p>

    <p><img src="https://github.com/lIllIlIIIll/Leeinformation.github.io/blob/master/_posts/image/Bootcamp/conflict.PNG?raw=true" alt="image.png" /></p>

    <p>vi 편집기로 확인하면 다음과 같이 코드 사이에 문자들이 존재하고, 중복된 부분이 존재한다.</p>

    <p><img src="https://github.com/lIllIlIIIll/Leeinformation.github.io/blob/master/_posts/image/Bootcamp/conflict_file.PNG?raw=true" alt="image.png" /></p>

    <p>이를 해결하여 add 후 commit을 한다면 충돌을 해결할 수 있다.</p>
  </li>
</ul>

<hr />

<hr />

<ul>
  <li>
    <p><strong><em>rebase</em></strong></p>

    <p>rebase는 각각의 branch를 하나의 branch로 덮어쓰는 방식을 사용한다.</p>

    <p>작업했던 branch 하나에서 다음의 명령어를 통해 진행할 수 있다.</p>

    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
  <span class="nv">$ </span>git rebase main

</code></pre></div>    </div>
  </li>
</ul>

<hr />

<h2 id="13-git-branching-strategy">1.3 git branching strategy</h2>

<hr />

<p>branch를 이용하여 git 버전을 관리하는 전략으로는 크게 3가지가 존재한다.</p>

<ul>
  <li>
    <p><strong>git flow</strong></p>

    <blockquote>
      <p>가장 전통적이고 많이 쓰이는 모델이다. 각 단계가 명확히 구분(master, develop, feature 등)되어 역할이 존재하며 복잡하다.</p>
    </blockquote>

    <blockquote>
      <p><a href="https://danielkummer.github.io/git-flow-cheatsheet/index.ko_KR.html">git flow</a></p>
    </blockquote>
  </li>
  <li>
    <p><strong>github flow</strong></p>

    <blockquote>
      <p>브랜치 모델을 단순화한 것으로, CI 의존성이 높고, github의 <strong><em>pull requests</em></strong>가 있어야 한다.</p>
    </blockquote>

    <blockquote>
      <p><a href="https://docs.github.com/en/get-started/using-github/github-flow">github flow</a></p>
    </blockquote>
  </li>
  <li>
    <p><strong>gitlab flow</strong></p>

    <blockquote>
      <p>github flow에서 배포 단계까지 추가한 것이다.</p>
    </blockquote>

    <blockquote>
      <p><a href="https://about.gitlab.com/topics/version-control/what-is-gitlab-flow/">gitlb flow</a></p>
    </blockquote>
  </li>
</ul>

<hr />

<h2 id="14-trouble-shoot">1.4 Trouble-shoot</h2>

<p>작업 중(후) 해결해야할 문제가 발생하여 branch 이동, push 취소 등의 작업을 수행하는 명령어들이다.</p>

<hr />

<ul>
  <li>
    <p><strong><em>Stash</em></strong></p>

    <p>작업 중 branch 이동이 필요하거나 작업사항을 잠시 미뤄둘 때 사용하는 명령어이다.</p>

    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
  <span class="nv">$ </span>git stash save <span class="s1">'[MESSAGE]'</span>

</code></pre></div>    </div>

    <p>위의 명령어로 작업사항을 임시 저장소에 쌓을 수 있고 다음의 명령어를 통해 가장 최근의 작업사항을 복구할 수 있다.</p>

    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
  git stash pop

</code></pre></div>    </div>

    <p>pop 뒤에 index를 넣어 원하는 작업을 복구시킬 수도 있다.</p>
  </li>
</ul>

<hr />

<hr />

<ul>
  <li>
    <p><strong><em>Undo</em></strong></p>

    <p>작업 디렉토리에서 변경사항을 취소할 때 사용하는 명령어이다.</p>

    <p>어떤 작업을 수행해서 파일이 변경되었는데, 가장 최신의 commit 상태로 되돌릴 수 있다.</p>

    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
  <span class="nv">$ </span>git restore <span class="o">[</span>FILE_NAME]

</code></pre></div>    </div>

    <p>만약 git add 명령어로 스테이징 영역에 올라간 상태라면 다음과 같이 취소할 수 있다.</p>

    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
  <span class="nv">$ </span>git restore <span class="nt">--staged</span> <span class="o">[</span>FILE_NAME]

</code></pre></div>    </div>
  </li>
</ul>

<hr />

<hr />

<ul>
  <li>
    <p><strong><em>Reset, Revert</em></strong></p>

    <p>reset은 repository에 업로드 되었을 때 이를 없었던 일로 되돌리는 명령어이다.</p>

    <p>하지만 해당 명령어를 사용한다 하더라도 나의 local과, clone한 remote repository에서만 지워질 뿐, 다른 local에 남아있기에 충돌이 발생할 가능성이 존재한다.</p>

    <p>따라서 잘못된 push로 특정 시점으로 되돌려야 하는 상황이라면 revert를 이용하여 잘못을 인정하고 되돌리는 편이 좋다.</p>

    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
  <span class="nv">$ </span>git revert <span class="nt">--no-commit</span> <span class="o">[</span>COMMIT_NAME]

</code></pre></div>    </div>

    <p>reset을 사용하여 없었던 일로 만드는 것은 사실 불가능하다. 기록도 남고, 나의 local에서만 지워지기에 팀 협업에 있어 근본적으로 문제를 해결할 방법이 되지 못한다.</p>

    <p>따라서 잘못된 점을 발견했다면 팀원들에게 전달, 문제를 인식하고 이를 해결할 방법을 모색하는 방향으로 진행해야 한다.</p>
  </li>
</ul>

<hr />

<h1 id="2-마치며">2. 마치며</h1>

<hr />

<p>이전까지 프로젝트를 진행할 때 github desktop을 굉장히 유용하게 썼었다.</p>

<p>하지만 git을 제대로 배우고 난 다음 문제를 해결하는 방법, 프로젝트 관리 등 github desktop에서 해결하기 까다로운 문제들을 git을 이용하여 해결하는 방법을 배우고 나니 git을 먼저 습득한 다음 GUI 도구들을 이용하는 것이 옳다는 생각이 들었다.</p>

<p>또한 팀 협업에 있어 <strong>역할 분담</strong> 및 <strong>소통</strong>의 중요성을 깨닫는 계기가 되었다.</p>

<hr />

<p>지금까지 나는 어떤 프로젝트 or 새로운 점을 배웠을 때, 한번에 정리하여 github blog에 업로드하였다.</p>

<p>해당 과정에서 시간이 좀 지난 작업의 경우 전부 기억하는 것은 힘들었고, 기록에 남기는 것은 더더욱 힘들었다.</p>

<p>이를 방지하기 위해 내가 오늘 어떤 점을 배웠고, 어떤 일을 하였는지 간단하게 기록하는 TIL을 작성하는 방법을 강사님께서 알려주셨다.</p>

<p>확실히 이번애 git에 대한 내용을 정리할 때도 하루하루 했던 일들을 정리하고나니 전체로 합치는 과정에서도 더욱 수월하게 진행할 수 있었다.</p>

<hr />

<p>이번 git 강의는 팀 협업에 있어 많은 것을 얻어갈 수 있는 강의였다.</p>

<p>이전에는 매번 직접적인 소통을 통해 프로젝트를 관리하는 방식을 사용하여 팀원끼리의 시간을 맞춰 진행했었다면 git을 이용하여 기록을 남겨 팀원 각각의 시간에 맞춰 작업 및 피드백을 할 수 있다는 점에서 훨씬 편리하게 관리할 수 있었다.</p>]]></content><author><name>Lee Jeong Min</name></author><category term="Bootcamp" /><category term="패스트캠퍼스" /><category term="패스트캠퍼스AI부트캠프" /><category term="업스테이지패스트캠퍼스" /><category term="UpstageAILab" /><category term="국비지원" /><category term="패스트캠퍼스업스테이지에이아이랩" /><category term="패스트캠퍼스업스테이지부트캠프" /><summary type="html"><![CDATA[이틀에 걸쳐 Git을 이용하여 Github 저장소를 관리하고 팀 협업의 전체적인 흐름을 배웠다. 이전 프로젝트를 진행할 때는 Github Desktop을 이용하여 GUI로 간편하게 작업했었는데, git을 이용하여 작업하는 방법도 배웠다. 기본적인 명령어(Ex: add, commit, push, pull)는 어느 정도 알고 있었는데, 충돌이 일어났을 때의 대처법과 잘못 push한 파일들을 되돌리는 방법 등 몰랐던 부분들을 강사분께서 알려주셨다. 내가 몰랐던 부분을 배우는지라 확실히 집중이 더 잘되었다. 1. Git 리눅스 커널 관리를 위해 만든 분산형 버전 관리 시스템으로 구조가 단순하고 빠른 속도를 자랑한다. Branch를 이용하여 비선형적 개발을 할 수 있으며 소스 코드를 주고받지 않고 동시 작업이 가능하여 생산성이 증가하고, 인터넷 연결없이 개발하다가 인터넷 연결 시 한번에 push가 가능하다. 1.1 기본 명령어 Clone git bash에서 가장 먼저 작업해주어야 하는 일은 github repository를 내 로컬과 연결해주어야 한다. 그러기 위해서 repository를 생성하고 url 주소를 가져와 다음의 명령어를 실행한다. $ git clone [REPO_ADDRESS] clone이 완료되면 다음과 같이 각각의 저장소가 복제되어 로컬에서 접속할 수 있게 된다. Status 현재 git이 add하고 있었는지, commit하고 있었는지 등 사용자가 진행하던 작업의 상태를 볼 수 있는 명령어이다. git add 및 commit, push 등 어떤 작업을 수행해도 되는지 확인 후 진행한다. $ git status 다음과 같이 더 이상 commit할 것이 없고 working tree가 비어있다면 add 작업을 진행해도 된다. add 작업 디렉토리 상의 변경 내용을 스테이징 영역에 추가하는 명령어이다. 자신이 작업했던 내용은 commit 하기 전 까지는 git 저장소의 변경 이력에는 아무것도 남지 않는다. $ git add [FILE_NAME] 다음과 같이 example.py를 add 할 수 있다. commit 변경된 코드(작업했던 내용)를 기록으로 남긴다. commit message를 통해 어떤 내용을 작업했는지 간단한 설명을 남기고, Conventional Commits를 참고하여 작성한다. $ git commit Conventional Commits는 다음과 같다. Prefix 설명 feat 기능 개발 관련 fix 오류 개선 혹은 버그 패치 docs 문서화 작업 test 테스트 코드 관련 conf 환경설정 관련 build 빌드 작업 관련 ci Continuous Integration 관련 chore 패키지 매니저, 스크립트 등 style 코드 포매팅 (세미콜론, 들여쓰기 등) 관련 명령어를 실행하면서 -m 명령어를 통해 바로 commit message를 작성할 수 있으나 해당 작업이 무슨 작업인지 헷갈릴 때에는 vi로 직접 확인한 뒤 변경하는 것이 옳다. push add 및 commit으로 기록했던 내용들을 Repository에 올리는 명령어이다. 다음과 같은 명령어로 실행할 수 있다. $ git push [REMOTE] [BRANCH] 다음과 같이 push 한 다음 Repository를 확인해보면 파일이 업로드된 것을 볼 수 있다. 1.2 .gitignore / Conflict 해결 .gitignore .gitignore 파일은 특정 파일 or 특정 디렉토리를 추적하지 않도록 명시하기 위한 파일이다. 즉, 어떤 파일을 repository에 올릴 때, 불필요한 파일들이 올라가는 것을 방지할 때 사용하는 필수적인 파일이다. .gitignore.io 위의 사이트에서 프로젝트에 사용되는 운영체제, 프로그래밍 언어, 개발 환경 등에 맞춰 생성된 내용을 붙여넣기만 해주면 된다. branch branch는 초기 코드에서 분기점을 생성하여 독립적으로 코드를 변경하도록 하는 모델이다. 다음의 명령어를 통해 현재 어떤 branch가 존재하는지 확인할 수 있다. $ git branch 또한, 다음의 명령어를 통해 새로운 브랜치를 생성한 다음 $ git branch [BRANCH_NAME] 해당 명령어를 통해 원하는 branch로 전환할 수 있다. $ git switch [BRANCH_NAME] 만약 branch를 삭제할 때에는 다음과 같이 작성해 주면 된다. $ git branch -D [BRANCH_NAME] merge 초기 코드에서 branch를 이용하여 분기점이 생성된 후 다시 합칠 때 충돌이 일어날 수 있다. 예를 들어 초기 코드에서 for 반복문이 다음과 같이 작성되어 있고 for i in range(20) : print(i) branch로 분기점이 생성된 다음 해당 branch에서 작업한 내용이 다음과 같다면 for i in range(30) : print(i) for 문의 범위가 같지 않기에 충돌이 발생할 것이다. 예를 들어 main에서 작성된 처음 파일이 다음과 같을 때, def test() : for i in range(20) : print(i) test() 해당 내용을 add 후 commit한 뒤 branch를 생성하여 해당 브랜치에서 다음과 같은 작업을 수행했다고 치자. def test() : for i in range(30) : print(i) test() 다시 main에서 다음과 같이 파일을 수정한다. def test() : for i in range(40) : print(i) test() 이제 main에서 두 파일을 합병한다면 다음과 같이 충돌이 발생한다. vi 편집기로 확인하면 다음과 같이 코드 사이에 문자들이 존재하고, 중복된 부분이 존재한다. 이를 해결하여 add 후 commit을 한다면 충돌을 해결할 수 있다. rebase rebase는 각각의 branch를 하나의 branch로 덮어쓰는 방식을 사용한다. 작업했던 branch 하나에서 다음의 명령어를 통해 진행할 수 있다. $ git rebase main 1.3 git branching strategy branch를 이용하여 git 버전을 관리하는 전략으로는 크게 3가지가 존재한다. git flow 가장 전통적이고 많이 쓰이는 모델이다. 각 단계가 명확히 구분(master, develop, feature 등)되어 역할이 존재하며 복잡하다. git flow github flow 브랜치 모델을 단순화한 것으로, CI 의존성이 높고, github의 pull requests가 있어야 한다. github flow gitlab flow github flow에서 배포 단계까지 추가한 것이다. gitlb flow 1.4 Trouble-shoot 작업 중(후) 해결해야할 문제가 발생하여 branch 이동, push 취소 등의 작업을 수행하는 명령어들이다. Stash 작업 중 branch 이동이 필요하거나 작업사항을 잠시 미뤄둘 때 사용하는 명령어이다. $ git stash save '[MESSAGE]' 위의 명령어로 작업사항을 임시 저장소에 쌓을 수 있고 다음의 명령어를 통해 가장 최근의 작업사항을 복구할 수 있다. git stash pop pop 뒤에 index를 넣어 원하는 작업을 복구시킬 수도 있다. Undo 작업 디렉토리에서 변경사항을 취소할 때 사용하는 명령어이다. 어떤 작업을 수행해서 파일이 변경되었는데, 가장 최신의 commit 상태로 되돌릴 수 있다. $ git restore [FILE_NAME] 만약 git add 명령어로 스테이징 영역에 올라간 상태라면 다음과 같이 취소할 수 있다. $ git restore --staged [FILE_NAME] Reset, Revert reset은 repository에 업로드 되었을 때 이를 없었던 일로 되돌리는 명령어이다. 하지만 해당 명령어를 사용한다 하더라도 나의 local과, clone한 remote repository에서만 지워질 뿐, 다른 local에 남아있기에 충돌이 발생할 가능성이 존재한다. 따라서 잘못된 push로 특정 시점으로 되돌려야 하는 상황이라면 revert를 이용하여 잘못을 인정하고 되돌리는 편이 좋다. $ git revert --no-commit [COMMIT_NAME] reset을 사용하여 없었던 일로 만드는 것은 사실 불가능하다. 기록도 남고, 나의 local에서만 지워지기에 팀 협업에 있어 근본적으로 문제를 해결할 방법이 되지 못한다. 따라서 잘못된 점을 발견했다면 팀원들에게 전달, 문제를 인식하고 이를 해결할 방법을 모색하는 방향으로 진행해야 한다. 2. 마치며 이전까지 프로젝트를 진행할 때 github desktop을 굉장히 유용하게 썼었다. 하지만 git을 제대로 배우고 난 다음 문제를 해결하는 방법, 프로젝트 관리 등 github desktop에서 해결하기 까다로운 문제들을 git을 이용하여 해결하는 방법을 배우고 나니 git을 먼저 습득한 다음 GUI 도구들을 이용하는 것이 옳다는 생각이 들었다. 또한 팀 협업에 있어 역할 분담 및 소통의 중요성을 깨닫는 계기가 되었다. 지금까지 나는 어떤 프로젝트 or 새로운 점을 배웠을 때, 한번에 정리하여 github blog에 업로드하였다. 해당 과정에서 시간이 좀 지난 작업의 경우 전부 기억하는 것은 힘들었고, 기록에 남기는 것은 더더욱 힘들었다. 이를 방지하기 위해 내가 오늘 어떤 점을 배웠고, 어떤 일을 하였는지 간단하게 기록하는 TIL을 작성하는 방법을 강사님께서 알려주셨다. 확실히 이번애 git에 대한 내용을 정리할 때도 하루하루 했던 일들을 정리하고나니 전체로 합치는 과정에서도 더욱 수월하게 진행할 수 있었다. 이번 git 강의는 팀 협업에 있어 많은 것을 얻어갈 수 있는 강의였다. 이전에는 매번 직접적인 소통을 통해 프로젝트를 관리하는 방식을 사용하여 팀원끼리의 시간을 맞춰 진행했었다면 git을 이용하여 기록을 남겨 팀원 각각의 시간에 맞춰 작업 및 피드백을 할 수 있다는 점에서 훨씬 편리하게 관리할 수 있었다.]]></summary></entry><entry><title type="html">[프로그래머스] 점프와 순간이동</title><link href="https://leeinformation.github.io/Leeinformation.github.io/coding_test/Jump_Tp/" rel="alternate" type="text/html" title="[프로그래머스] 점프와 순간이동" /><published>2025-04-24T00:00:00+00:00</published><updated>2025-04-24T00:00:00+00:00</updated><id>https://leeinformation.github.io/Leeinformation.github.io/coding_test/Jump_Tp</id><content type="html" xml:base="https://leeinformation.github.io/Leeinformation.github.io/coding_test/Jump_Tp/"><![CDATA[<head>
  <style>
    table.dataframe {
      white-space: normal;
      width: 100%;
      height: 240px;
      display: block;
      overflow: auto;
      font-family: Arial, sans-serif;
      font-size: 0.9rem;
      line-height: 20px;
      text-align: center;
      border: 0px !important;
    }

    table.dataframe th {
      text-align: center;
      font-weight: bold;
      padding: 8px;
    }

    table.dataframe td {
      text-align: center;
      padding: 8px;
    }

    table.dataframe tr:hover {
      background: #b8d1f3; 
    }

    .output_prompt {
      overflow: auto;
      font-size: 0.9rem;
      line-height: 1.45;
      border-radius: 0.3rem;
      -webkit-overflow-scrolling: touch;
      padding: 0.8rem;
      margin-top: 0;
      margin-bottom: 15px;
      font: 1rem Consolas, "Liberation Mono", Menlo, Courier, monospace;
      color: $code-text-color;
      border: solid 1px $border-color;
      border-radius: 0.3rem;
      word-break: normal;
      white-space: pre;
    }

  .dataframe tbody tr th:only-of-type {
      vertical-align: middle;
  }

  .dataframe tbody tr th {
      vertical-align: top;
  }

  .dataframe thead th {
      text-align: center !important;
      padding: 8px;
  }

  .page__content p {
      margin: 0 0 0px !important;
  }

  .page__content p > strong {
    font-size: 0.8rem !important;
  }

  </style>
</head>

<h1 id="1-생각의-흐름">1. 생각의 흐름</h1>

<p>처음 문제를 봤을 때 탐욕 알고리즘을 사용해야하나 싶었다.</p>

<p>맨 처음 한 칸은 무조건 JUMP로 가고, 목표보다 작은 최대값을 가질 때까지 TP하고 나머지는 JUMP하면 되려나? 라고 생각했다.</p>

<p>그런데 뭔가 좀 이상했다. 간단하게 생각해봐도 이는 최적해를 도출할 수 없을 것 같았다.</p>

<p>2배를 간다고 했으므로…</p>

<ol>
  <li>
    <p>처음 한칸만 JUMP : 1 → 2 → 4 → 8 → 16 …</p>
  </li>
  <li>
    <p>세 칸 JUMP : 3 → 6 → 12 → 24 → 48 …</p>
  </li>
</ol>

<p>여기서 만약에 가야하는 목표 거리가 21이라고 생각해 본다면 첫 번째 방법은 JUMP를 7번, 2번째 방법은 JUMP를 3번한다.</p>

<p>따라서 탐욕 알고리즘을 버리고 그냥 간단하게 생각해보기로 했다.</p>

<hr />

<p>n이 짝수라면 다음과 같고</p>

<ul>
  <li>n 일 때 사용한 건전지량 == n / 2 일때 사용한 건전지량</li>
</ul>

<p>n이 홀수라면 다음과 같다.</p>

<ul>
  <li>n 일 때 사용한 건전지량 == (n-1) / 2일때 사용한 건전지량 + 1</li>
</ul>

<p>결국 반복문으로 간단하게 돌려주면 해결할 수 있는 쉬운 문제였다.</p>

<h1 id="2-코드-구현">2. 코드 구현</h1>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">solution</span><span class="p">(</span><span class="n">n</span><span class="p">)</span> <span class="p">:</span>
    <span class="n">count</span> <span class="o">=</span> <span class="mi">0</span>
    
    <span class="k">while</span> <span class="n">n</span> <span class="o">&gt;</span> <span class="mi">0</span> <span class="p">:</span>
        <span class="k">if</span> <span class="n">n</span> <span class="o">%</span> <span class="mi">2</span> <span class="o">!=</span> <span class="mi">0</span> <span class="p">:</span>
            <span class="n">count</span> <span class="o">+=</span> <span class="mi">1</span>
            <span class="n">n</span> <span class="o">-=</span> <span class="mi">1</span>
        <span class="n">n</span> <span class="o">//=</span> <span class="mi">2</span>
    <span class="k">return</span> <span class="n">count</span>
</code></pre></div></div>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">solution</span><span class="p">(</span><span class="mi">5000</span><span class="p">)</span>
</code></pre></div></div>

<pre>
5
</pre>]]></content><author><name>Lee Jeong Min</name></author><category term="Coding_Test" /><category term="python" /><category term="coding" /><summary type="html"><![CDATA[1. 생각의 흐름 처음 문제를 봤을 때 탐욕 알고리즘을 사용해야하나 싶었다. 맨 처음 한 칸은 무조건 JUMP로 가고, 목표보다 작은 최대값을 가질 때까지 TP하고 나머지는 JUMP하면 되려나? 라고 생각했다. 그런데 뭔가 좀 이상했다. 간단하게 생각해봐도 이는 최적해를 도출할 수 없을 것 같았다. 2배를 간다고 했으므로… 처음 한칸만 JUMP : 1 → 2 → 4 → 8 → 16 … 세 칸 JUMP : 3 → 6 → 12 → 24 → 48 … 여기서 만약에 가야하는 목표 거리가 21이라고 생각해 본다면 첫 번째 방법은 JUMP를 7번, 2번째 방법은 JUMP를 3번한다. 따라서 탐욕 알고리즘을 버리고 그냥 간단하게 생각해보기로 했다. n이 짝수라면 다음과 같고 n 일 때 사용한 건전지량 == n / 2 일때 사용한 건전지량 n이 홀수라면 다음과 같다. n 일 때 사용한 건전지량 == (n-1) / 2일때 사용한 건전지량 + 1 결국 반복문으로 간단하게 돌려주면 해결할 수 있는 쉬운 문제였다. 2. 코드 구현 def solution(n) : count = 0 while n &gt; 0 : if n % 2 != 0 : count += 1 n -= 1 n //= 2 return count solution(5000) 5]]></summary></entry><entry><title type="html">[프로그래머스] 다음 큰 숫자</title><link href="https://leeinformation.github.io/Leeinformation.github.io/coding_test/Next_Number/" rel="alternate" type="text/html" title="[프로그래머스] 다음 큰 숫자" /><published>2025-04-24T00:00:00+00:00</published><updated>2025-04-24T00:00:00+00:00</updated><id>https://leeinformation.github.io/Leeinformation.github.io/coding_test/Next_Number</id><content type="html" xml:base="https://leeinformation.github.io/Leeinformation.github.io/coding_test/Next_Number/"><![CDATA[<head>
  <style>
    table.dataframe {
      white-space: normal;
      width: 100%;
      height: 240px;
      display: block;
      overflow: auto;
      font-family: Arial, sans-serif;
      font-size: 0.9rem;
      line-height: 20px;
      text-align: center;
      border: 0px !important;
    }

    table.dataframe th {
      text-align: center;
      font-weight: bold;
      padding: 8px;
    }

    table.dataframe td {
      text-align: center;
      padding: 8px;
    }

    table.dataframe tr:hover {
      background: #b8d1f3; 
    }

    .output_prompt {
      overflow: auto;
      font-size: 0.9rem;
      line-height: 1.45;
      border-radius: 0.3rem;
      -webkit-overflow-scrolling: touch;
      padding: 0.8rem;
      margin-top: 0;
      margin-bottom: 15px;
      font: 1rem Consolas, "Liberation Mono", Menlo, Courier, monospace;
      color: $code-text-color;
      border: solid 1px $border-color;
      border-radius: 0.3rem;
      word-break: normal;
      white-space: pre;
    }

  .dataframe tbody tr th:only-of-type {
      vertical-align: middle;
  }

  .dataframe tbody tr th {
      vertical-align: top;
  }

  .dataframe thead th {
      text-align: center !important;
      padding: 8px;
  }

  .page__content p {
      margin: 0 0 0px !important;
  }

  .page__content p > strong {
    font-size: 0.8rem !important;
  }

  </style>
</head>

<h1 id="1-생각의-흐름">1. 생각의 흐름</h1>

<p>문제가 쉬웠다.</p>

<ol>
  <li>
    <p>주어진 n값을을 2진수로 변환하고, 1의 갯수를 센다.</p>
  </li>
  <li>
    <p>n += 1 을 해주며 1의 과정과 똑같은 과정을 반복, n과 1의 갯수가 같은지 비교</p>
  </li>
  <li>
    <p>같다면 반복문을 멈추고 return</p>
  </li>
</ol>

<h1 id="2-코드-구현">2. 코드 구현</h1>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">solution</span><span class="p">(</span><span class="n">n</span><span class="p">):</span>
    <span class="n">binary_number</span> <span class="o">=</span> <span class="nb">bin</span><span class="p">(</span><span class="n">n</span><span class="p">)[</span><span class="mi">2</span><span class="p">:]</span>
    <span class="n">one_count_n</span> <span class="o">=</span> <span class="n">binary_number</span><span class="p">.</span><span class="n">count</span><span class="p">(</span><span class="s">"1"</span><span class="p">)</span>
    
    <span class="k">while</span> <span class="bp">True</span> <span class="p">:</span>
        <span class="n">n</span> <span class="o">+=</span> <span class="mi">1</span>
        <span class="k">if</span> <span class="n">one_count_n</span> <span class="o">==</span> <span class="nb">bin</span><span class="p">(</span><span class="n">n</span><span class="p">)[</span><span class="mi">2</span><span class="p">:].</span><span class="n">count</span><span class="p">(</span><span class="s">"1"</span><span class="p">)</span> <span class="p">:</span>
            <span class="k">break</span>
    <span class="k">return</span> <span class="n">n</span>
</code></pre></div></div>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">solution</span><span class="p">(</span><span class="mi">78</span><span class="p">)</span>
</code></pre></div></div>

<pre>
83
</pre>]]></content><author><name>Lee Jeong Min</name></author><category term="Coding_Test" /><category term="python" /><category term="coding" /><summary type="html"><![CDATA[1. 생각의 흐름 문제가 쉬웠다. 주어진 n값을을 2진수로 변환하고, 1의 갯수를 센다. n += 1 을 해주며 1의 과정과 똑같은 과정을 반복, n과 1의 갯수가 같은지 비교 같다면 반복문을 멈추고 return 2. 코드 구현 def solution(n): binary_number = bin(n)[2:] one_count_n = binary_number.count("1") while True : n += 1 if one_count_n == bin(n)[2:].count("1") : break return n solution(78) 83]]></summary></entry><entry><title type="html">[프로그래머스] 유사 칸토어 비트열</title><link href="https://leeinformation.github.io/Leeinformation.github.io/coding_test/Pseudo_Kantor/" rel="alternate" type="text/html" title="[프로그래머스] 유사 칸토어 비트열" /><published>2025-04-24T00:00:00+00:00</published><updated>2025-04-24T00:00:00+00:00</updated><id>https://leeinformation.github.io/Leeinformation.github.io/coding_test/Pseudo_Kantor</id><content type="html" xml:base="https://leeinformation.github.io/Leeinformation.github.io/coding_test/Pseudo_Kantor/"><![CDATA[<head>
  <style>
    table.dataframe {
      white-space: normal;
      width: 100%;
      height: 240px;
      display: block;
      overflow: auto;
      font-family: Arial, sans-serif;
      font-size: 0.9rem;
      line-height: 20px;
      text-align: center;
      border: 0px !important;
    }

    table.dataframe th {
      text-align: center;
      font-weight: bold;
      padding: 8px;
    }

    table.dataframe td {
      text-align: center;
      padding: 8px;
    }

    table.dataframe tr:hover {
      background: #b8d1f3; 
    }

    .output_prompt {
      overflow: auto;
      font-size: 0.9rem;
      line-height: 1.45;
      border-radius: 0.3rem;
      -webkit-overflow-scrolling: touch;
      padding: 0.8rem;
      margin-top: 0;
      margin-bottom: 15px;
      font: 1rem Consolas, "Liberation Mono", Menlo, Courier, monospace;
      color: $code-text-color;
      border: solid 1px $border-color;
      border-radius: 0.3rem;
      word-break: normal;
      white-space: pre;
    }

  .dataframe tbody tr th:only-of-type {
      vertical-align: middle;
  }

  .dataframe tbody tr th {
      vertical-align: top;
  }

  .dataframe thead th {
      text-align: center !important;
      padding: 8px;
  }

  .page__content p {
      margin: 0 0 0px !important;
  }

  .page__content p > strong {
    font-size: 0.8rem !important;
  }

  </style>
</head>

<h1 id="1-생각의-흐름">1. 생각의 흐름</h1>

<p>문제의 조건을 확인해보면 다음과 같다.</p>

\[1 ≤ n ≤ 20\]

\[1 ≤ l, r ≤ 5^n\]

\[l ≤ r &lt; l + 10,000,000\]

<p>이는 유사 칸토어 비트열로 변환하고 구간 내 1의 갯수를 센다는 것은 불가능함을 보여준다.</p>

<p>n이 20이면 $5^{20}$, 이는 필연적으로 규칙을 찾아야함을 의미한다.</p>

<p>그렇기에 먼저 1을 11011로 치환하는 과정을 살펴보았다.</p>

<hr />

<p>가운데, 즉 index가 2일 때 0이 되고 나머지는 1로 치환되는 것을 볼 수 있다.</p>

<p>즉 첫 번째 5개의 수와 두 번째 5개의 수에 대해서만 어떤 수를 5로 나눴을 때 나머지가 2라면 0으로 치환된다.</p>

<p>나머지 11번째 수 부터 어떻게 해야할 지 고민하던 중 다음과 같은 규칙을 발견했다.</p>

<table>
  <tbody>
    <tr>
      <td>데이터</td>
      <td>표현식</td>
      <td>n 값</td>
    </tr>
  </tbody>
</table>

<p>|—————————–|———–|——-|</p>

<table>
  <tbody>
    <tr>
      <td>1     1     0     1     1</td>
      <td>$4^1$</td>
      <td>n = 1</td>
    </tr>
  </tbody>
</table>

<table>
  <tbody>
    <tr>
      <td>11011 11011 00000 11011 11011</td>
      <td>$4^2$</td>
      <td>n = 2</td>
    </tr>
  </tbody>
</table>

<table>
  <tbody>
    <tr>
      <td>5<sup>3</sup>개</td>
      <td>$4^3$</td>
      <td>n = 3</td>
    </tr>
  </tbody>
</table>

<p>n = 2 일때를 보면 각각의 인덱스가 가지는 수를 보면…</p>

<ul>
  <li>
    <p>2, 7, 10, 11, 12, 13, 14, 17, … : 1</p>
  </li>
  <li>
    <p>그 외 나머지 : 0</p>
  </li>
</ul>

<p>해당 수의 규칙이 처음에는 보이지 않았다. 하지만 문제의 제한사항에서 $5^n$을 보고 5진법으로 변환하면 어떨까라는 생각을 하게 되었고</p>

<p>실제로 5진법 변환함수를 만들어 해당 수들을 넣어보았다.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">convert_5</span><span class="p">(</span><span class="n">n</span><span class="p">)</span> <span class="p">:</span>
    <span class="n">five_number</span> <span class="o">=</span> <span class="s">''</span>
    
    <span class="k">while</span> <span class="n">n</span> <span class="o">&gt;</span> <span class="mi">0</span> <span class="p">:</span>
        <span class="n">quotient</span> <span class="o">=</span> <span class="n">n</span> <span class="o">//</span> <span class="mi">5</span>
        <span class="n">reminder</span> <span class="o">=</span> <span class="n">n</span> <span class="o">%</span> <span class="mi">5</span>
        <span class="n">n</span> <span class="o">=</span> <span class="n">quotient</span>
        <span class="n">five_number</span> <span class="o">+=</span> <span class="nb">str</span><span class="p">(</span><span class="n">reminder</span><span class="p">)</span>
        
    <span class="k">return</span> <span class="n">five_number</span><span class="p">[::</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span>

<span class="n">convert_5</span><span class="p">(</span><span class="mi">7</span><span class="p">)</span>
</code></pre></div></div>

<pre>
'12'
</pre>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">list</span> <span class="o">=</span> <span class="p">[</span><span class="mi">2</span><span class="p">,</span> <span class="mi">7</span><span class="p">,</span> <span class="mi">10</span><span class="p">,</span> <span class="mi">11</span><span class="p">,</span> <span class="mi">12</span><span class="p">,</span> <span class="mi">13</span><span class="p">,</span> <span class="mi">14</span><span class="p">,</span> <span class="mi">17</span><span class="p">]</span>

<span class="k">for</span> <span class="n">value</span> <span class="ow">in</span> <span class="nb">list</span> <span class="p">:</span>
    <span class="k">print</span><span class="p">(</span><span class="n">convert_5</span><span class="p">(</span><span class="n">value</span><span class="p">))</span>
</code></pre></div></div>

<pre>
2
12
20
21
22
23
24
32
</pre>
<p>이제 보일텐데, 해당 수들은 5진법으로 변환했을 때, 전부 2라는 수를 가지고 있다.</p>

<p>따라서 이에 맞춰 주어진 구간의 값들을 전부 5진법으로 변환하고 안에 2가 존재한다면 count를 1 늘려주면 되겠구나 생각했다.</p>

<p>하지만 이런 방식도 시간 초과가 떴다.</p>

<p>그래서 생각했던게 굳이 5진법으로 전부 표현하지 않고 변환 중 2가 나오면 break를 걸어주면 되겠다하고 실제로 코드에 옮겼다.</p>

<h1 id="2-코드-구현">2. 코드 구현</h1>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">convert_5</span><span class="p">(</span><span class="n">n</span><span class="p">)</span> <span class="p">:</span>
    <span class="n">five_number</span> <span class="o">=</span> <span class="s">''</span>
    
    <span class="k">while</span> <span class="n">n</span> <span class="o">&gt;</span> <span class="mi">0</span> <span class="p">:</span>
        <span class="n">quotient</span> <span class="o">=</span> <span class="n">n</span> <span class="o">//</span> <span class="mi">5</span>
        <span class="n">reminder</span> <span class="o">=</span> <span class="n">n</span> <span class="o">%</span> <span class="mi">5</span>
        <span class="n">n</span> <span class="o">=</span> <span class="n">quotient</span>

        <span class="k">if</span> <span class="n">reminder</span> <span class="o">==</span> <span class="mi">2</span> <span class="p">:</span>
            <span class="k">break</span>
        
    <span class="k">else</span> <span class="p">:</span>
        <span class="k">return</span> <span class="bp">True</span>
        
    <span class="k">return</span> <span class="bp">False</span>

<span class="k">def</span> <span class="nf">solution</span><span class="p">(</span><span class="n">n</span><span class="p">,</span> <span class="n">l</span><span class="p">,</span> <span class="n">r</span><span class="p">):</span>
    <span class="n">count</span> <span class="o">=</span> <span class="mi">0</span>
    <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">l</span><span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="n">r</span><span class="p">)</span> <span class="p">:</span>
        <span class="n">x</span> <span class="o">=</span> <span class="n">convert_5</span><span class="p">(</span><span class="n">i</span><span class="p">)</span>
        <span class="k">if</span> <span class="n">x</span> <span class="p">:</span>
            <span class="n">count</span> <span class="o">+=</span> <span class="mi">1</span>
            
    <span class="k">return</span> <span class="n">count</span>
</code></pre></div></div>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">solution</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">17</span><span class="p">)</span>
</code></pre></div></div>

<pre>
8
</pre>]]></content><author><name>Lee Jeong Min</name></author><category term="Coding_Test" /><category term="python" /><category term="coding" /><summary type="html"><![CDATA[1. 생각의 흐름 문제의 조건을 확인해보면 다음과 같다. \[1 ≤ n ≤ 20\] \[1 ≤ l, r ≤ 5^n\] \[l ≤ r &lt; l + 10,000,000\] 이는 유사 칸토어 비트열로 변환하고 구간 내 1의 갯수를 센다는 것은 불가능함을 보여준다. n이 20이면 $5^{20}$, 이는 필연적으로 규칙을 찾아야함을 의미한다. 그렇기에 먼저 1을 11011로 치환하는 과정을 살펴보았다. 가운데, 즉 index가 2일 때 0이 되고 나머지는 1로 치환되는 것을 볼 수 있다. 즉 첫 번째 5개의 수와 두 번째 5개의 수에 대해서만 어떤 수를 5로 나눴을 때 나머지가 2라면 0으로 치환된다. 나머지 11번째 수 부터 어떻게 해야할 지 고민하던 중 다음과 같은 규칙을 발견했다. 데이터 표현식 n 값 |—————————–|———–|——-| 1 1 0 1 1 $4^1$ n = 1 11011 11011 00000 11011 11011 $4^2$ n = 2 53개 $4^3$ n = 3 n = 2 일때를 보면 각각의 인덱스가 가지는 수를 보면… 2, 7, 10, 11, 12, 13, 14, 17, … : 1 그 외 나머지 : 0 해당 수의 규칙이 처음에는 보이지 않았다. 하지만 문제의 제한사항에서 $5^n$을 보고 5진법으로 변환하면 어떨까라는 생각을 하게 되었고 실제로 5진법 변환함수를 만들어 해당 수들을 넣어보았다. def convert_5(n) : five_number = '' while n &gt; 0 : quotient = n // 5 reminder = n % 5 n = quotient five_number += str(reminder) return five_number[::-1] convert_5(7) '12' list = [2, 7, 10, 11, 12, 13, 14, 17] for value in list : print(convert_5(value)) 2 12 20 21 22 23 24 32 이제 보일텐데, 해당 수들은 5진법으로 변환했을 때, 전부 2라는 수를 가지고 있다. 따라서 이에 맞춰 주어진 구간의 값들을 전부 5진법으로 변환하고 안에 2가 존재한다면 count를 1 늘려주면 되겠구나 생각했다. 하지만 이런 방식도 시간 초과가 떴다. 그래서 생각했던게 굳이 5진법으로 전부 표현하지 않고 변환 중 2가 나오면 break를 걸어주면 되겠다하고 실제로 코드에 옮겼다. 2. 코드 구현 def convert_5(n) : five_number = '' while n &gt; 0 : quotient = n // 5 reminder = n % 5 n = quotient if reminder == 2 : break else : return True return False def solution(n, l, r): count = 0 for i in range(l-1, r) : x = convert_5(i) if x : count += 1 return count solution(2, 4, 17) 8]]></summary></entry></feed>