<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>외부 저장소</title>
    <link>https://aliencoder.tistory.com/</link>
    <description>내가 까먹을까봐 만든 블로그</description>
    <language>ko</language>
    <pubDate>Tue, 7 Jul 2026 10:03:14 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>AlienCoder</managingEditor>
    <image>
      <title>외부 저장소</title>
      <url>https://tistory1.daumcdn.net/tistory/5057158/attach/31fd96ec0edf48f78c7be5f19bc09954</url>
      <link>https://aliencoder.tistory.com</link>
    </image>
    <item>
      <title>CRLF(CR + LF) vs LF(Line Feed)</title>
      <link>https://aliencoder.tistory.com/244</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;개발을 하다 보면 협업 과정에서 코드는 똑같은데 에러가 나는 상황이 종종 발생한다. 특히 Windows와 macOS/Linux 환경을 오가면서 작업할 때 자주 발생하는 문제 중 하나가 바로 &lt;b&gt;줄 바꿈(Line Ending) 문제&lt;/b&gt;이다. 이때 등장하는 개념이 &lt;b&gt;CRLF와 LF&lt;/b&gt;이다. 결론부터 말하면 CRLF와 LF는 모두 줄 바꿈을 의미하지만, &lt;b&gt;표현 방식이 다르고 운영체제에 따라 사용하는 방식이 다르&lt;/b&gt;다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;CRLF(CR + LF)와 LF(Line Feed)&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;CRLF&lt;/b&gt;와 &lt;b&gt;LF&lt;/b&gt;는 모두 &lt;b&gt;개행 문자(Line Ending)&lt;/b&gt;이다. 즉, 한 줄이 끝나고 다음 줄로 넘어간다는 것을 표현하는 방식이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각각의 정의는 다음과 같다. 그 전에 &lt;b&gt;CR(Carriage Return)&lt;/b&gt;과 &lt;b&gt;LF(Line Feed)&lt;/b&gt;가 뭔지 간단히 정리하면 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;CR(Carriage Return, ( \texttt{\r} ))&lt;/b&gt;&lt;br /&gt;&amp;rarr; 커서를 현재 줄의 맨 앞으로 이동&lt;/li&gt;
&lt;li&gt;&lt;b&gt;LF(Line Feed, ( \texttt{\n} ))&lt;/b&gt;&lt;br /&gt;&amp;rarr; 한 줄 아래로 이동&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;CRLF(\r\n)&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;CR + LF를 함께 사용&lt;/li&gt;
&lt;li&gt;Windows에서 기본 사용&lt;/li&gt;
&lt;li&gt;두 번의 동작 수행
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;줄의 시작으로 이동 (CR)&lt;/li&gt;
&lt;li&gt;다음 줄로 이동 (LF)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;CRLF&amp;nbsp;예시&amp;nbsp;(Windows)&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1777980480535&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Hello\r\nWorld

1. Hello 출력
2. \r &amp;rarr; 커서를 줄 맨 앞으로 이동
3. \n &amp;rarr; 다음 줄로 이동

result:
Hello
World&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;LF(\n)&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;LF 하나만 사용&lt;/li&gt;
&lt;li&gt;Linux, macOS에서 기본 사용&lt;/li&gt;
&lt;li&gt;한 번의 동작만 수행
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;다음 줄로 이동&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;LF&amp;nbsp;예시&amp;nbsp;(Linux&amp;nbsp;/&amp;nbsp;macOS)&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1777980531676&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Hello\nWorld

1. Hello 출력
2. \n &amp;rarr; 다음 줄로 이동

result:
Hello
World&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;차이를 한 줄로 정리하면 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;CRLF&amp;nbsp;&amp;rarr;&amp;nbsp;앞으로&amp;nbsp;이동&amp;nbsp;+&amp;nbsp;아래로&amp;nbsp;이동&lt;/li&gt;
&lt;li&gt;LF &amp;rarr; 아래로 이동만&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, CRLF는 두 동작을 모두 수행하고, LF는 하나의 동작만 수행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 개념은 의외로 &lt;b&gt;타자기에서 유래&lt;/b&gt;했다. 타자기로 문서를 작성할 때 줄을 바꾸려면 두 가지 동작이 필요했다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;캐리지를 왼쪽으로 이동 (CR)&lt;/li&gt;
&lt;li&gt;종이를 한 줄 올림 (LF)&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 줄바꿈은 원래 두 단계 작업이었다.&lt;/p&gt;
&lt;pre class=&quot;abnf&quot;&gt;&lt;code&gt;CR &amp;rarr; 줄의 시작으로 이동
LF &amp;rarr; 다음 줄로 이동&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 개념이 그대로 컴퓨터에 들어오면서 CRLF와 LF가 나뉘게 되었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;OS별 줄바꿈 방식 차이&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;운영체제마다 기본 줄 바꿈 방식이 다르다.&lt;/p&gt;
&lt;pre class=&quot;taggerscript&quot;&gt;&lt;code&gt;Windows        &amp;rarr; CRLF (\r\n)
Linux / Unix   &amp;rarr; LF   (\n)
macOS          &amp;rarr; LF   (\n)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 차이 때문에 협업 시 문제가 발생한다. CRLF와 LF의 차이는 단순한 개념이지만, 실제로는 다음과 같은 문제를 만든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1. 실행 오류&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Linux에서 실행해야 하는 스크립트가 CRLF로 저장된 경우 CR(\r)이&amp;nbsp;포함되어&amp;nbsp;에러&amp;nbsp;발생한다.&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;/bin/bash^M: bad interpreter&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2. Git 충돌 문제&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CRLF와 LF는 내부적으로 다른 값이다. 따라서&amp;nbsp;Git에서는 이를 &lt;b&gt;다른 변경으로 인식&lt;/b&gt;한다&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;코드 수정 안 했는데도 변경됨 표시&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3. 협업 문제&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Windows &amp;rarr; CRLF&lt;/li&gt;
&lt;li&gt;Mac/Linux &amp;rarr; LF&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 서로 다른 환경에서 파일을 주고받으면 줄 바꿈이 깨지게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실무에서는 보통 LF를 사용하도록 통일한다. 이유는 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;대부분의 서버 환경(Linux)이 LF 사용&lt;/li&gt;
&lt;li&gt;바이트 사용량이 적음&lt;/li&gt;
&lt;li&gt;Git 충돌 최소화&lt;/li&gt;
&lt;li&gt;협업 시 안정적&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CRLF는 ( \texttt{\r\n} )로 두 문자이고, LF는 ( \texttt{\n} ) 하나이기 때문에 데이터도 더 간결하다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;4. 해결 방법&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. IDE에서 통일&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;VSCode: LF/CRLF 설정 변경&lt;/li&gt;
&lt;li&gt;IntelliJ: Line Separator 설정&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. Git 설정&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;git config --global core.autocrlf input&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;LF 기준으로 관리&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;3. lint / formatter 설정&lt;/b&gt;&lt;/h4&gt;
&lt;pre class=&quot;scheme&quot;&gt;&lt;code&gt;'linebreak-style': ['error', 'unix']&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;LF 강제&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정리하자면 아래와 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;CRLF와 LF는 모두 줄바꿈 문자이다&lt;/li&gt;
&lt;li&gt;CRLF는 두 동작, LF는 한 동작이다&lt;/li&gt;
&lt;li&gt;Windows는 CRLF, Linux/macOS는 LF를 사용한다&lt;/li&gt;
&lt;li&gt;환경이 다르면 실행 오류나 Git 충돌이 발생할 수 있다&lt;/li&gt;
&lt;li&gt;실무에서는 LF로 통일하는 것이 일반적이다&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CRLF와 LF는 단순한 개행 문자 차이지만, 실제 개발 환경에서는 중요한 문제를 만든다. 특히 협업 환경에서는 이 차이를 이해하지 못하면 실행 오류, &lt;span style=&quot;letter-spacing: 0px;&quot;&gt;Git 충돌, &lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;디버깅 시간 증가와&lt;/span&gt; 같은 문제가 발생한다. 잘 기억해 두고 이 일로 시간 허비하지 않도록 주의가 필요할 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;참고 자료&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://brunch.co.kr/@hongjyoun/97&quot;&gt;https://brunch.co.kr/@hongjyoun/97&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://velog.io/@dev_yong/CRLF%EC%99%80-LF%EC%B0%A8%EC%9D%B4%EC%9D%98-%EC%9D%B4%ED%95%B4&quot;&gt;https://velog.io/@dev_yong/CRLF와-LF차이의-이해&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://42jerrykim.github.io/post/2024-10-15-crlf/&quot;&gt;https://42jerrykim.github.io/post/2024-10-15-crlf/&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Computer Science/Software Engineering</category>
      <category>CR</category>
      <category>crlf</category>
      <category>LF</category>
      <author>AlienCoder</author>
      <guid isPermaLink="true">https://aliencoder.tistory.com/244</guid>
      <comments>https://aliencoder.tistory.com/244#entry244comment</comments>
      <pubDate>Thu, 7 May 2026 08:20:08 +0900</pubDate>
    </item>
    <item>
      <title>Claude Code Skills vs Codex Skills: 구조와 차이 완전 정리</title>
      <link>https://aliencoder.tistory.com/243</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1342&quot; data-origin-height=&quot;383&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/k1nj5/dJMcaayqMm0/LOPK7i3FB3WuSZOpsNx0Xk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/k1nj5/dJMcaayqMm0/LOPK7i3FB3WuSZOpsNx0Xk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/k1nj5/dJMcaayqMm0/LOPK7i3FB3WuSZOpsNx0Xk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fk1nj5%2FdJMcaayqMm0%2FLOPK7i3FB3WuSZOpsNx0Xk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;709&quot; height=&quot;202&quot; data-origin-width=&quot;1342&quot; data-origin-height=&quot;383&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Claude Code&lt;/b&gt;나 &lt;b&gt;Codex&lt;/b&gt; 같은 AI 기반 개발 도구를 사용하다 보면 단순한 프롬프트만으로는 반복 작업이나 복잡한 흐름을 처리하기 어려운 순간이 온다. 이때 사용하는 개념이 바로 &lt;b&gt;Skill(스킬)&lt;/b&gt;이다. 결론부터 말하면 Skill은 &lt;b&gt;AI에게 특정 작업을 수행하는 방법을 정의해두고 재사용하는 구조&lt;/b&gt;이다. 단순한 프롬프트를 넘어, 일종의 &amp;ldquo;재사용 가능한 작업 단위&amp;rdquo;라고 볼 수 있다. 이 글에서는 Claude Skill과 Codex Skill의 개념, 구조, 동작 방식, 그리고 실제 활용 예시까지 정리한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Skill이란 무엇인가&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Skill은 AI가 특정 작업을 수행할 때 사용하는 &lt;b&gt;정의된 실행 단위&lt;/b&gt;이다. 간단히 표현하면 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;사용자 요청
  &amp;darr;
Skill 선택
  &amp;darr;
정의된 작업 실행
  &amp;darr;
결과 반환&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 매번 프롬프트로 동일한 작업을 설명하는 대신, Skill로 정의해두고 필요할 때 호출하는 방식이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;왜 Skill이 필요한가&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI에게 동일한 작업을 반복적으로 요청할 때 다음 문제가 발생한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;매번 프롬프트를 다시 작성해야 한다&lt;/li&gt;
&lt;li&gt;작업 방식이 일관되지 않을 수 있다&lt;/li&gt;
&lt;li&gt;복잡한 작업은 실수 가능성이 높다&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Skill을 사용하면 다음과 같이 바뀐다.&lt;/p&gt;
&lt;pre class=&quot;sqf&quot;&gt;&lt;code&gt;프롬프트 반복 &amp;rarr; Skill 호출&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정리하면 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;재사용 가능&lt;/li&gt;
&lt;li&gt;일관된 결과&lt;/li&gt;
&lt;li&gt;복잡한 작업 캡슐화&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Claude Skill 구조&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Claude Code에서 Skill은 보통 &lt;code&gt;.claude/skills&lt;/code&gt; 디렉토리에 정의된다. 구조는 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;applescript&quot;&gt;&lt;code&gt;.claude/
  skills/
    my-skill/
      skill.yaml
      prompt.md
      script.sh&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 파일의 역할은 다음과 같다.&lt;/p&gt;
&lt;table style=&quot;height: 153px;&quot; width=&quot;488&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;&lt;b&gt;파일&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;&lt;b&gt;역할&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;skill.yaml&lt;/td&gt;
&lt;td&gt;Skill 정의 (이름, 설명, 실행 조건)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;prompt.md&lt;/td&gt;
&lt;td&gt;AI에게 전달되는 프롬프트&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;script.sh&lt;/td&gt;
&lt;td&gt;실제 실행 로직&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Skill 동작 흐름&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Claude에서 Skill이 실행되는 흐름은 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;사용자 입력
  &amp;darr;
Skill 선택 (자동 또는 명시)
  &amp;darr;
prompt.md 실행
  &amp;darr;
script 실행
  &amp;darr;
결과 반환&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, Skill은 단순 프롬프트가 아니라 &lt;b&gt;프롬프트 + 실행 로직&lt;/b&gt;의 조합이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Claude Skill 예시&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. skill.yaml&lt;/h3&gt;
&lt;pre class=&quot;avrasm&quot;&gt;&lt;code&gt;name: format-code
description: 코드 포맷팅 수행&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. prompt.md&lt;/h3&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;다음 코드를 포맷팅하라&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. script.sh&lt;/h3&gt;
&lt;pre class=&quot;bash&quot;&gt;&lt;code&gt;#!/bin/bash
black .&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용 흐름은 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;사용자: 코드 정리해줘
  &amp;darr;
Claude &amp;rarr; format-code Skill 선택
  &amp;darr;
black 실행&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Codex Skill 구조&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Codex에서도 Skill 개념은 유사하다. 핵심은 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;특정 작업을 정의&lt;/li&gt;
&lt;li&gt;모델이 자동으로 선택&lt;/li&gt;
&lt;li&gt;실행 로직 포함&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구조는 다음과 같이 이해할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;nginx&quot;&gt;&lt;code&gt;Skill
 ├─ 이름
 ├─ 설명
 ├─ 입력
 ├─ 실행 로직&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Claude vs Codex Skill 차이&lt;/b&gt;&lt;/h2&gt;
&lt;table style=&quot;height: 203px;&quot; width=&quot;647&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;&lt;b&gt;항목&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;&lt;b&gt;Claude Skill&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;&lt;b&gt;Codex Skill&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 24px;&quot;&gt;
&lt;td style=&quot;height: 24px;&quot;&gt;정의 방식&lt;/td&gt;
&lt;td style=&quot;height: 24px;&quot;&gt;파일 기반&lt;/td&gt;
&lt;td style=&quot;height: 24px;&quot;&gt;API / 플랫폼 기반&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;실행 방식&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;로컬 스크립트 가능&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;모델 중심 실행&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;확장성&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;매우 높음&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;플랫폼 의존&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;활용&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;개발 자동화&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;코드 생성/보조&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Claude는 &amp;ldquo;자동화 도구&amp;rdquo;에 가깝고&lt;/li&gt;
&lt;li&gt;Codex는 &amp;ldquo;AI 기능 확장&amp;rdquo;에 가깝다&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Skill을 사용하는 대표적인 경우&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Skill은 다음과 같은 작업에 적합하다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;코드 포맷팅&lt;/li&gt;
&lt;li&gt;테스트 실행&lt;/li&gt;
&lt;li&gt;빌드 자동화&lt;/li&gt;
&lt;li&gt;로그 분석&lt;/li&gt;
&lt;li&gt;코드 리뷰&lt;/li&gt;
&lt;li&gt;API 호출 자동화&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Hook과 Skill의 차이&lt;/b&gt;&lt;/h2&gt;
&lt;table style=&quot;height: 162px;&quot; width=&quot;446&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;&lt;b&gt; 구분 &lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;&lt;b&gt; Hook &lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;&lt;b&gt; Skill &lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;실행 방식&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;자동&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;필요 시 실행&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;목적&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;흐름 제어&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;작업 수행&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;예시&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;Bash 차단&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;코드 포맷팅&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Hook은 &amp;ldquo;언제 실행될지&amp;rdquo;&lt;/li&gt;
&lt;li&gt;Skill은 &amp;ldquo;무엇을 할지&amp;rdquo;를 정의한다&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Skill은 단순 프롬프트를 넘어서 AI를 &amp;ldquo;작업 단위로 확장&amp;rdquo;하는 개념이다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;반복 작업 제거&lt;/li&gt;
&lt;li&gt;일관성 확보&lt;/li&gt;
&lt;li&gt;자동화 강화&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 세 가지를 동시에 해결할 수 있다. Claude Code에서는 Hook과 Skill을 함께 사용하면 더 강력해진다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Hook &amp;rarr; 실행 흐름 제어&lt;/li&gt;
&lt;li&gt;Skill &amp;rarr; 작업 자동화&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 Skill은 AI를 단순한 도구가 아니라 &lt;b&gt;자동화된 개발 파이프라인의 일부로 만드는 핵심 요소이다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;관련 포스팅&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://aliencoder.tistory.com/242&quot;&gt;2026.05.05 - [AI/AI Tools] - Claude Code Hook 이해하기: 개념, 구조, 그리고 실전 자동화 예제&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;참고 자료&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://goddaehee.tistory.com/411&quot;&gt;https://goddaehee.tistory.com/411&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://code.claude.com/docs/ko/skills&quot;&gt;https://code.claude.com/docs/ko/skills&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://developers.openai.com/codex/skills&quot;&gt;https://developers.openai.com/codex/skills&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>AI/AI Tools</category>
      <category>Claude</category>
      <category>claude code</category>
      <category>codex</category>
      <category>Hook</category>
      <category>hooks</category>
      <category>skill</category>
      <category>Skills</category>
      <author>AlienCoder</author>
      <guid isPermaLink="true">https://aliencoder.tistory.com/243</guid>
      <comments>https://aliencoder.tistory.com/243#entry243comment</comments>
      <pubDate>Wed, 6 May 2026 08:04:49 +0900</pubDate>
    </item>
    <item>
      <title>Claude Code Hook 이해하기: 개념, 구조, 그리고 실전 자동화 예제</title>
      <link>https://aliencoder.tistory.com/242</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;720&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cilUrD/dJMcajnpuOr/JFdqCSDVAQsUjKkBkDDWz0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cilUrD/dJMcajnpuOr/JFdqCSDVAQsUjKkBkDDWz0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cilUrD/dJMcajnpuOr/JFdqCSDVAQsUjKkBkDDWz0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcilUrD%2FdJMcajnpuOr%2FJFdqCSDVAQsUjKkBkDDWz0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;689&quot; height=&quot;388&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;720&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Claude Code&lt;/b&gt;를 사용하다 보면 단순히 프롬프트를 잘 작성하는 것만으로는 부족한 상황이 생긴다. 예를 들어 코드 수정 후 자동으로 포맷팅을 실행하고 싶거나, 위험한 Bash 명령어를 사전에 차단하고 싶거나, Claude가 도구를 사용하기 전후에 특정 검증 로직을 실행하고 싶을 수 있다. 이때 사용할 수 있는 기능이 &lt;b&gt;Hook(훅)&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Hook은 Claude Code의 실행 흐름 중 특정 시점에 사용자가 정의한 명령을 자동으로 실행하는 기능이다. Git에서 commit 전에 pre-commit hook을 실행하는 것처럼, Claude Code에서도 도구 사용 전후, 프롬프트 제출 시점, 세션 시작과 종료 시점 등에 원하는 로직을 끼워 넣을 수 있다. 이 글에서는 Claude Code에서 사용할 수 있는 Hook의 개념, 동작 구조, 설정 방식, 그리고 실제 구현 예시를 정리한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Hook(훅)&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Hook&lt;/b&gt;은 프로그램의 실행 흐름 중 특정 시점에 끼어들어 실행되는 사용자 정의 로직이다. 이름 그대로 실행 흐름의 특정 지점에 &amp;ldquo;걸어두는 코드&amp;rdquo;라고 볼 수 있다. Claude Code에서 Hook은 Claude가 특정 작업을 수행하기 전이나 후에 자동으로 실행된다. 예를 들어 Claude가 Bash 명령어를 실행하기 전에 해당 명령어가 위험한 명령인지 검사하거나, 파일을 수정한 뒤 자동으로 formatter를 실행할 수 있다. 간단히 표현하면 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;Claude Code 실행 흐름
      &amp;darr;
특정 이벤트 발생
      &amp;darr;
Hook 실행
      &amp;darr;
계속 진행 또는 차단&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 중요한 점은 Hook이 단순한 권장사항이 아니라 실제 실행 흐름에 개입한다는 것이다. CLAUDE.md에 &amp;ldquo;위험한 명령어를 실행하지 말 것&amp;rdquo;이라고 작성하는 것은 지시사항에 가깝다. 반면 Hook은 조건에 맞으면 명령어 실행 자체를 차단할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, Hook은 프롬프트 기반 규칙보다 더 결정적인 자동화 장치이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;왜 Hook이 필요한가&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Claude Code는 강력하지만, 결국 사용자의 개발 환경에서 파일을 수정하고 명령어를 실행한다. 따라서 다음과 같은 상황을 제어할 필요가 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;파일 수정 후 자동 포맷팅을 실행하고 싶을 때&lt;/li&gt;
&lt;li&gt;&lt;code&gt;rm -rf&lt;/code&gt;, &lt;code&gt;git push --force&lt;/code&gt; 같은 위험한 명령어를 막고 싶을 때&lt;/li&gt;
&lt;li&gt;특정 파일이나 디렉터리 수정을 제한하고 싶을 때&lt;/li&gt;
&lt;li&gt;Claude가 도구를 사용하기 전에 사용자 정책을 적용하고 싶을 때&lt;/li&gt;
&lt;li&gt;세션 시작 시 프로젝트 컨텍스트를 자동으로 주입하고 싶을 때&lt;/li&gt;
&lt;li&gt;작업 종료 시 로그를 남기거나 알림을 보내고 싶을 때&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 작업을 매번 프롬프트로 요청하는 것은 번거롭고, 누락될 가능성도 있다. Hook을 사용하면 이러한 규칙을 설정 파일에 등록해 자동으로 적용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Claude Code Hook의 기본 구조&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Claude Code Hook은 일반적으로 설정 파일에 정의한다. 프로젝트 단위로 관리하려면 &lt;code&gt;.claude/settings.json&lt;/code&gt;에 작성하고, 개인 로컬 환경에서만 사용하려면 &lt;code&gt;.claude/settings.local.json&lt;/code&gt;에 작성할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본 구조는 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;{
  &quot;hooks&quot;: {
    &quot;PreToolUse&quot;: [
      {
        &quot;matcher&quot;: &quot;Bash&quot;,
        &quot;hooks&quot;: [
          {
            &quot;type&quot;: &quot;command&quot;,
            &quot;command&quot;: &quot;python3 .claude/hooks/check-command.py&quot;
          }
        ]
      }
    ]
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 설정은 Claude가 Bash 도구를 사용하기 직전에 &lt;code&gt;check-command.py&lt;/code&gt;를 실행한다는 의미이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 요소의 의미는 다음과 같다.&lt;/p&gt;
&lt;table style=&quot;height: 128px;&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;&lt;b&gt;항목&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;&lt;b&gt;의미&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;&lt;code&gt;hooks&lt;/code&gt;&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;Hook 설정의 최상위 항목&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;&lt;code&gt;PreToolUse&lt;/code&gt;&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;Hook이 실행될 이벤트 시점&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;&lt;code&gt;matcher&lt;/code&gt;&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;어떤 도구나 조건에 Hook을 적용할지 지정&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;&lt;code&gt;type&lt;/code&gt;&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;Hook 실행 방식&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;&lt;code&gt;command&lt;/code&gt;&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;실제 실행할 명령어&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, Hook 설정은 &amp;ldquo;언제&amp;rdquo;, &amp;ldquo;무엇에 대해&amp;rdquo;, &amp;ldquo;어떤 명령을 실행할지&amp;rdquo;를 정의하는 구조이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Matcher 동작 방식&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;Matcher&lt;/code&gt; 값은 문자열 형태이며, 포함된 문자에 따라 평가 방식이 달라진다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 63px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;&lt;b&gt;Matcher&amp;nbsp; 값&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;&lt;b&gt;&lt;span style=&quot;background-color: #9b9b9b; text-align: start;&quot;&gt;평가 방식&lt;/span&gt; &lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;&lt;b&gt; &lt;span style=&quot;background-color: #9b9b9b; text-align: start;&quot;&gt;예제&lt;/span&gt; &lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;&quot;*&quot;, &quot;&quot;, 생략&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;모든 경우 매칭&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;모든 이벤트에서 실행&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;문자/숫자/_/&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;만 포함&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;정확한 문자열 또는 OR 매칭&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;기타 문자 포함&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;정규식 (JavaScript regex)&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;^Notebook, mcp__.*&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고로 이벤트별 matcher 적용 대상이 다르다. 이벤트는 Hook에서 좀 더 자세히 설명해두었다. 이벤트별 matcher 내용은 다음 표와 같다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 442px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 16px;&quot;&gt;
&lt;td style=&quot;height: 16px; text-align: center; width: 39.8837%;&quot;&gt;&lt;b&gt;이벤트&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 16px; text-align: center; width: 31.8604%;&quot;&gt;&lt;b&gt;Matcher&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 16px; text-align: center; width: 28.1396%;&quot;&gt;&lt;b&gt;대상 예시&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 33px;&quot;&gt;
&lt;td style=&quot;height: 33px; width: 39.8837%;&quot;&gt;PreToolUse, PostToolUse, PostToolUseFailure, PermissionRequest, PermissionDenied&lt;/td&gt;
&lt;td style=&quot;height: 33px; width: 31.8604%;&quot;&gt;도구 이름&lt;/td&gt;
&lt;td style=&quot;height: 33px; width: 28.1396%;&quot;&gt;Bash, `Edit&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 43px;&quot;&gt;
&lt;td style=&quot;height: 43px; width: 39.8837%;&quot;&gt;SessionStart&lt;/td&gt;
&lt;td style=&quot;height: 43px; width: 31.8604%;&quot;&gt;세션 시작 방식&lt;/td&gt;
&lt;td style=&quot;height: 43px; width: 28.1396%;&quot;&gt;startup, resume&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px; width: 39.8837%;&quot;&gt;Setup&lt;/td&gt;
&lt;td style=&quot;height: 21px; width: 31.8604%;&quot;&gt;CLI 플래그&lt;/td&gt;
&lt;td style=&quot;height: 21px; width: 28.1396%;&quot;&gt;init, maintenance&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px; width: 39.8837%;&quot;&gt;SessionEnd&lt;/td&gt;
&lt;td style=&quot;height: 21px; width: 31.8604%;&quot;&gt;종료 이유&lt;/td&gt;
&lt;td style=&quot;height: 21px; width: 28.1396%;&quot;&gt;logout, clear&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 33px;&quot;&gt;
&lt;td style=&quot;height: 33px; width: 39.8837%;&quot;&gt;Notification&lt;/td&gt;
&lt;td style=&quot;height: 33px; width: 31.8604%;&quot;&gt;알림 타입&lt;/td&gt;
&lt;td style=&quot;height: 33px; width: 28.1396%;&quot;&gt;permission_prompt, idle_prompt&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px; width: 39.8837%;&quot;&gt;SubagentStart / Stop&lt;/td&gt;
&lt;td style=&quot;height: 21px; width: 31.8604%;&quot;&gt;에이전트 유형&lt;/td&gt;
&lt;td style=&quot;height: 21px; width: 28.1396%;&quot;&gt;Plan, Explore&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px; width: 39.8837%;&quot;&gt;PreCompact / PostCompact&lt;/td&gt;
&lt;td style=&quot;height: 21px; width: 31.8604%;&quot;&gt;압축 트리거&lt;/td&gt;
&lt;td style=&quot;height: 21px; width: 28.1396%;&quot;&gt;manual, auto&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 43px;&quot;&gt;
&lt;td style=&quot;height: 43px; width: 39.8837%;&quot;&gt;ConfigChange&lt;/td&gt;
&lt;td style=&quot;height: 43px; width: 31.8604%;&quot;&gt;설정 변경 소스&lt;/td&gt;
&lt;td style=&quot;height: 43px; width: 28.1396%;&quot;&gt;user_settings, project_settings&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px; width: 39.8837%;&quot;&gt;StopFailure&lt;/td&gt;
&lt;td style=&quot;height: 21px; width: 31.8604%;&quot;&gt;오류 유형&lt;/td&gt;
&lt;td style=&quot;height: 21px; width: 28.1396%;&quot;&gt;rate_limit, server_error&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px; width: 39.8837%;&quot;&gt;InstructionsLoaded&lt;/td&gt;
&lt;td style=&quot;height: 21px; width: 31.8604%;&quot;&gt;로드 이유&lt;/td&gt;
&lt;td style=&quot;height: 21px; width: 28.1396%;&quot;&gt;session_start, include&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px; width: 39.8837%;&quot;&gt;UserPromptExpansion&lt;/td&gt;
&lt;td style=&quot;height: 21px; width: 31.8604%;&quot;&gt;명령 이름&lt;/td&gt;
&lt;td style=&quot;height: 21px; width: 28.1396%;&quot;&gt;skill 이름&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 43px;&quot;&gt;
&lt;td style=&quot;height: 43px; width: 39.8837%;&quot;&gt;Elicitation / Result&lt;/td&gt;
&lt;td style=&quot;height: 43px; width: 31.8604%;&quot;&gt;MCP 서버 이름&lt;/td&gt;
&lt;td style=&quot;height: 43px; width: 28.1396%;&quot;&gt;서버 이름&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px; width: 39.8837%;&quot;&gt;FileChanged&lt;/td&gt;
&lt;td style=&quot;height: 21px; width: 31.8604%;&quot;&gt;파일명&lt;/td&gt;
&lt;td style=&quot;height: 21px; width: 28.1396%;&quot;&gt;.env, .envrc&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px; width: 39.8837%;&quot;&gt;CwdChanged&lt;/td&gt;
&lt;td style=&quot;height: 21px; width: 31.8604%;&quot;&gt;matcher 없음&lt;/td&gt;
&lt;td style=&quot;height: 21px; width: 28.1396%;&quot;&gt;항상 실행&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px; width: 39.8837%;&quot;&gt;UserPromptSubmit, Stop 등 일부&lt;/td&gt;
&lt;td style=&quot;height: 21px; width: 31.8604%;&quot;&gt;matcher 없음&lt;/td&gt;
&lt;td style=&quot;height: 21px; width: 28.1396%;&quot;&gt;항상 실행&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Hook Lifecycle과 이벤트 종류&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Claude Code Hook은 이벤트 기반으로 동작한다. 이벤트는 Claude Code의 실행 흐름 중 Hook이 개입할 수 있는 시점을 의미한다.&amp;nbsp;다음 그림은 Hook의 이벤트 타이밍을 도식화한 내용이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;520&quot; data-origin-height=&quot;1228&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/boEWjw/dJMcai4dKPa/vWgvp6NVtGhpUAxQ2kAENk/tfile.svg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/boEWjw/dJMcai4dKPa/vWgvp6NVtGhpUAxQ2kAENk/tfile.svg&quot; data-alt=&quot;Fig 1. Hook Lifecycle&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/boEWjw/dJMcai4dKPa/vWgvp6NVtGhpUAxQ2kAENk/tfile.svg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FboEWjw%2FdJMcai4dKPa%2FvWgvp6NVtGhpUAxQ2kAENk%2Ftfile.svg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;397&quot; height=&quot;938&quot; data-origin-width=&quot;520&quot; data-origin-height=&quot;1228&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Fig 1. Hook Lifecycle&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전체 이벤트는 다음과 같다. 자주쓰는 이벤트도 함께 밑줄로 표기해두었다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;&lt;b&gt; 이벤트 &lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;&lt;b&gt;실행 시점&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;&lt;b&gt;대표 용도&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;u&gt;&lt;b&gt;SessionStart&lt;/b&gt;&lt;/u&gt;&lt;/td&gt;
&lt;td&gt;&lt;u&gt;&lt;b&gt;세션이 시작되거나 재개될 때&lt;/b&gt;&lt;/u&gt;&lt;/td&gt;
&lt;td&gt;&lt;u&gt;&lt;b&gt;프로젝트 규칙 주입, 초기 컨텍스트 설정&lt;/b&gt;&lt;/u&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Setup&lt;/td&gt;
&lt;td&gt;--init, --maintenance 실행 시&lt;/td&gt;
&lt;td&gt;CI 초기화, 환경 세팅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;u&gt;&lt;b&gt;UserPromptSubmit&lt;/b&gt;&lt;/u&gt;&lt;/td&gt;
&lt;td&gt;&lt;u&gt;&lt;b&gt;사용자가 프롬프트 제출 시&lt;/b&gt;&lt;/u&gt;&lt;/td&gt;
&lt;td&gt;&lt;u&gt;&lt;b&gt;입력 검증, 컨텍스트 자동 추가&lt;/b&gt;&lt;/u&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;UserPromptExpansion&lt;/td&gt;
&lt;td&gt;명령 확장 전&lt;/td&gt;
&lt;td&gt;커맨드 확장 차단, 검증&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;u&gt;&lt;b&gt;PreToolUse&lt;/b&gt;&lt;/u&gt;&lt;/td&gt;
&lt;td&gt;&lt;u&gt;&lt;b&gt;도구 실행 직전&lt;/b&gt;&lt;/u&gt;&lt;/td&gt;
&lt;td&gt;&lt;u&gt;&lt;b&gt;위험 명령어 차단, 권한 검사&lt;/b&gt;&lt;/u&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PermissionRequest&lt;/td&gt;
&lt;td&gt;권한 요청 시&lt;/td&gt;
&lt;td&gt;사용자 승인 로직 커스터마이징&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PermissionDenied&lt;/td&gt;
&lt;td&gt;실행 차단 시&lt;/td&gt;
&lt;td&gt;재시도 유도, 정책 대응&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;u&gt;&lt;b&gt;PostToolUse&lt;/b&gt;&lt;/u&gt;&lt;/td&gt;
&lt;td&gt;&lt;u&gt;&lt;b&gt;도구 실행 성공 후&lt;/b&gt;&lt;/u&gt;&lt;/td&gt;
&lt;td&gt;&lt;u&gt;&lt;b&gt;포맷팅, 로그 기록&lt;/b&gt;&lt;/u&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PostToolUseFailure&lt;/td&gt;
&lt;td&gt;도구 실행 실패 후&lt;/td&gt;
&lt;td&gt;오류 로그, 자동 복구 시도&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PostToolBatch&lt;/td&gt;
&lt;td&gt;병렬 작업 완료 후&lt;/td&gt;
&lt;td&gt;전체 결과 검증&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;u&gt;&lt;b&gt;Notification&lt;/b&gt;&lt;/u&gt;&lt;/td&gt;
&lt;td&gt;&lt;u&gt;&lt;b&gt;알림 발생 시&lt;/b&gt;&lt;/u&gt;&lt;/td&gt;
&lt;td&gt;&lt;u&gt;&lt;b&gt;Slack, Discord 알림&lt;/b&gt;&lt;/u&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SubagentStart&lt;/td&gt;
&lt;td&gt;서브 에이전트 시작&lt;/td&gt;
&lt;td&gt;작업 추적, 로그 시작&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;u&gt;&lt;b&gt;SubagentStop&lt;/b&gt;&lt;/u&gt;&lt;/td&gt;
&lt;td&gt;&lt;u&gt;&lt;b&gt;서브 에이전트 종료&lt;/b&gt;&lt;/u&gt;&lt;/td&gt;
&lt;td&gt;&lt;u&gt;&lt;b&gt;결과 검증&lt;/b&gt;&lt;/u&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TaskCreated&lt;/td&gt;
&lt;td&gt;작업 생성 시&lt;/td&gt;
&lt;td&gt;작업 기록, 초기화&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TaskCompleted&lt;/td&gt;
&lt;td&gt;작업 완료 시&lt;/td&gt;
&lt;td&gt;완료 로그, 후처리&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;u&gt;&lt;b&gt;Stop&lt;/b&gt;&lt;/u&gt;&lt;/td&gt;
&lt;td&gt;&lt;u&gt;&lt;b&gt;응답 종료 시&lt;/b&gt;&lt;/u&gt;&lt;/td&gt;
&lt;td&gt;&lt;u&gt;&lt;b&gt;테스트 실행, 후처리&lt;/b&gt;&lt;/u&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;StopFailure&lt;/td&gt;
&lt;td&gt;API 오류 종료 시&lt;/td&gt;
&lt;td&gt;오류 대응, 알림&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TeammateIdle&lt;/td&gt;
&lt;td&gt;에이전트 idle 직전&lt;/td&gt;
&lt;td&gt;상태 기록&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;InstructionsLoaded&lt;/td&gt;
&lt;td&gt;CLAUDE.md 로드 시&lt;/td&gt;
&lt;td&gt;규칙 검증&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ConfigChange&lt;/td&gt;
&lt;td&gt;설정 변경 시&lt;/td&gt;
&lt;td&gt;동적 설정 반영&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CwdChanged&lt;/td&gt;
&lt;td&gt;디렉터리 변경 시&lt;/td&gt;
&lt;td&gt;환경 재설정&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FileChanged&lt;/td&gt;
&lt;td&gt;파일 변경 감지 시&lt;/td&gt;
&lt;td&gt;자동 빌드, reload&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;WorktreeCreate&lt;/td&gt;
&lt;td&gt;worktree 생성 시&lt;/td&gt;
&lt;td&gt;Git 환경 설정&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;WorktreeRemove&lt;/td&gt;
&lt;td&gt;worktree 제거 시&lt;/td&gt;
&lt;td&gt;정리 작업&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;u&gt;&lt;b&gt;PreCompact&lt;/b&gt;&lt;/u&gt;&lt;/td&gt;
&lt;td&gt;&lt;u&gt;&lt;b&gt;컨텍스트 압축 전&lt;/b&gt;&lt;/u&gt;&lt;/td&gt;
&lt;td&gt;&lt;u&gt;&lt;b&gt;중요 데이터 유지&lt;/b&gt;&lt;/u&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PostCompact&lt;/td&gt;
&lt;td&gt;컨텍스트 압축 후&lt;/td&gt;
&lt;td&gt;상태 재구성&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Elicitation&lt;/td&gt;
&lt;td&gt;사용자 입력 요청 시&lt;/td&gt;
&lt;td&gt;인터랙션 처리&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ElicitationResult&lt;/td&gt;
&lt;td&gt;사용자 응답 후&lt;/td&gt;
&lt;td&gt;입력 검증&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SessionEnd&lt;/td&gt;
&lt;td&gt;세션 종료 시&lt;/td&gt;
&lt;td&gt;로그 저장, 정리&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 많이 사용되는 이벤트는 &lt;code&gt;PreToolUse&lt;/code&gt;와 &lt;code&gt;PostToolUse&lt;/code&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;PreToolUse&lt;/code&gt;는 실행 전에 개입하므로 차단 목적에 적합하다. 반면 &lt;code&gt;PostToolUse&lt;/code&gt;는 실행 후에 개입하므로 포맷팅, 테스트, 로그 기록 같은 후처리에 적합하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;PreToolUse 예시: 위험한 Bash 명령어 차단&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 대표적인 Hook 예시는 위험한 Bash 명령어를 차단하는 것이다. 예를 들어 Claude가 다음 명령어를 실행하려고 한다고 가정한다.&lt;/p&gt;
&lt;pre class=&quot;nginx&quot;&gt;&lt;code&gt;rm -rf /&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 명령어는 실수로 실행되면 큰 문제가 발생할 수 있다. 따라서 Bash 실행 전에 Hook을 걸어 위험한 패턴을 검사하는 것이 좋다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 &lt;code&gt;.claude/settings.json&lt;/code&gt;에 다음과 같이 Hook을 등록한다.&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;{
  &quot;hooks&quot;: {
    &quot;PreToolUse&quot;: [
      {
        &quot;matcher&quot;: &quot;Bash&quot;,
        &quot;hooks&quot;: [
          {
            &quot;type&quot;: &quot;command&quot;,
            &quot;command&quot;: &quot;python3 .claude/hooks/block-dangerous-command.py&quot;
          }
        ]
      }
    ]
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 &lt;code&gt;.claude/hooks/block-dangerous-command.py&lt;/code&gt; 파일을 작성한다.&lt;/p&gt;
&lt;pre class=&quot;awk&quot;&gt;&lt;code&gt;#!/usr/bin/env python3
import json
import re
import sys

payload = json.load(sys.stdin)
command = payload.get(&quot;tool_input&quot;, {}).get(&quot;command&quot;, &quot;&quot;)

dangerous_patterns = [
    r&quot;rm\s+-rf\s+/&quot;,
    r&quot;git\s+push\s+--force&quot;,
    r&quot;DROP\s+TABLE&quot;,
    r&quot;dd\s+if=&quot;,
]

for pattern in dangerous_patterns:
    if re.search(pattern, command, re.IGNORECASE):
        print(f&quot;위험한 명령어가 차단됨: {command}&quot;, file=sys.stderr)
        sys.exit(2)

sys.exit(0)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 핵심은 종료 코드이다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;0&lt;/code&gt;: 정상 통과&lt;/li&gt;
&lt;li&gt;&lt;code&gt;2&lt;/code&gt;: 실행 차단&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, Hook 스크립트가 &lt;code&gt;sys.exit(2)&lt;/code&gt;를 반환하면 Claude Code는 해당 도구 실행을 차단한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 구조를 통해 개발자는 단순히 &amp;ldquo;위험한 명령어를 쓰지 말라&amp;rdquo;고 요청하는 수준을 넘어, 실제 실행 자체를 막을 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;PostToolUse 예시: 파일 수정 후 자동 포맷팅&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;PostToolUse&lt;/code&gt;는 도구 사용 이후에 실행된다. 따라서 파일 수정 후 formatter를 실행하는 용도로 적합하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 Claude가 파일을 수정한 뒤 Python 파일에 대해 &lt;code&gt;black&lt;/code&gt;을 자동 실행하고 싶다면 다음과 같이 설정할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;{
  &quot;hooks&quot;: {
    &quot;PostToolUse&quot;: [
      {
        &quot;matcher&quot;: &quot;Edit|Write|MultiEdit&quot;,
        &quot;hooks&quot;: [
          {
            &quot;type&quot;: &quot;command&quot;,
            &quot;command&quot;: &quot;python3 .claude/hooks/format-python.py&quot;
          }
        ]
      }
    ]
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스크립트는 다음과 같이 작성할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;awk&quot;&gt;&lt;code&gt;#!/usr/bin/env python3
import json
import subprocess
import sys
from pathlib import Path

payload = json.load(sys.stdin)
file_path = payload.get(&quot;tool_input&quot;, {}).get(&quot;file_path&quot;)

if not file_path:
    sys.exit(0)

path = Path(file_path)

if path.suffix == &quot;.py&quot;:
    subprocess.run([&quot;black&quot;, str(path)], check=False)

sys.exit(0)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 하면 Claude가 Python 파일을 수정한 뒤 자동으로 &lt;code&gt;black&lt;/code&gt;이 실행된다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;Claude가 파일 수정
      &amp;darr;
PostToolUse Hook 실행
      &amp;darr;
black formatter 실행
      &amp;darr;
정리된 코드 유지&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 방식은 코드 스타일을 유지하는 데 유용하다. 특히 팀 프로젝트에서 공통 formatter를 강제하고 싶을 때 효과적이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;UserPromptSubmit 예시: 프롬프트 제출 시 컨텍스트 추가&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;UserPromptSubmit&lt;/code&gt;은 사용자가 프롬프트를 제출한 직후 실행된다. 이 이벤트는 Claude가 본격적으로 응답하기 전에 컨텍스트를 추가하거나, 입력 내용을 검사할 때 사용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 매번 현재 Git 브랜치와 변경 파일 목록을 Claude에게 알려주고 싶다면 다음과 같이 구성할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;{
  &quot;hooks&quot;: {
    &quot;UserPromptSubmit&quot;: [
      {
        &quot;hooks&quot;: [
          {
            &quot;type&quot;: &quot;command&quot;,
            &quot;command&quot;: &quot;bash .claude/hooks/inject-git-context.sh&quot;
          }
        ]
      }
    ]
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code style=&quot;letter-spacing: 0px;&quot;&gt;.claude/hooks/inject-git-context.sh&lt;/code&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;는 다음과 같이 작성할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;bash&quot;&gt;&lt;code&gt;#!/usr/bin/env bash

echo &quot;현재 Git 컨텍스트:&quot;
echo &quot;브랜치: $(git branch --show-current)&quot;
echo &quot;변경 파일:&quot;
git status --short&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 Hook을 사용하면 매번 사용자가 직접 &amp;ldquo;현재 브랜치 확인해줘&amp;rdquo;, &amp;ldquo;변경 파일 보고 작업해줘&amp;rdquo;라고 말하지 않아도 된다. Claude는 프롬프트 처리 전에 현재 프로젝트 상태를 함께 참고할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;SessionStart 예시: 프로젝트 규칙 자동 주입&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;SessionStart&lt;/code&gt;는 Claude Code 세션이 시작될 때 실행된다. 프로젝트마다 반드시 지켜야 하는 규칙이나 작업 방식을 자동으로 주입하는 데 적합하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 다음과 같은 규칙을 항상 Claude에게 전달하고 싶다고 가정한다.&lt;/p&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;- 테스트 없이 리팩토링하지 않는다
- main 브랜치에서는 직접 수정하지 않는다
- Controller에서 Repository를 직접 호출하지 않는다&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설정은 다음과 같이 작성할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;{
  &quot;hooks&quot;: {
    &quot;SessionStart&quot;: [
      {
        &quot;hooks&quot;: [
          {
            &quot;type&quot;: &quot;command&quot;,
            &quot;command&quot;: &quot;cat .claude/project-rules.md&quot;
          }
        ]
      }
    ]
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code style=&quot;letter-spacing: 0px;&quot;&gt;.claude/project-rules.md&lt;/code&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;에는 프로젝트 규칙을 작성한다.&lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;markdown&quot;&gt;&lt;code&gt;# Project Rules

- 테스트 없이 리팩토링하지 않는다.
- main 브랜치에서는 직접 수정하지 않는다.
- Controller에서 Repository를 직접 호출하지 않는다.
- 변경 전후로 관련 테스트를 확인한다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 하면 세션 시작 시 프로젝트 규칙이 자동으로 Claude에게 전달된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Hook의 입력과 출력&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Claude Code는 Hook 스크립트에 JSON 형태의 입력을 전달한다. Hook 스크립트는 이 JSON을 읽고, 조건에 따라 통과하거나 차단할 수 있다. 구조를 단순화하면 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Claude Code
   &amp;darr; JSON 입력(stdin)
Hook Script
   &amp;darr; 종료 코드 / 출력
Claude Code&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 &lt;code&gt;PreToolUse&lt;/code&gt;에서는 어떤 도구를 사용하려는지, 해당 도구에 어떤 입력이 전달되는지 확인할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;import json
import sys

payload = json.load(sys.stdin)
print(payload)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Hook을 처음 작성할 때는 위처럼 입력 payload를 출력해 구조를 확인하는 것이 좋다. 이벤트마다 전달되는 JSON 구조가 다를 수 있기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Hook 타입&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Claude Code Hook은 여러 실행 방식을 지원한다. 가장 기본적인 방식은 &lt;code&gt;command&lt;/code&gt; 타입이다.&lt;/p&gt;
&lt;table style=&quot;height: 128px;&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px; text-align: center;&quot;&gt;&lt;b&gt;타입&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 21px; text-align: center;&quot;&gt;&lt;b&gt;설명&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 21px; text-align: center;&quot;&gt;&lt;b&gt;사용 예&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;&lt;code&gt;command&lt;/code&gt;&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;로컬 셸 명령 실행&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;Python/Bash 스크립트 실행&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;&lt;code&gt;http&lt;/code&gt;&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;HTTP 엔드포인트 호출&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;사내 API, Slack Webhook 호출&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;&lt;code&gt;prompt&lt;/code&gt;&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;LLM 프롬프트 기반 평가&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;정책 검사, 응답 검증&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;&lt;code&gt;agent&lt;/code&gt;&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;별도 agent 실행&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;복잡한 검토나 분석 작업&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적인 자동화는 대부분 &lt;code&gt;command&lt;/code&gt; 타입으로 충분하다. 로컬 파일 검사, 명령어 차단, formatter 실행, 로그 기록은 모두 &lt;code&gt;command&lt;/code&gt; 타입으로 구현할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;외부 시스템과 연동해야 한다면 &lt;code&gt;http&lt;/code&gt; 타입을 사용할 수 있다. 예를 들어 Claude가 작업을 끝냈을 때 Slack으로 알림을 보내는 식이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Hook과 CLAUDE.md의 차이&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Claude Code를 사용할 때 프로젝트 규칙을 작성하는 대표적인 방법은 &lt;code&gt;CLAUDE.md&lt;/code&gt;이다. 하지만 &lt;code&gt;CLAUDE.md&lt;/code&gt;와 Hook은 역할이 다르다.&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;&lt;b&gt;구분&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;&lt;b&gt;CLAUDE.md&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;&lt;b&gt;Hook&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;목적&lt;/td&gt;
&lt;td&gt;Claude에게 지침 제공&lt;/td&gt;
&lt;td&gt;실행 흐름에 직접 개입&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;성격&lt;/td&gt;
&lt;td&gt;설명, 규칙, 컨텍스트&lt;/td&gt;
&lt;td&gt;자동화, 검증, 차단&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;강제력&lt;/td&gt;
&lt;td&gt;상대적으로 약함&lt;/td&gt;
&lt;td&gt;조건에 따라 실행 차단 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;예시&lt;/td&gt;
&lt;td&gt;&amp;ldquo;테스트를 먼저 확인하라&amp;rdquo;&lt;/td&gt;
&lt;td&gt;테스트 미통과 시 작업 차단&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, &lt;code&gt;CLAUDE.md&lt;/code&gt;는 Claude에게 알려주는 문서이고, Hook은 실제로 실행되는 코드이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 중요한 정책은 Hook으로 처리하는 것이 더 안전하다. 예를 들어 &amp;ldquo;운영 설정 파일을 수정하지 말라&amp;rdquo;는 지침은 &lt;code&gt;CLAUDE.md&lt;/code&gt;에 적는 것보다 &lt;code&gt;PreToolUse&lt;/code&gt; Hook으로 차단하는 것이 더 확실하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;실전 예시: 운영 설정 파일 수정 차단&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;운영 설정 파일은 실수로 수정되면 큰 장애로 이어질 수 있다. 따라서 Claude가 특정 파일을 수정하려고 할 때 차단하는 Hook을 만들 수 있다. 설정은 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;{
  &quot;hooks&quot;: {
    &quot;PreToolUse&quot;: [
      {
        &quot;matcher&quot;: &quot;Edit|Write|MultiEdit&quot;,
        &quot;hooks&quot;: [
          {
            &quot;type&quot;: &quot;command&quot;,
            &quot;command&quot;: &quot;python3 .claude/hooks/protect-prod-files.py&quot;
          }
        ]
      }
    ]
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스크립트는 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;awk&quot;&gt;&lt;code&gt;#!/usr/bin/env python3
import json
import sys
from pathlib import Path

payload = json.load(sys.stdin)
file_path = payload.get(&quot;tool_input&quot;, {}).get(&quot;file_path&quot;, &quot;&quot;)

protected_files = [
    &quot;application-prod.yml&quot;,
    &quot;application-prod.properties&quot;,
    &quot;.env.production&quot;,
]

path = Path(file_path)

if path.name in protected_files:
    print(f&quot;보호된 운영 설정 파일 수정 차단: {file_path}&quot;, file=sys.stderr)
    sys.exit(2)

sys.exit(0)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 Claude가 &lt;code&gt;application-prod.yml&lt;/code&gt; 같은 파일을 수정하려고 하면 Hook이 먼저 실행되고, 조건에 맞으면 작업이 차단된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;실전 예시: 작업 종료 후 테스트 실행&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작업이 끝난 뒤 자동으로 테스트를 실행하고 싶다면 &lt;code&gt;Stop&lt;/code&gt; 이벤트를 사용할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;{
  &quot;hooks&quot;: {
    &quot;Stop&quot;: [
      {
        &quot;hooks&quot;: [
          {
            &quot;type&quot;: &quot;command&quot;,
            &quot;command&quot;: &quot;npm test&quot;
          }
        ]
      }
    ]
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또는 Java 프로젝트라면 다음과 같이 사용할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;{
  &quot;hooks&quot;: {
    &quot;Stop&quot;: [
      {
        &quot;hooks&quot;: [
          {
            &quot;type&quot;: &quot;command&quot;,
            &quot;command&quot;: &quot;./gradlew test&quot;
          }
        ]
      }
    ]
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 테스트 시간이 긴 프로젝트에서는 모든 응답 종료 시마다 테스트를 실행하는 것이 부담이 될 수 있다. 이 경우 특정 파일 수정 후에만 실행하거나, 별도 스크립트에서 조건을 분기하는 방식이 좋다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Hook을 사용할 때 주의할 점&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Hook은 강력하지만 잘못 작성하면 오히려 개발 흐름을 방해할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;첫째&lt;/b&gt;, 너무 많은 Hook을 한 번에 적용하지 않는 것이 좋다. 처음에는 위험 명령어 차단, 파일 보호, 자동 포맷팅처럼 효과가 명확한 것부터 적용하는 것이 좋다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;둘째&lt;/b&gt;, Hook 스크립트는 빠르게 실행되어야 한다. 모든 도구 사용 전후에 실행되는 Hook이 느리면 Claude Code 사용 경험이 크게 나빠진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;셋째&lt;/b&gt;, 차단 조건은 명확해야 한다. 너무 넓은 패턴으로 차단하면 정상적인 명령까지 막을 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;넷째&lt;/b&gt;, 팀에서 공유할 설정과 개인 설정을 구분해야 한다. 팀 공통 규칙은 &lt;code&gt;.claude/settings.json&lt;/code&gt;에 두고, 개인 알림이나 로컬 환경 의존 설정은 &lt;code&gt;.claude/settings.local.json&lt;/code&gt;에 두는 것이 좋다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Hook을 적용하기 좋은 경우&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Hook은 다음과 같은 작업에 특히 잘 맞는다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Bash 명령어 보안 검사&lt;/li&gt;
&lt;li&gt;운영 파일 수정 방지&lt;/li&gt;
&lt;li&gt;코드 포맷팅 자동 실행&lt;/li&gt;
&lt;li&gt;테스트 자동 실행&lt;/li&gt;
&lt;li&gt;세션 시작 시 프로젝트 규칙 주입&lt;/li&gt;
&lt;li&gt;작업 종료 후 로그 기록&lt;/li&gt;
&lt;li&gt;Slack, Discord 등 외부 알림 전송&lt;/li&gt;
&lt;li&gt;Git 상태 자동 첨부&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반대로 복잡한 판단이 필요하거나 매번 상황에 따라 달라지는 작업은 Hook보다 프롬프트나 별도 agent에 맡기는 것이 나을 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Claude Code Hook은 Claude의 실행 흐름에 사용자가 직접 개입할 수 있게 해주는 자동화 기능이다. 단순히 &amp;ldquo;이렇게 해줘&amp;rdquo;라고 지시하는 것이 아니라, 특정 시점에 실제 스크립트를 실행하고 필요하면 작업을 차단할 수 있다는 점에서 강력하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 &lt;code&gt;PreToolUse&lt;/code&gt;는 위험한 명령어 차단이나 파일 보호에 적합하고, &lt;code&gt;PostToolUse&lt;/code&gt;는 포맷팅이나 후처리에 적합하다. &lt;code&gt;UserPromptSubmit&lt;/code&gt;과 &lt;code&gt;SessionStart&lt;/code&gt;는 프로젝트 컨텍스트를 자동으로 주입할 때 유용하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정리하면 Hook은 Claude Code를 더 안전하고 일관된 개발 도구로 만들기 위한 장치이다. 프롬프트는 Claude에게 방향을 알려주고, Hook은 반드시 지켜야 하는 규칙을 코드로 강제한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 Claude Code를 실무 프로젝트에서 사용한다면 Hook은 단순 편의 기능이 아니라 개발 워크플로우를 제어하는 핵심 기능이라고 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;관련 포스팅&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://aliencoder.tistory.com/243&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;2026.05.05 - [AI/AI Tools] - Claude Code Skills vs Codex Skills: 구조와 차이 완전 정리&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;참고 자료&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://wikidocs.net/333424&quot;&gt;https://wikidocs.net/333424&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://javaexpert.tistory.com/1528&quot;&gt;https://javaexpert.tistory.com/1528&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://code.claude.com/docs/ko/hooks-guide&quot;&gt;https://code.claude.com/docs/ko/hooks-guide&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://code.claude.com/docs/ko/hooks&quot;&gt;https://code.claude.com/docs/ko/hooks&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://news.hada.io/topic?id=21772&quot;&gt;https://news.hada.io/topic?id=21772&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>AI/AI Tools</category>
      <category>Claude</category>
      <category>claude code</category>
      <category>Hook</category>
      <category>hooks</category>
      <author>AlienCoder</author>
      <guid isPermaLink="true">https://aliencoder.tistory.com/242</guid>
      <comments>https://aliencoder.tistory.com/242#entry242comment</comments>
      <pubDate>Tue, 5 May 2026 18:28:12 +0900</pubDate>
    </item>
    <item>
      <title>[Spring boot] 레거시(MyBatis) 구조: SVO/DVO/DAO 흐름과 JPA 비교</title>
      <link>https://aliencoder.tistory.com/240</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;640&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/beziAU/btsLJCUjM9q/1j7sNwamcIoIsAjwJrD1Vk/tfile.dat&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/beziAU/btsLJCUjM9q/1j7sNwamcIoIsAjwJrD1Vk/tfile.dat&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/beziAU/btsLJCUjM9q/1j7sNwamcIoIsAjwJrD1Vk/tfile.dat&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbeziAU%2FbtsLJCUjM9q%2F1j7sNwamcIoIsAjwJrD1Vk%2Ftfile.dat&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;732&quot; height=&quot;366&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;640&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring Boot 기반으로 개발을 진행하다 보면, 이전에 사용하던 구조와 전혀 다른 형태의 레거시 프로젝트를 마주하는 경우가 있다. 이번 프로젝트에서는 MyBatis를 사용하며 jQuery, XML Mapper, DAO, DVO, SVO, Servlet을 명시적으로 사용하는 구조로 구성되어 있었고, 내가 기존에 사용하던 Model, Entity, DTO, Repository 중심의 JPA 구조와는 차이가 있었다. 이 레거시 구조는 SQL 중심의 명시적인 설계이고, JPA 기반 구조는 객체 중심의 추상화된 설계라고 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 글에서는 두 구조를 비교하면서 레거시를 이해하고, 동시에 Spring의 기본 구조를 다시 정리하려 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;MyBatis와 JPA 구조의 본질적인 차이&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 전체 구조를 단순하게 보면 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt; MyBatis 구조                    Hibernate/JPA 구조
───────────────                 ────────────────────
   Service                          Service
     &amp;darr;                                 &amp;darr;
    DAO                           Repository
     &amp;darr;                                 &amp;darr;
 Mapper XML                        Hibernate
 :sqlSession.select(...)           :JPA가 구현체 자동 생성
     &amp;darr;                                 &amp;darr;
 SQL 직접 작성                     SQL 자동 생성
 : Mapper XML&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 차이는 단순해 보이지만 굉장히 중요하다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;MyBatis&lt;/b&gt;: 개발자가 SQL을 직접 작성하고 제어하는 구조이다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;JPA(Java Persistence API)&lt;/b&gt;: Entity 상태를 기반으로 SQL을 자동 생성한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, MyBatis는 &amp;ldquo;SQL 중심&amp;rdquo;, JPA는 &amp;ldquo;객체 중심&amp;rdquo;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 구조를 대응 관계로 보면 다음과 같다.&lt;/p&gt;
&lt;table style=&quot;height: 249px;&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center; width: 163px;&quot;&gt;&lt;b&gt; Legacy(MyBatis) &lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 196px;&quot;&gt;&lt;b&gt; Hibernate/JPA &lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 179px; text-align: center;&quot;&gt;&lt;b&gt;같은 점&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 313px; text-align: center;&quot;&gt;&lt;b&gt;다른 점&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 43px;&quot;&gt;
&lt;td style=&quot;text-align: center; height: 43px; width: 163px;&quot;&gt;DVO&lt;/td&gt;
&lt;td style=&quot;text-align: center; height: 43px; width: 196px;&quot;&gt;@Entity&lt;/td&gt;
&lt;td style=&quot;height: 43px; width: 179px;&quot;&gt;DB 테이블 필드 1:1 매핑&lt;/td&gt;
&lt;td style=&quot;height: 43px; width: 313px;&quot;&gt;DVO는 순수 POJO(@Data 등), Entity는 @Entity, @Table, @Column 등 매핑 어노테이션 사용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 43px;&quot;&gt;
&lt;td style=&quot;text-align: center; height: 43px; width: 163px;&quot;&gt;SVO&lt;/td&gt;
&lt;td style=&quot;text-align: center; height: 43px; width: 196px;&quot;&gt;DTO (Request/Response)&lt;/td&gt;
&lt;td style=&quot;height: 43px; width: 179px;&quot;&gt;화면 &amp;harr; Service 간 데이터 전달&lt;/td&gt;
&lt;td style=&quot;height: 43px; width: 313px;&quot;&gt;SVO는 BaseAbstractGridVO 상속으로 페이징 포함, DTO는 보통 독립 객체&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 43px;&quot;&gt;
&lt;td style=&quot;text-align: center; height: 43px; width: 163px;&quot;&gt;DAO + Mapper XML&lt;/td&gt;
&lt;td style=&quot;text-align: center; height: 43px; width: 196px;&quot;&gt;Repository&lt;/td&gt;
&lt;td style=&quot;height: 43px; width: 179px;&quot;&gt;데이터 접근 계층 분리&lt;/td&gt;
&lt;td style=&quot;height: 43px; width: 313px;&quot;&gt;DAO는 XML에 SQL 직접 작성, Repository는 메서드명으로 쿼리 자동 생성&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 56px;&quot;&gt;
&lt;td style=&quot;text-align: center; height: 56px; width: 163px;&quot;&gt;DEM.xml&lt;/td&gt;
&lt;td style=&quot;text-align: center; height: 56px; width: 196px;&quot;&gt;@Modifying, @Query &lt;br /&gt;또는 변경 감지&lt;/td&gt;
&lt;td style=&quot;height: 56px; width: 179px;&quot;&gt;INSERT / UPDATE &lt;br /&gt;/ DELETE 처리&lt;/td&gt;
&lt;td style=&quot;height: 56px; width: 313px;&quot;&gt;XML에 SQL 직접 작성 vs JPA는 Entity 변경 감지로 자동 SQL&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 43px;&quot;&gt;
&lt;td style=&quot;text-align: center; height: 43px; width: 163px;&quot;&gt;DQM.xml&lt;/td&gt;
&lt;td style=&quot;text-align: center; height: 43px; width: 196px;&quot;&gt;findBy...(), @Query&lt;/td&gt;
&lt;td style=&quot;height: 43px; width: 179px;&quot;&gt;SELECT 처리&lt;/td&gt;
&lt;td style=&quot;height: 43px; width: 313px;&quot;&gt;XML에 SQL 직접 작성 vs 메서드명 파싱으로 자동 쿼리 생성&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;SVO와 DVO, 그리고 DTO의 차이&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 프로젝트를 처음 보면 &lt;b&gt;SVO(Service Value Object)&lt;/b&gt;, &lt;b&gt;DVO(Data Valude Object)&lt;/b&gt;라는 용어가 가장 먼저 눈에 들어온다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;SVO(Service Value Object)&lt;/b&gt;: 화면 &amp;harr; Service&lt;/li&gt;
&lt;li&gt;&lt;b&gt;DVO(Data Valude Object)&lt;/b&gt;: Service &amp;harr; DAO(Data Access Object) &amp;harr; DB&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;흐름을 보면 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Browser &amp;rarr; Controller &amp;rarr; Service &amp;rarr; DAO &amp;rarr; DB&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 데이터 흐름은 조금 더 구체적이다. JPA 구조에서 활용되는 &lt;b&gt;DTO(Data Transfer Object)&lt;/b&gt;가 어디 매핑되는지 함께 표기하였다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Browser
  &amp;darr;
Controller &amp;rarr; SVO 수신 (DTO)
  &amp;darr;
Service &amp;rarr; SVO &amp;rarr; DVO 변환 (DTO &amp;rarr; DTO)
  &amp;darr;
DAO &amp;rarr; DVO 전달 (DTO)
  &amp;darr;
Mapper XML &amp;rarr; SQL 실행
  &amp;darr;
 DB&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 중요한 포인트는 &lt;b&gt;&amp;ldquo;객체를 분리한다&amp;rdquo;&lt;/b&gt;는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SVO는 화면 요청 데이터를 담는다. DVO는 DB 처리를 위한 데이터만 담는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 화면에서는 페이징이나 검색 조건이 포함될 수 있다.&lt;/p&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;pageIndex
pageSize
searchKeyword
mntrtmGroupSn&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 DELETE SQL에서는 다음 값만 필요하다.&lt;/p&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;mntrtmGroupSn&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 SVO를 그대로 DAO까지 넘기면 불필요한 데이터가 DB 계층까지 전달된다.&lt;br /&gt;그래서 Service에서 SVO를 DVO로 변환하는 과정이 존재한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;JPA 구조&lt;/b&gt;에서는 보통 다음과 같이 단순화된다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Controller &amp;rarr; DTO &amp;rarr; Service &amp;rarr; Entity &amp;rarr; DB&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;SVO &amp;rarr; DTO&lt;/li&gt;
&lt;li&gt;DVO &amp;rarr; Entity 또는 DTO&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;형태로 통합되는 경우가 많다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Layered Architecture&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 프로젝트의 핵심 구조는 계층 구조이다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Controller &amp;rarr; Service &amp;rarr; DAO &amp;rarr; DB&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 중요한 규칙이 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Controller는 Service만 호출한다&lt;/li&gt;
&lt;li&gt;Service는 DAO만 호출한다&lt;/li&gt;
&lt;li&gt;DAO는 DB 접근만 담당한다&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 규칙이 깨지면 구조는 바로 무너진다. 예를 들어 Service에서 SQL을 직접 실행하면 다음과 같은 문제가 발생한다.&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;비즈니스 로직 + 데이터 접근 로직 혼합&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DAO를 분리하면 다음과 같이 바뀐다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;// Service
comDAO.deleteMntrtmGroupInfo(dvo);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Service는 &amp;ldquo;무엇을 할지&amp;rdquo;만 알고, &amp;ldquo;어떻게 SQL을 실행하는지&amp;rdquo;는 모른다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이게 계층 분리의 핵심이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Front Controller와 Servlet (with AJAX)&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;레거시 구조에서는 &lt;b&gt;Servlet&lt;/b&gt;이 명확하게 드러난다. 브라우저가 요청을 보내면 서버(Tomcat)가 받고, 그 요청을 실제로 처리하는 것이 Servlet이다. 모든 요청은 DispatcherServlet을 통해 들어온다.&lt;/p&gt;
&lt;pre class=&quot;mipsasm&quot;&gt;&lt;code&gt;Browser &amp;rarr; DispatcherServlet &amp;rarr; Controller&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;web.xml에는 보통 다음과 같은 설정이 있다.&lt;/p&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;&amp;lt;servlet-mapping&amp;gt;
    &amp;lt;url-pattern&amp;gt;*.do&amp;lt;/url-pattern&amp;gt;
    &amp;lt;url-pattern&amp;gt;*.ajax&amp;lt;/url-pattern&amp;gt;
&amp;lt;/servlet-mapping&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 구조의 목적은 단순하다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;인증 처리&lt;/li&gt;
&lt;li&gt;로깅&lt;/li&gt;
&lt;li&gt;공통 예외 처리&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 요청을 하나의 진입점에서 처리하기 위한 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 &lt;b&gt;AJAX&lt;/b&gt;라는 말이 보이는데 AJAX란 페이지를 새로고침하지 않고 서버와 통신하는 방식이다. 그리고 이 ajax를 쉽게 쓰기 위한 것이 &lt;b&gt;jQquery&lt;/b&gt;라고 보면 된다. jQquery는 더 많은 기능이 있지만 간단하게 이렇게만 정리하고 넘어가자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정리하자면 &lt;b&gt;jQuery로 AJAX 요청을 보내고, Servlet이 그 요청을 처리&lt;/b&gt;한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;DAO와 Mapper XML&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;DAO(Data&amp;nbsp;Access&amp;nbsp;Object)&lt;/b&gt;는 데이터 접근을 담당하는 객체이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Service가 SQL을 직접 알지 않도록 분리하는 역할을 한다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;comDAO.deleteMntrtmGroupInfo(dvo);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 SQL은 Mapper XML에 존재한다.&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;&amp;lt;delete id=&quot;deleteMntrtmGroupInfo&quot;&amp;gt;
    DELETE FROM mntrtm_group
    WHERE mntrtm_group_sn = #{mntrtmGroupSn}
&amp;lt;/delete&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MyBatis의 특징은 SQL이 그대로 보인다는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;장점은 명확하다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;복잡한 쿼리 작성이 쉽다&lt;/li&gt;
&lt;li&gt;실행 SQL을 예측할 수 있다&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 단점도 존재한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;XML 관리 필요&lt;/li&gt;
&lt;li&gt;반복 코드 증가&lt;/li&gt;
&lt;li&gt;유지보수 비용 증가&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;DEM.xml과 DQM.xml&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 프로젝트에서는 XML도 역할에 따라 나뉜다. 레거시에선 대표적으로 &lt;b&gt;DEM(Data Elementary Module), DQM(Data Query XML)&lt;/b&gt;이 있었다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;DEM.xml &amp;rarr; INSERT / UPDATE / DELETE&lt;/li&gt;
&lt;li&gt;DQM.xml &amp;rarr; SELECT&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 나누는 이유는 명확하다. 데이터 변경과 조회를 분리하기 위함이다. JPA에서는 이 역할을 Repository가 담당한다.&lt;/p&gt;
&lt;pre class=&quot;aspectj&quot;&gt;&lt;code&gt;findBy...
@Query
@Modifying&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또는 Entity 변경 감지를 통해 자동으로 SQL이 생성된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;AOP(with Proxy 패턴)와 트랜잭션&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 중요한 개념 중 하나는 &lt;b&gt;AOP(Aspect-Oriented&amp;nbsp;Programming)&lt;/b&gt;이다. 이 프로젝트 뿐만 아니라 모든 프로젝트에서 트랜잭션과 관련된 내용은 중요하게 다루어져야한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 개발자는 다음과 같이 작성한다.&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;// 개발자가 작성한 코드 &amp;mdash; 트랜잭션 코드가 없음
public void delete(ComSVO svo) {
    comDAO.deleteA(dvo);
    comDAO.deleteB(dvo);  // 여기서 에러 나면?
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 실제 동작은 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;begin
deleteA
deleteB
commit
(에러 발생 시 rollback)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이&amp;nbsp;과정은&amp;nbsp;AOP를&amp;nbsp;통해&amp;nbsp;처리된다.&amp;nbsp;개발자는&amp;nbsp;비즈니스&amp;nbsp;코드&amp;nbsp;안에&amp;nbsp;트랜잭션&amp;nbsp;시작,&amp;nbsp;커밋,&amp;nbsp;롤백&amp;nbsp;코드를&amp;nbsp;직접&amp;nbsp;작성하지&amp;nbsp;않아도&amp;nbsp;된다.&amp;nbsp;Spring에서는&amp;nbsp;트랜잭션&amp;nbsp;AOP가&amp;nbsp;보통&amp;nbsp;&lt;b&gt;Proxy 패턴 &lt;/b&gt;기반으로 동작한다. 즉, Service 객체를 직접 호출하는 것처럼 보이지만 실제로는 Proxy 객체가 먼저 호출되고, 이 Proxy가 트랜잭션을 시작한 뒤 실제 Service 메서드를 실행한다. 메서드가 정상 종료되면 commit을 수행하고, 예외가 발생하면 rollback을 수행한다. 다만 이 글에서는 Proxy 패턴 자체를 깊게 다루지는 않고, 트랜잭션이 AOP를 통해 비즈니스 로직과 분리된다는 점만 짚고 넘어가려 한다. 다음은 ServiceImpl 계층에 AOP를 적용하기 위한 pointcut 설정 예시이다.&lt;/p&gt;
&lt;pre id=&quot;code_1777863343902&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;aop:config&amp;gt;
    &amp;lt;aop:pointcut
        id=&quot;servicePointcut&quot;
        expression=&quot;execution(* ..impl.*Impl.*(..))&quot; /&amp;gt;

    &amp;lt;aop:advisor
        advice-ref=&quot;txAdvice&quot;
        pointcut-ref=&quot;servicePointcut&quot; /&amp;gt;
&amp;lt;/aop:config&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;pointcut&amp;nbsp;=&amp;nbsp;어디에&amp;nbsp;적용할지&lt;/li&gt;
&lt;li&gt;advice&amp;nbsp;=&amp;nbsp;무엇을&amp;nbsp;적용할지&lt;/li&gt;
&lt;li&gt;advisor&amp;nbsp;=&amp;nbsp;pointcut과&amp;nbsp;advice를&amp;nbsp;연결&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결론은 AOP를 적용하면 개발자는 delete라는 비즈니스 로직까지만 신경쓰면 되기에 편리하다는 것이다. 실제로 자세하게 동작하는 모습을 코드로 직접 구현한다면 다음과 같은 형태이다.&lt;/p&gt;
&lt;pre id=&quot;code_1777863409160&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 자동 생성된 프록시 (개발자 눈에는 안 보임)
try {
    transaction.begin();
    comDAO.deleteA(dvo);     // ✅
    comDAO.deleteB(dvo);     // ❌ 에러!
    transaction.commit();
} catch (Exception e) {
    transaction.rollback();  // deleteA도 취소됨 &amp;rarr; 데이터 일관성 보장
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 구조의 핵심은 다음이다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;트랜잭션 코드 제거&lt;/li&gt;
&lt;li&gt;실수 방지&lt;/li&gt;
&lt;li&gt;데이터 일관성 유지&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 Service 계층에 AOP를 적용함으로써 비즈니스 로직과 트랜잭션 로직을 분리할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Template Method 패턴과 공통 구조&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;*AbstractServiceImpl, BaseAbstractDAO와 같은 네이밍으로 구성된 구조는 대부분 &lt;b&gt;Template Method 패턴&lt;/b&gt;이다. 공통 실행 흐름은 부모 클래스에 존재하고, 세부 구현은 자식 클래스가 담당한다.&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;BaseAbstractDAO
   &amp;uarr;
ComDAO&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 구조를 통해 sqlSession 호출 같은 반복 코드를 줄일 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Decorator 패턴과 SiteMesh&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SiteMesh는 화면에 공통 레이아웃(header, footer 등)을 입히는 역할을 한다. 이 구조는 기존 View를 감싸는 방식으로 동작하기 때문에 &lt;b&gt;Decorator 패턴&lt;/b&gt;으로 볼 수 있다. 일반적인 흐름은 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;cos&quot;&gt;&lt;code&gt;Controller &amp;rarr; View &amp;rarr; SiteMesh &amp;rarr; Browser&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Controller는&amp;nbsp;View를&amp;nbsp;반환하고,&amp;nbsp;SiteMesh는&amp;nbsp;이&amp;nbsp;View를&amp;nbsp;그대로&amp;nbsp;브라우저로&amp;nbsp;보내지&amp;nbsp;않고&amp;nbsp;한&amp;nbsp;번&amp;nbsp;감싼&amp;nbsp;뒤&amp;nbsp;공통&amp;nbsp;레이아웃을&amp;nbsp;추가한다.&amp;nbsp;즉,&amp;nbsp;원본&amp;nbsp;View를&amp;nbsp;수정하지&amp;nbsp;않고&amp;nbsp;외부에서&amp;nbsp;기능을&amp;nbsp;덧붙이는&amp;nbsp;구조이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 *.ajax 요청은 다르다. AJAX&amp;nbsp;요청은&amp;nbsp;HTML이&amp;nbsp;아니라&amp;nbsp;JSON&amp;nbsp;데이터를&amp;nbsp;반환하기&amp;nbsp;때문이다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;*.do &amp;rarr; HTML &amp;rarr; SiteMesh 적용&lt;/li&gt;
&lt;li&gt;*.ajax &amp;rarr; JSON &amp;rarr; SiteMesh 미적용&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1777865439095&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{ &quot;result&quot;: &quot;success&quot; }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AJAX 응답에 header, footer 같은 HTML 레이아웃이 붙으면 JSON이 깨진다. 그래서 AJAX 요청에는 SiteMesh를 적용하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;전체 흐름 정리&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;삭제 요청 기준으로 전체 흐름을 보면 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;Browser 삭제 버튼 클릭
  &amp;darr;
DispatcherServlet이 URL 라우팅 (Front Controller)
  &amp;darr;
Controller (SVO(or DTO)로 파라미터 수신)
  &amp;darr;
Service (SVO &amp;rarr; DVO 변환, AOP 트랜잭션 적용)
  &amp;darr;
DAO (SQL 호출)
  &amp;darr;
Mapper XML &amp;rarr; PostgreSQL or Other DB
  &amp;darr;
JSON 응답 (*.ajax &amp;rarr; SiteMesh 미적용)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 흐름 하나에 여러 개념이 동시에 포함된다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Layered Architecture&lt;/li&gt;
&lt;li&gt;DTO (SVO / DVO)&lt;/li&gt;
&lt;li&gt;DAO Pattern&lt;/li&gt;
&lt;li&gt;AOP&lt;/li&gt;
&lt;li&gt;Servlet / Front Controller&lt;/li&gt;
&lt;li&gt;Template Method&lt;/li&gt;
&lt;li&gt;Decorator&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;레거시 구조는 복잡해 보이지만 오히려 구조가 명확하게 드러나고 흐름이 눈에 보인다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;어디서 요청이 시작되는지&lt;/li&gt;
&lt;li&gt;어디서 객체가 변환되는지&lt;/li&gt;
&lt;li&gt;어디서 SQL이 실행되는지&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면 JPA는 많은 부분을 자동화한다. 하지만 자동화된 구조를 제대로 이해하려면 이런 명시적인 구조를 먼저 이해하는 것이 중요하다. 결국 이 레거시 구조를 분석하는 과정은 Spring의 기본 개념을 다시 정리하는 과정이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로 한번 더 정리해 보면 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;MyBatis는 SQL 중심 구조이다&lt;/li&gt;
&lt;li&gt;JPA는 객체 중심 구조이다&lt;/li&gt;
&lt;li&gt;SVO는 화면 요청 객체이다&lt;/li&gt;
&lt;li&gt;DVO는 DB 처리 객체이다&lt;/li&gt;
&lt;li&gt;DAO는 Service와 DB를 분리한다&lt;/li&gt;
&lt;li&gt;Mapper XML은 실제 SQL을 가진다&lt;/li&gt;
&lt;li&gt;Servlet은 요청의 진입점이다&lt;/li&gt;
&lt;li&gt;AOP는 트랜잭션을 자동 처리한다&lt;/li&gt;
&lt;li&gt;SiteMesh는 화면 레이아웃을 담당한다&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Languages/Java(Spring Boot)</category>
      <category>ajax</category>
      <category>aop</category>
      <category>DAO</category>
      <category>DVO</category>
      <category>hiberante</category>
      <category>mybatis</category>
      <category>servlet</category>
      <category>spring boot</category>
      <category>SVO</category>
      <category>XML Mapper</category>
      <author>AlienCoder</author>
      <guid isPermaLink="true">https://aliencoder.tistory.com/240</guid>
      <comments>https://aliencoder.tistory.com/240#entry240comment</comments>
      <pubDate>Mon, 4 May 2026 11:49:38 +0900</pubDate>
    </item>
    <item>
      <title>codex cli permission 모두 허용하기 (Windows, macOS)</title>
      <link>https://aliencoder.tistory.com/239</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;300&quot; data-origin-height=&quot;168&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DWQrm/dJMcafTwR9n/myY1YeuBGvc5ljO7o544S0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DWQrm/dJMcafTwR9n/myY1YeuBGvc5ljO7o544S0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DWQrm/dJMcafTwR9n/myY1YeuBGvc5ljO7o544S0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDWQrm%2FdJMcafTwR9n%2FmyY1YeuBGvc5ljO7o544S0%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;637&quot; height=&quot;357&quot; data-origin-width=&quot;300&quot; data-origin-height=&quot;168&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전에 claude에 대한 skip permission을 올렸었는데 codex도 마찬가지로 파일을 분석시키거나 커맨드 명령을 수행할 때 항상 사용자에게 허락을 받고 진행하는게 많아서 효율이 떨어진다 느껴졌다. codex도 claude 못지 않게 좋은 성능을 보이고 있으므로 이 역시 나보다 나을 때가 많기에 믿고 쓰기로 한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;1. 일회성으로 사용하는 명령&lt;/b&gt;&lt;/h2&gt;
&lt;pre id=&quot;code_1774398317290&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;codex --dangerously-bypass-approvals-and-sandbox&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또는 (동일 기능)&lt;/p&gt;
&lt;pre id=&quot;code_1774398336151&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;codex --yolo&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;2. Windows에서 alias를 영구적으로 설정하는 방법&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Mac에 비해 윈도우는 좀 복잡한 것 같은데 alias(매크로)로 doskey + AutoRun 조합을 사용하여 해결할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 아래 명령을 Power Shell에서 관리자 모드로 실행하여 매크로 파일 생성/편집을 실행한다.&lt;/p&gt;
&lt;pre id=&quot;code_1774398346769&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;notepad &quot;$env:USERPROFILE\doskey_aliases.txt&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 다음 메모장이 뜨면 아래 한 줄을 그대로 붙여 넣고 저장해 준다. 이건 3가지 종류가 있는데 취사선택하면 된다. 샌드박스라는 작업 범위 제한으로 codex가 시스템 전체를 건드리지 못하게하는 환경 설정을 따로 옵션으로 줄 수 있어서 여러가지로 나누어봤다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2.1 가장 위험한 방법인 전체 범위 샌드박스까지 모두 허용&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1774398355622&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;codex=codex --dangerously-bypass-approvals-and-sandbox $*&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt;h4data-ke-size=&quot;size20&quot;&amp;gt;&lt;b&gt;2.2 작업 경로에서만 샌드박스 권한 허용&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1774398685225&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;codex --ask-for-approval never --sandbox workspace-write&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2.3 승인만 끄는 비교적 안전한 방식&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1774398365566&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;codex=codex --ask-for-approval never --sandbox workspace-write $*&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 내용들 중 하나를 입력하고나서&amp;nbsp;마지막으로 cmd가 열릴 때마다 자동으로 doskey 로드되게 영구 등록하기 위해 레지스트리를 등록해 준다.&lt;/p&gt;
&lt;pre id=&quot;code_1774398372001&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;reg add &quot;HKCU\Software\Microsoft\Command Processor&quot; /v AutoRun /t REG_SZ /d &quot;doskey /macrofile=%USERPROFILE%\doskey_aliases.txt&quot; /f&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작업하던 cmd 창을 닫아 세션을 종료하고 새로운 cmd 창을 띄우고 codex를 입력하면 skip permissions 모드로 실행된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;3. macOS에서 alias를 영구적으로 설정하는 방법&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;macOS는 기본 셸이 &lt;b&gt;zsh&lt;/b&gt;이다. bash를 사용하는 경우도 동일한 원리이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설정 파일 편집을 위해 아래 명령을 실행한다.&lt;/p&gt;
&lt;pre id=&quot;code_1774398379121&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;nano ~/.zshrc&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 다음 alias를 추가해 준다. 각각 명령 순서는 위의 윈도우와 동일하다. 원하는걸 다음 3가지 중 하나를 선택하면 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1774398385139&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;alias codex='codex --dangerously-bypass-approvals-and-sandbox'&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1774398816548&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;alias codex='codex --ask-for-approval never --sandbox workspace-write'&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1774398391437&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;alias codex='codex --ask-for-approval never --sandbox workspace-write'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저장하고 난 뒤 다음 명령으로 즉시 적용하여 사용할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1774398398580&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;source ~/.zshrc&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>AI/AI Tools</category>
      <category>allow</category>
      <category>codex</category>
      <category>codex cli</category>
      <category>permission</category>
      <author>AlienCoder</author>
      <guid isPermaLink="true">https://aliencoder.tistory.com/239</guid>
      <comments>https://aliencoder.tistory.com/239#entry239comment</comments>
      <pubDate>Wed, 25 Mar 2026 09:23:04 +0900</pubDate>
    </item>
    <item>
      <title>claude code skip permission으로 모두 허용하기 (Windows, macOS)</title>
      <link>https://aliencoder.tistory.com/238</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;720&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cilUrD/dJMcajnpuOr/JFdqCSDVAQsUjKkBkDDWz0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cilUrD/dJMcajnpuOr/JFdqCSDVAQsUjKkBkDDWz0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cilUrD/dJMcajnpuOr/JFdqCSDVAQsUjKkBkDDWz0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcilUrD%2FdJMcajnpuOr%2FJFdqCSDVAQsUjKkBkDDWz0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;689&quot; height=&quot;388&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;720&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;claude code를 사용하며 파일을 분석시키거나 커맨드 명령을 수행할 때 항상 사용자에게 허락을 받고 진행하는데 가끔 너무 자주 물어봐서 효율이 떨어진다 느껴졌다. plan mode로 계획을 모두 세웠을 때도 가끔 중간에 물어봐서 작업이 끊기기도 한다. 웬만큼 plan을 잘 세워두면 나보다 나을 때가 많기에 믿고 쓰기로 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000;&quot; data-ke-size=&quot;size26&quot; data-start=&quot;329&quot; data-end=&quot;373&quot;&gt;&lt;b&gt;1. 일회성으로 사용하는 명령&lt;/b&gt;&lt;/h2&gt;
&lt;pre id=&quot;code_1768824396757&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;claude --dangerously-skip-permissions&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-end=&quot;373&quot; data-start=&quot;329&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;2. Windows에서 alias를 영구적으로 설정하는 방법&lt;/b&gt;&lt;/h2&gt;
&lt;p data-end=&quot;427&quot; data-start=&quot;375&quot; data-ke-size=&quot;size16&quot;&gt;Mac에 비해 윈도우는 좀 복잡한 것 같은데 alias(매크로)로 doskey + AutoRun 조합을 사용하여 해결할 수 있다.&lt;/p&gt;
&lt;p data-end=&quot;427&quot; data-start=&quot;375&quot; data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;먼저 아래 명령을 Power Shell에서 관리자 모드로 실행하여 매크로 파일 생성/편집을 실행한다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1768824480106&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;notepad &quot;$env:USERPROFILE\doskey_aliases.txt&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-end=&quot;427&quot; data-start=&quot;375&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 다음 메모장이 뜨면 아래 한 줄을 그대로 붙여 넣고 저장해 준다.&lt;/p&gt;
&lt;pre id=&quot;code_1768824592436&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;claude=claude --dangerously-skip-permissions $*&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로 cmd가 열릴 때마다 자동으로 doskey 로드되게 영구 등록하기 위해 레지스트리를 등록해 준다.&lt;/p&gt;
&lt;pre id=&quot;code_1768825158723&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;reg add &quot;HKCU\Software\Microsoft\Command Processor&quot; /v AutoRun /t REG_SZ /d &quot;doskey /macrofile=%USERPROFILE%\doskey_aliases.txt&quot; /f&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작업하던 cmd 창을 닫아 세션을 종료하고 새로운 cmd 창을 띄우고 claude를 입력하면 skip permissions 모드로 실행된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-end=&quot;1705&quot; data-start=&quot;1674&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;3. macOS에서 alias를 영구적으로 설정하는 방법&lt;/b&gt;&lt;/h2&gt;
&lt;p data-end=&quot;1755&quot; data-start=&quot;1707&quot; data-ke-size=&quot;size16&quot;&gt;macOS는 기본 셸이 &lt;b&gt;zsh&lt;/b&gt;이다. bash를 사용하는 경우도 동일한 원리이다.&lt;/p&gt;
&lt;p data-end=&quot;1755&quot; data-start=&quot;1707&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;1755&quot; data-start=&quot;1707&quot; data-ke-size=&quot;size16&quot;&gt;설정 파일 편집을 위해 아래 명령을 실행한다.&lt;/p&gt;
&lt;pre id=&quot;code_1768824720854&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;nano ~/.zshrc&lt;/code&gt;&lt;/pre&gt;
&lt;p data-end=&quot;1755&quot; data-start=&quot;1707&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;1755&quot; data-start=&quot;1707&quot; data-ke-size=&quot;size16&quot;&gt;그 다음 alias를 추가해 준다.&lt;/p&gt;
&lt;pre id=&quot;code_1768824737356&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;alias claude='claude --dangerously-skip-permissions'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-end=&quot;1755&quot; data-start=&quot;1707&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;1755&quot; data-start=&quot;1707&quot; data-ke-size=&quot;size16&quot;&gt;저장하고 난 뒤 다음 명령으로 즉시 적용하여 사용할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1768824759751&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;source ~/.zshrc&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>AI/AI Tools</category>
      <category>allow</category>
      <category>bypass</category>
      <category>Claude</category>
      <category>claude code</category>
      <category>dangerously-skip-permissions</category>
      <category>skip permission</category>
      <author>AlienCoder</author>
      <guid isPermaLink="true">https://aliencoder.tistory.com/238</guid>
      <comments>https://aliencoder.tistory.com/238#entry238comment</comments>
      <pubDate>Mon, 19 Jan 2026 21:14:33 +0900</pubDate>
    </item>
    <item>
      <title>마샬링(Marshalling)과 직렬화(Serialization)</title>
      <link>https://aliencoder.tistory.com/237</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;마샬링(Marshalling)&lt;/b&gt;은 객체나 데이터 구조를 전송 및 저장에 적합한 표준 형식으로 변환하는 과정이다. 이 개념은 분산 시스템과 네트워크 통신에서 필수적인 핵심 기술이다. 마샬링과 직렬화의 내용이 혼용되기 쉬워서 정리하려고 한다. 거기에 더해 언마샬링에 대해서도 간단하게 적어보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;마샬링(Marshalling)&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마샬링은 프로그램 내부 메모리에 존재하는 객체를 외부로 전달 가능한 형태로 바꾸는 과정이다. 메모리 구조는 프로세스마다 다르기 때문에 그대로는 다른 시스템이나 프로세스가 이해할 수 없다. 이 문제를 해결하기 위해 객체의 구조와 값을 JSON, XML, 바이너리 스트림과 같은 표준 포맷으로 변환하는&amp;nbsp;작업이 마샬링이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마샬링이 필요한 대표적인 이유 3가지를 간단하게 다음과 같이 정리할 수 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;프로세스 간 통신:&lt;/b&gt; 서로 다른 메모리 공간을 사용하는 프로세스는 객체를 직접 공유할 수 없다. 마샬링을 통해 데이터 형태로 변환해야 한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;분산 시스템 통신:&lt;/b&gt; 서로 다른 서버, 서로 다른 언어 환경(Java, Python, C++)에서도 동일한 데이터 구조를 이해하게 하다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;네트워크 전송:&lt;/b&gt;&amp;nbsp;네트워크는 객체를 전송하지 못하고 바이트 단위 데이터만 전송할 수 있다. 이를 해결하는 과정이 마샬링이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;언마샬링(Unmarshalling)&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;언마샬링은 마샬링의 반대 과정으로 전송되거나 저장된 데이터를 다시 원래의 객체 구조로 복원하는 과정이다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;전송 측: 객체 &amp;rarr; 마샬링 &amp;rarr; 데이터 스트림&lt;/li&gt;
&lt;li&gt;수신 측: 데이터 스트림 &amp;rarr; 언마샬링 &amp;rarr; 객체&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이&amp;nbsp;두&amp;nbsp;과정이&amp;nbsp;한&amp;nbsp;쌍으로&amp;nbsp;동작한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;직렬화(Serialization)&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;직렬화(Serialization)&lt;/b&gt;는 데이터 구조나 객체 상태를 동일하거나 다른 컴퓨터 환경에 저장하고 나중에 재구성할 수 있는 포맷으로 변환하는 과정이다. 객체를 직렬화하는 과정은 오브젝트를 마샬링 한다고도 한다. 반대로, 일련의 바이트로부터 데이터 구조를 추출하는 일은 &lt;b&gt;역직렬화(Deserialization)&lt;/b&gt;이라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;직렬화와 마샬링의 차이는 다음과 같다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 84px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;height: 21px; text-align: center;&quot;&gt;Serialization&lt;/td&gt;
&lt;td style=&quot;height: 21px; text-align: center;&quot;&gt;Marshalling&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px; text-align: center;&quot;&gt;정의&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;객체를 바이트 스트림으로 변환하다&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;객체를 &lt;b&gt;전송&amp;middot;상호운용 목적&lt;/b&gt;의 포맷으로 변환하다&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px; text-align: center;&quot;&gt;목적&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;저장 또는 전송 가능하게 만들다&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;&lt;b&gt;분산 환경에서 통신 가능하게 만들다&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px; text-align: center;&quot;&gt;관점&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;데이터 표현 중심이다&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;데이터 + 전송 맥락 중심이다&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;참고 자료&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://ko.wikipedia.org/wiki/%EC%A7%81%EB%A0%AC%ED%99%94&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://ko.wikipedia.org/wiki/%EC%A7%81%EB%A0%AC%ED%99%94&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://velog.io/@wlsrhkd4023/CS-%EB%A7%88%EC%83%AC%EB%A7%81Marshalling&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://velog.io/@wlsrhkd4023/CS-%EB%A7%88%EC%83%AC%EB%A7%81Marshalling&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Computer Science/Software Engineering</category>
      <category>marshalling</category>
      <category>Unmarshalling</category>
      <category>마샬링</category>
      <category>언마샬링</category>
      <category>직렬화</category>
      <author>AlienCoder</author>
      <guid isPermaLink="true">https://aliencoder.tistory.com/237</guid>
      <comments>https://aliencoder.tistory.com/237#entry237comment</comments>
      <pubDate>Mon, 29 Dec 2025 14:01:33 +0900</pubDate>
    </item>
    <item>
      <title>[Spring boot] Service, ServiceImpl 구조와 OCP</title>
      <link>https://aliencoder.tistory.com/236</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;640&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/beziAU/btsLJCUjM9q/1j7sNwamcIoIsAjwJrD1Vk/tfile.dat&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/beziAU/btsLJCUjM9q/1j7sNwamcIoIsAjwJrD1Vk/tfile.dat&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/beziAU/btsLJCUjM9q/1j7sNwamcIoIsAjwJrD1Vk/tfile.dat&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbeziAU%2FbtsLJCUjM9q%2F1j7sNwamcIoIsAjwJrD1Vk%2Ftfile.dat&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;732&quot; height=&quot;366&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;640&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;Spring 기반 애플리케이션을 설계하다 보면 Service 인터페이스와 이를 구현한 ServiceImpl 클래스를 분리하는 구조를 흔히 접하게 된다. 처음 Spring을 접하는 입장에서는 굳이 왜 이렇게 나누는지 의문이 자연스럽게 든다. 이 구조는 객체지향 설계 원칙, 특히 OCP(Open-Closed Principle)와 깊게 연결되어 있다고 하지만 여기에 대한 필요성과 논의는 오래도록 있어온 것 같다.&lt;br /&gt;&lt;br /&gt;이 글에서는 Service와 ServiceImpl의 관계를 객체지향 관점에서 정리하고, 이 구조가 OCP를 어떻게 만족시키는지 살펴보려 한다. 먼저 OCP(Open-Closed Principle)가 무엇인지 알아보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt; OCP(Open-Closed Principle)&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;OCP는 &amp;ldquo;소프트웨어 엔티티는 확장에는 열려 있어야 하고, 변경에는 닫혀 있어야 한다&amp;rdquo;는 원칙이다. 쉽게 말해 이는 기존 코드를 수정하지 않고도 새로운 기능을 추가할 수 있어야 한다는 의미이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 원칙이 중요한 이유는 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이미 검증된 코드를 수정하면 버그가 발생할 가능성이 높음&lt;/li&gt;
&lt;li&gt;기능이 추가될수록 수정 범위가 넓어지면 유지보수가 어려움&lt;/li&gt;
&lt;li&gt;협업 환경에서 코드 변경 충돌이 잦아짐&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 OCP를 만족하는 구조는 장기적인 관점에서 안정성과 확장성을 동시에 확보한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Service와&amp;nbsp;ServiceImpl의&amp;nbsp;기본&amp;nbsp;구조&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;Spring에서 일반적으로 사용하는 구조는 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Service&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Service:&lt;/b&gt; 비즈니스 로직의 역할(Role)을 정의하는 interface&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1766386338355&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public interface MemberService {

    void join(Member member);

    Member findMember(Long memberId);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;ServiceImpl&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;ServiceImpl:&lt;/b&gt; 해당 역할을 구현(Implementation)한 class&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1766386155257&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import org.springframework.stereotype.Service;

@Service
public class MemberServiceImpl implements MemberService {

    private final MemberRepository memberRepository;

    public MemberServiceImpl(MemberRepository memberRepository) {
        this.memberRepository = memberRepository;
    }

    @Override
    public void join(Member member) {
        memberRepository.save(member);
    }

    @Override
    public Member findMember(Long memberId) {
        return memberRepository.findById(memberId);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다시 말해 Service는 &amp;ldquo;무엇을 하는가&amp;rdquo;를 정의하고, ServiceImpl은 &amp;ldquo;어떻게 하는가&amp;rdquo;를 정의한다. 이 분리는 단순한 파일 분리가 아니라, 의존성 방향을 제어하기 위한 의도적인 설계이다. Controller나 다른 Service 계층은 Service 인터페이스에만 의존하고, 구체적인 구현체인 ServiceImpl에는 의존하지 않는다. 이로 인해 상위 계층은 하위 계층의 구현 변경으로부터 보호된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Service / ServiceImpl 구조와 OCP 예시&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;Service 인터페이스를 기준으로 의존성을 설계하면, 구현체가 변경되거나 추가되더라도 기존 코드를 수정할 필요가 없다. 예를 들어 결제 로직을 생각해 보면 다음과 같은 상황이 발생할 수 있다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;초기에는&amp;nbsp;카드&amp;nbsp;결제만&amp;nbsp;지원한다&lt;/li&gt;
&lt;li&gt;이후 계좌 이체 결제를 추가해야 한다&lt;/li&gt;
&lt;li&gt;계약 변경으로 외부 PG사를 변경해야 한다&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;초기 요구사항 반영&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;먼저 결제라는 역할을 정의하고 결제가 카드인지, 계좌이체인지, 외부 PG사인지에 대한 정보는 포함하지 않는 다음과 같은 Service를 작성한다.&lt;/p&gt;
&lt;pre id=&quot;code_1766386620054&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public interface PaymentService {

    void pay(PaymentRequest paymentRequest);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 다음 카드 결제 구현체를 다음과 같이 작성한다.&lt;/p&gt;
&lt;pre id=&quot;code_1766386423606&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import org.springframework.stereotype.Service;

@Service
public class CardPaymentServiceImpl implements PaymentService {

    private final CardPaymentClient cardPaymentClient;

    public CardPaymentServiceImpl(CardPaymentClient cardPaymentClient) {
        this.cardPaymentClient = cardPaymentClient;
    }

    @Override
    public void pay(PaymentRequest paymentRequest) {
        cardPaymentClient.requestPayment(
                paymentRequest.getAmount(),
                paymentRequest.getCardNumber()
        );
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;요구사항 변경&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;위와 같이 잘 사용하고 있는 도중 계좌이체 결제 구현체 추가되어야 하는 신규 요구사항이 접수되고 계좌이체 결제가 다음과 같이 추가된 상황이다. 이때 PaymentService 인터페이스는 전혀 변경되지 않는다. 구현체만 확장되며, 이는 OCP를 충족하는 구조이다.&lt;/p&gt;
&lt;pre id=&quot;code_1766386713744&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import org.springframework.stereotype.Service;

@Service
public class AccountTransferPaymentServiceImpl implements PaymentService {

    private final BankTransferClient bankTransferClient;

    public AccountTransferPaymentServiceImpl(
            BankTransferClient bankTransferClient
    ) {
        this.bankTransferClient = bankTransferClient;
    }

    @Override
    public void pay(PaymentRequest paymentRequest) {
        bankTransferClient.transfer(
                paymentRequest.getAmount(),
                paymentRequest.getAccountNumber()
        );
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Controller는 다음과 같이 구현되어 있었고 결제 방식에 대해 전혀 알지 못하고 오직 PaymentService에만 의존한다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1766386788501&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import org.springframework.web.bind.annotation.RestController;

@RestController
public class PaymentController {

    private final PaymentService paymentService;

    public PaymentController(PaymentService paymentService) {
        this.paymentService = paymentService;
    }

    public void pay(PaymentRequest paymentRequest) {
        paymentService.pay(paymentRequest);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 이때 Controller나 상위 로직이 특정 구현 클래스에 직접 의존하고 있었다면, 구현이 바뀔 때마다 상위 코드도 함께 수정해야 한다. 이는 OCP를 위반하는 구조이다. 반면, Service 인터페이스에만 의존하고 있다면 새로운 ServiceImpl을 추가하거나 교체하는 것만으로 기능 확장이 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Discussion&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;실제로 프로젝트를 구현할 때 1:N으로 구현체를 만드는 경우보단 1:1로 만드는 경우가 더 많은 것 같다. 따로 Service 이외에도 구현체를 만드는 과정이 불필요해보이기도 하고 번거롭기도 하다. 다시 말해 모든 Service에 반드시 인터페이스를 만들어야 하는 것은 아니다. 다음과 같은 경우는 Service에 직접 구현하는 것도 좋은 방안이 될 수 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;구현이&amp;nbsp;단&amp;nbsp;하나뿐이고&amp;nbsp;변경&amp;nbsp;가능성이&amp;nbsp;매우&amp;nbsp;낮은&amp;nbsp;경우&lt;/li&gt;
&lt;li&gt;프로젝트 규모가 작고 확장이 거의 없는 경우&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 위에 보이던 예시와 같이 시시각각 요구사항에 대응하기엔 추상화된 Service Layer를 쉽게 확인할 수 있게 구현하고 뒤에 세부 로직들을 바꾸는 것이 좀 더 명확하고 협업하는 상황에서 의도를 전달하기 더 좋다고 생각된다. 또한&amp;nbsp;규모가&amp;nbsp;커지거나,&amp;nbsp;요구사항&amp;nbsp;변경&amp;nbsp;가능성이&amp;nbsp;조금이라도&amp;nbsp;존재한다면&amp;nbsp;Service와&amp;nbsp;ServiceImpl을&amp;nbsp;분리하는&amp;nbsp;설계가&amp;nbsp;장기적으로&amp;nbsp;유리하다.&amp;nbsp;이는&amp;nbsp;미래의&amp;nbsp;변경&amp;nbsp;비용을&amp;nbsp;현재의&amp;nbsp;설계&amp;nbsp;비용으로&amp;nbsp;미리&amp;nbsp;지불하는&amp;nbsp;선택이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;그리고 Impl이라는 네이밍 컨벤션에 대한 논의들도 다수 존재하는 것 같은데 관습적으로 굳어진 것을 혼자 더 좋은 방향으로 바꾸려고 하는 것도 쉽지는 않을 것 같아 다수의 의견에 동참하는 것이 작업 효율을 더 높이는 방향이지 않을까 생각한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style5&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;참고 자료&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://junior-datalist.tistory.com/243&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://junior-datalist.tistory.com/243&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Languages/Java(Spring Boot)</category>
      <category>OCP</category>
      <category>Open-Closed Principle</category>
      <category>service</category>
      <category>ServiceImpl</category>
      <author>AlienCoder</author>
      <guid isPermaLink="true">https://aliencoder.tistory.com/236</guid>
      <comments>https://aliencoder.tistory.com/236#entry236comment</comments>
      <pubDate>Mon, 22 Dec 2025 16:19:08 +0900</pubDate>
    </item>
    <item>
      <title>Sort(정렬) 알고리즘 정리</title>
      <link>https://aliencoder.tistory.com/235</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;자주 사용하는 정렬 알고리즘들을 정리해보려 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;버블 정렬(Bubble Sort)&lt;/b&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;정의&lt;/b&gt;: 인접한 원소들을 비교하여 큰 값을 뒤로 보내는 정렬&lt;/li&gt;
&lt;li&gt;&lt;b&gt;시간복잡도&lt;/b&gt;: O(n&amp;sup2;) - 삽입/삭제 후 재정렬 필요, 검색 O(n)&lt;b&gt;&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;480&quot; data-origin-height=&quot;150&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cW65RS/btsQrKmIwQ6/NiGLL9ftKe0LeSee2k3991/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cW65RS/btsQrKmIwQ6/NiGLL9ftKe0LeSee2k3991/img.gif&quot; data-alt=&quot;Fig 1. Bubble Sort&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cW65RS/btsQrKmIwQ6/NiGLL9ftKe0LeSee2k3991/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/cW65RS/btsQrKmIwQ6/NiGLL9ftKe0LeSee2k3991/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;480&quot; height=&quot;150&quot; data-origin-width=&quot;480&quot; data-origin-height=&quot;150&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Fig 1. Bubble Sort&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;의사코드&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1757502245379&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;for i = 0 to n-2:
    for j = 0 to n-2-i:
        if arr[j] &amp;gt; arr[j+1]:
            swap(arr[j], arr[j+1])&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;선택 정렬(Selection Sort)&lt;/b&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;정의&lt;/b&gt;: 최솟값을 찾아 앞쪽부터 차례로 배치하는 정렬&lt;/li&gt;
&lt;li&gt;&lt;b&gt;시간복잡도&lt;/b&gt;: O(n&amp;sup2;) - 삽입/삭제 후 재정렬 필요, 검색 O(n)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;480&quot; data-origin-height=&quot;150&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b9yhcX/btsQtS4AJkV/01s4YVBbfoXK5QF5QzxGR1/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b9yhcX/btsQtS4AJkV/01s4YVBbfoXK5QF5QzxGR1/img.gif&quot; data-alt=&quot;Fig 2. Selection Sort&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b9yhcX/btsQtS4AJkV/01s4YVBbfoXK5QF5QzxGR1/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/b9yhcX/btsQtS4AJkV/01s4YVBbfoXK5QF5QzxGR1/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;480&quot; height=&quot;150&quot; data-origin-width=&quot;480&quot; data-origin-height=&quot;150&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Fig 2. Selection Sort&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;의사코드&lt;/b&gt;&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre id=&quot;code_1757502262271&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;for i = 0 to n-2:
    min_idx = i
    for j = i+1 to n-1:
        if arr[j] &amp;lt; arr[min_idx]:
            min_idx = j
    swap(arr[i], arr[min_idx])&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;삽입 정렬(Insertion Sort)&lt;/b&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;정의&lt;/b&gt;: 각 원소를 정렬된 부분의 적절한 위치에 삽입하는 정렬&lt;/li&gt;
&lt;li&gt;&lt;b&gt;시간복잡도&lt;/b&gt;: O(n&amp;sup2;) - 삽입 O(n), 삭제 O(n), 검색 O(n)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;480&quot; data-origin-height=&quot;150&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/IweBp/btsQsjPC8v1/J3B30JSWDF0DwRqUpbMGqK/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/IweBp/btsQsjPC8v1/J3B30JSWDF0DwRqUpbMGqK/img.gif&quot; data-alt=&quot;Fig 3. Insertion Sort&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/IweBp/btsQsjPC8v1/J3B30JSWDF0DwRqUpbMGqK/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/IweBp/btsQsjPC8v1/J3B30JSWDF0DwRqUpbMGqK/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;480&quot; height=&quot;150&quot; data-origin-width=&quot;480&quot; data-origin-height=&quot;150&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Fig 3. Insertion Sort&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;의사코드&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre id=&quot;code_1757502275234&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;for i = 1 to n-1:
    key = arr[i]
    j = i-1
    while j &amp;gt;= 0 and arr[j] &amp;gt; key:
        arr[j+1] = arr[j]
        j = j-1
    arr[j+1] = key&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;병합 정렬(Merge Sort)&lt;/b&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;936&quot; data-origin-height=&quot;527&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/IuSRL/btsQWemkqpP/9T1zsKb6di0ul1byBNnatk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/IuSRL/btsQWemkqpP/9T1zsKb6di0ul1byBNnatk/img.png&quot; data-alt=&quot;Fig 4. Merge Sort 1&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/IuSRL/btsQWemkqpP/9T1zsKb6di0ul1byBNnatk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FIuSRL%2FbtsQWemkqpP%2F9T1zsKb6di0ul1byBNnatk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;556&quot; height=&quot;313&quot; data-origin-width=&quot;936&quot; data-origin-height=&quot;527&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Fig 4. Merge Sort 1&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;정의&lt;/b&gt;: 분할 정복으로 배열을 나누고 병합하며 정렬&lt;/li&gt;
&lt;li&gt;&lt;b&gt;시간복잡도&lt;/b&gt;: O(n log n) - 삽입/삭제 후 재정렬 필요, 검색 O(n)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;480&quot; data-origin-height=&quot;329&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kWCgQ/btsQtNbrbt4/jdHjoZkSNZkzfK4lr5nZd1/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kWCgQ/btsQtNbrbt4/jdHjoZkSNZkzfK4lr5nZd1/img.gif&quot; data-alt=&quot;Fig 5. Merge Sort 2&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kWCgQ/btsQtNbrbt4/jdHjoZkSNZkzfK4lr5nZd1/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/kWCgQ/btsQtNbrbt4/jdHjoZkSNZkzfK4lr5nZd1/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;480&quot; height=&quot;329&quot; data-origin-width=&quot;480&quot; data-origin-height=&quot;329&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Fig 5. Merge Sort 2&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;의사코드&lt;/b&gt;&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre id=&quot;code_1757502281275&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;MERGE(A, p, q, r)
    n1 = q - p + 1
    n2 = r - q
    let L[1..n1+1] and R[1..n2+1] be new arrays
    for i = 1 to n1
        L[i] = A[p + i - 1]
    for j = 1 to n2
        R[j] = A[q + j]
    L[n1 + 1] = &amp;infin;
    R[n2 + 1] = &amp;infin;
    i = 1
    j = 1
    for k = p to r
        if L[i] &amp;le; R[j]
            A[k] = L[i]
            i = i + 1
        else
            A[k] = R[j]
            j = j + 1

MERGE-SORT(A, p, r)
    if p &amp;lt; r
        q = &amp;lfloor;(p + r) / 2&amp;rfloor;     // divide
        MERGE-SORT(A, p, q)   // conquer
        MERGE-SORT(A, q+1, r) // conquer
        MERGE(A, p, q, r)     // combine&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;퀵 정렬(Quick Sort) - 분할 정복&lt;/b&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;정의&lt;/b&gt;: 피벗을 기준으로 분할하여 정렬하는 분할 정복 알고리즘&lt;/li&gt;
&lt;li&gt;&lt;b&gt;시간복잡도&lt;/b&gt;: 평균 O(n log n), 최악 O(n&amp;sup2;) - 삽입/삭제 후 재정렬 필요, 검색 O(n)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;480&quot; data-origin-height=&quot;159&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qBQyJ/btsQu08Pyw3/ulGsu9UODrDBlkzgHLzkkk/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qBQyJ/btsQu08Pyw3/ulGsu9UODrDBlkzgHLzkkk/img.gif&quot; data-alt=&quot;Fig 6. Quick Sort&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qBQyJ/btsQu08Pyw3/ulGsu9UODrDBlkzgHLzkkk/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/qBQyJ/btsQu08Pyw3/ulGsu9UODrDBlkzgHLzkkk/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;480&quot; height=&quot;159&quot; data-origin-width=&quot;480&quot; data-origin-height=&quot;159&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Fig 6. Quick Sort&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;의사코드&lt;/b&gt;&lt;/p&gt;
&lt;div&gt;
&lt;pre id=&quot;code_1757502289678&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;QuickSort(A, p, r)
    if p &amp;gt;= r
        return
    else
        q &amp;lt;- partition(A, p, r)
        QuickSort(A, p, q - 1)
        QuickSort(A, q + 1, r)

partition(A, p, r)
    x = A[r]
    i = p - 1
    for j = p to r - 1
        if A[j] &amp;lt;= x
            i = i + 1
            A[i] &amp;lt;-&amp;gt; A[j]   // exchange
    A[i + 1] &amp;lt;-&amp;gt; A[r]       // exchange
    return i + 1&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Quick Sort는 위의 시간복잡도를 자세히 보면 &lt;b&gt;성능의 이중성&lt;/b&gt;이 있는 것을 알 수 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;평균&lt;/b&gt;: O(n log n) - 매우 빠름&lt;/li&gt;
&lt;li&gt;&lt;b&gt;최악&lt;/b&gt;: O(n&amp;sup2;) - 매우 느림&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 피벗 선택에 따라 성능이 극단적으로 달라지기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;퀵 정렬은 줄 세우기 게임으로 설명이 비유할 수 있다. 예를 들어 한 명을 기준(피벗)으로 세워두고, 나머지를 &quot;기준보다 작은 사람은 왼쪽, 큰 사람은 오른쪽&quot;으로 나눈다고 가정하자.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;data: [7, 2, 9, 1, 5] &lt;br /&gt;기준을 7로 정함 &lt;br /&gt;&lt;br /&gt;7보다 작은 항목: [2, 1, 5]&lt;br /&gt;7과 동일한 항목: [7]&lt;br /&gt;7 보다 큰 항목: [9]&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;case 1. 운이 좋을 때(기준을 잘 골랐을 때)&lt;/b&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;[5,&amp;nbsp;1,&amp;nbsp;9,&amp;nbsp;2,&amp;nbsp;8]&amp;nbsp;&amp;rarr;&amp;nbsp;기준&amp;nbsp;5 &lt;br /&gt;[1,&amp;nbsp;2]&amp;nbsp;[5]&amp;nbsp;[9,&amp;nbsp;8]&amp;nbsp;&amp;rarr;&amp;nbsp;반반&amp;nbsp;나눠짐&amp;nbsp;&amp;rarr;&amp;nbsp;빠름&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;case 2. 운이 나쁠 때(기준을 잘못 골랐을 때)&lt;/b&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;[1,&amp;nbsp;2,&amp;nbsp;3,&amp;nbsp;4,&amp;nbsp;5]&amp;nbsp;&amp;rarr;&amp;nbsp;기준&amp;nbsp;5 &lt;br /&gt;[1,&amp;nbsp;2,&amp;nbsp;3,&amp;nbsp;4]&amp;nbsp;[5]&amp;nbsp;[]&amp;nbsp;&amp;rarr;&amp;nbsp;한쪽만&amp;nbsp;많음&amp;nbsp;&amp;rarr;&amp;nbsp;느림&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다시 말해 Quick Sort는 기준 하나 정해서 양쪽으로 나누는 정렬인데, 기준을 잘 고르면 엄청 빠르고 못 고르면 엄청 느린 도박성 정렬이라고 볼 수 있다. 하지만 실제로 대부분의 라이브러리에서 기본 정렬로 채택하고 있는데 이는 평균 성능이 뛰어나고, 개선 기법들로 최악 케이스 회피 가능하기 때문이다. 개선 기법들은 대표적으로 다음과 같은 방법들이 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Randomized&amp;nbsp;Pivot:&amp;nbsp;피벗을&amp;nbsp;랜덤&amp;nbsp;선택&lt;/li&gt;
&lt;li&gt;3-way&amp;nbsp;Partitioning:&amp;nbsp;중복값&amp;nbsp;처리&amp;nbsp;최적화&lt;/li&gt;
&lt;li&gt;Hybrid&amp;nbsp;Sort:&amp;nbsp;작은&amp;nbsp;배열에서는&amp;nbsp;삽입정렬&amp;nbsp;사용&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한 가지 더 의문이 생긴다. Quick Sort의 정렬에 대한 시간 복잡도는 Merge Sort의 최악의 경우와 동일한데도 많이 사용하는 이유가 무엇일까? 이유는 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Merge Sort는 원본 배열 + 추가 배열 = 2배의 메모리 필요한데 비해, Quick Sort는 원본 배열 = 1배만큼의 메모리만 있어도 된다. 이러한 특성은 큰 데이터에서는 메모리가 부족할 수 있거나 서버 환경에서 메모리 비용이 중요한 환경에서 이점을 가져가기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;힙 정렬(Heap Sort)&lt;/b&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;정의&lt;/b&gt;: 힙 자료구조를 이용한 정렬 알고리즘&lt;/li&gt;
&lt;li&gt;&lt;b&gt;시간복잡도&lt;/b&gt;: O(n log n) - 삽입/삭제 O(log n), 검색 O(n)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;608&quot; data-origin-height=&quot;535&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bcCvWa/btsQsMKM2aW/y7E5GaSlyewssW61cD3uY1/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bcCvWa/btsQsMKM2aW/y7E5GaSlyewssW61cD3uY1/img.gif&quot; data-alt=&quot;Fig 7. Heap Sort&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bcCvWa/btsQsMKM2aW/y7E5GaSlyewssW61cD3uY1/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/bcCvWa/btsQsMKM2aW/y7E5GaSlyewssW61cD3uY1/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;357&quot; height=&quot;314&quot; data-origin-width=&quot;608&quot; data-origin-height=&quot;535&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Fig 7. Heap Sort&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;의사코드&lt;/b&gt;&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre id=&quot;code_1757502300552&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;heapSort(arr):
    buildMaxHeap(arr)
    for i = n-1 to 1:
        swap(arr[0], arr[i])
        heapify(arr, 0, i)

buildMaxHeap(arr):
    for i = n/2-1 to 0:
        heapify(arr, i, n)&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;참고 자료&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://hyo-ue4study.tistory.com/68&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://hyo-ue4study.tistory.com/68&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://emre.me/algorithms/sorting-algorithms/#heap-sort&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://emre.me/algorithms/sorting-algorithms/#heap-sort&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #555555; text-align: center;&quot;&gt;&lt;a href=&quot;https://www.101computing.net/merge-sort-algorithm/&quot;&gt;https://www.101computing.net/merge-sort-algorithm/&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://jjunsu.tistory.com/187&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://jjunsu.tistory.com/187&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Computer Science/Algorithm</category>
      <category>bubble sort</category>
      <category>heap sort</category>
      <category>Insertion Sort</category>
      <category>Merge sort</category>
      <category>Quick Sort</category>
      <category>Selection sort</category>
      <author>AlienCoder</author>
      <guid isPermaLink="true">https://aliencoder.tistory.com/235</guid>
      <comments>https://aliencoder.tistory.com/235#entry235comment</comments>
      <pubDate>Wed, 10 Sep 2025 19:40:57 +0900</pubDate>
    </item>
    <item>
      <title>LogSumExp trick</title>
      <link>https://aliencoder.tistory.com/234</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;
&lt;script src=&quot;https://polyfill.io/v3/polyfill.min.js?features=es6&quot;&gt;&lt;/script&gt;
&lt;script src=&quot;https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js&quot;&gt;&lt;/script&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최근 수업 중 custom loss를 구현하는 과제에서 알게 된 trick인데 softmax 연산을 수행할 때 내부적으로 overflow나 underflow를 방지하기 위한 방법으로 이 방식을 사용한다고 한다. 이름에 나오듯 Log와 Summation 그리고 Exponential을 이용한 트릭이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;exp를 기존 방식대로 계산해보면 아래와 같이 overflow가 발생하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1750665780772&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import numpy as np

x = np.array([1000, 1000, 1000])
print(np.exp(x))
# [inf inf inf]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;x와 같은 logit(입력값)을 구해 softmax 연산을 위해 적용하였을 때 overflow가 발생하게 되고 이는 결국 loss가 발산하거나 backpropagation이 실패하는 결과로 이어질 수 있다. 물론 x와 같이 극단적인 크기의 logit이 발생하는 경우는 정규화 등을 통해 방지할 수 있겠지만, 저런 식으로 발생할 수밖에 없는 경우도 있을 수 있기에 logsumexp를 이용해 사전에 방지할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Softmax와 LogSumExp&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Softmax의 수식은 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ p_i = \text{softmax}(x_i) = \frac{\exp(x_i)}{\sum_{n=1}^{N} \exp(x_n)} $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 \( p_i \)는 다음과 같이 정의된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ \sum_{n=1}^{N} p_i = 1 $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 LogSumExp의 수식을 살펴보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ \mathrm{LSE}(x_1, \dots, x_N) = \log\left( \sum_{n=1}^{N} \exp(x_n) \right) $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이름 그대로 Log, Summation, Exponential 순서로 전개된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Softmax 수식 유도&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번엔 자주 사용하는 softmax를 LSE를 사용하는 형태로 유도해 보자. 사실 간단하게 softmax 수식에 양변에 log를 취하면 되지만 절차에 따라 조금 더 상세히 살펴보려한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ p_i = \frac{\exp(x_i)}{\sum_{n=1}^{N} \exp(x_n)} $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 softmax 정의에서 denominator를 양변에 곱해주면 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ \exp(x_i) = p_i&amp;nbsp;\sum_{n=1}^{N}&amp;nbsp;\exp(x_n) $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 양변에 log를 취해주면 다음과 같이 정리된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;\begin{align*}&lt;br /&gt;\log(\exp(x_i)) = \log(p_i \sum_{n=1}^{N} \exp(x_n)) \\&lt;br /&gt;x_i&amp;nbsp;=&amp;nbsp;log(p_i)&amp;nbsp;+&amp;nbsp;\log(\sum_{n=1}^{N}&amp;nbsp;\exp(x_n))) &lt;br /&gt;\end{align*}&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 \( log(p_i) \)에 대한 수식으로 정리하고 양변에 exp를 취해주면 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;\begin{align*} &lt;br /&gt;\log(p_i)&amp;nbsp;&amp;amp;=&amp;nbsp;x_i&amp;nbsp;-&amp;nbsp;\log\left(&amp;nbsp;\sum_{n=1}^{N}&amp;nbsp;\exp(x_n)&amp;nbsp;\right)&amp;nbsp;\\ &lt;br /&gt;p_i&amp;nbsp;&amp;amp;=&amp;nbsp;\exp\left(&amp;nbsp;x_i&amp;nbsp;-&amp;nbsp;\log&amp;nbsp;\sum_{n=1}^{N}&amp;nbsp;\exp(x_n)&amp;nbsp;\right)&amp;nbsp;\\ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;amp;=&amp;nbsp;\exp\left(&amp;nbsp;x_i&amp;nbsp;-&amp;nbsp;\mathrm{LSE}(x_1,&amp;nbsp;\dots,&amp;nbsp;x_N)&amp;nbsp;\right) &lt;br /&gt;\end{align*}&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금까지의 수식 전개는 softmax와 LSE 간의 수학적 관계를 나타낸 것이며, 해당 수식 전개는 단순한 수학적 정리일 뿐이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 trick에 대한 내용을 알아보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;LogSumExp trick&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LogSumExp trick을 위해선 overflow 방지용 &lt;b&gt;수치 안정화(numerical stability)&lt;/b&gt;가 핵심이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;\begin{align*} &lt;br /&gt;y&amp;nbsp;&amp;amp;=&amp;nbsp;\log\left(&amp;nbsp;\sum_{n=1}^{N}&amp;nbsp;\exp(x_n)&amp;nbsp;\right)&amp;nbsp;\\ &lt;br /&gt;e^y &amp;amp;= \sum_{n=1}^{N} \exp(x_n) \\&lt;br /&gt;e^y &amp;amp;= \sum_{n=1}^{N} \exp(x_n - c + c), \quad \exp(x_n - c + c) = e^{x_n-c} \cdot e^c \\ &lt;br /&gt;e^y&amp;nbsp;&amp;amp;=&amp;nbsp;e^c&amp;nbsp;\sum_{n=1}^{N}&amp;nbsp;\exp(x_n&amp;nbsp;-&amp;nbsp;c)&amp;nbsp;\\ &lt;br /&gt;y&amp;nbsp;&amp;amp;=&amp;nbsp;c&amp;nbsp;+&amp;nbsp;\log&amp;nbsp;\sum_{n=1}^{N}&amp;nbsp;\exp(x_n&amp;nbsp;-&amp;nbsp;c) &lt;br /&gt;\end{align*}&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 전개하면 결과적으로 마지막에 c가 나오게 된다. 여기서 c는 현재 들어온 입력 벡터의 최댓값이 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ c = \max\{x_1, \dots, x_N\} $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제로 구현하여 PyTorch에서 제공해 주는 logsumexp 메소드의 결과와 비교해 보자.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1750668346163&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import numpy as np
import torch

def logsumexp(x):
    c = max(x)
    return c + np.log(np.sum(np.exp(x - c)))

x = np.array([1000, 1000, 1000])
print(logsumexp(x))
# 1001.0986122886682
print(torch.logsumexp(torch.tensor(x), dim=0).item())
# 1001.0986328125

# softmax 연산
print(np.exp(x - logsumexp(x)))
# [0.33333333 0.33333333 0.33333333]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;결과에선&amp;nbsp;소수점&amp;nbsp;이하에서&amp;nbsp;미세한&amp;nbsp;차이가&amp;nbsp;발생하는&amp;nbsp;것을&amp;nbsp;확인할&amp;nbsp;수&amp;nbsp;있다.&amp;nbsp;이는&amp;nbsp;NumPy는&amp;nbsp;기본적으로&amp;nbsp;float64(배정밀도)를&amp;nbsp;사용하고&amp;nbsp;PyTorch는&amp;nbsp;기본적으로&amp;nbsp;float32(단정밀도)를&amp;nbsp;사용하기&amp;nbsp;때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;참고 자료&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://gregorygundersen.com/blog/2020/02/09/log-sum-exp/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://gregorygundersen.com/blog/2020/02/09/log-sum-exp/&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://blog.naver.com/pkk1113/221327027304&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://blog.naver.com/pkk1113/221327027304&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://heygeronimo.tistory.com/88&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://heygeronimo.tistory.com/88&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>AI/Data Analysis</category>
      <category>logsumexp</category>
      <category>LSE</category>
      <category>softmax</category>
      <author>AlienCoder</author>
      <guid isPermaLink="true">https://aliencoder.tistory.com/234</guid>
      <comments>https://aliencoder.tistory.com/234#entry234comment</comments>
      <pubDate>Mon, 23 Jun 2025 17:51:02 +0900</pubDate>
    </item>
    <item>
      <title>임펄스 입력/응답(Impulse Input/Impulse Response)과 Convolution</title>
      <link>https://aliencoder.tistory.com/233</link>
      <description>&lt;script src=&quot;https://polyfill.io/v3/polyfill.min.js?features=es6&quot;&gt;&lt;/script&gt;
&lt;script src=&quot;https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js&quot;&gt;&lt;/script&gt;
&lt;p data-end=&quot;369&quot; data-start=&quot;187&quot; data-ke-size=&quot;size16&quot;&gt;신호처리에서 LTI 시스템과 같은 부분에 대해 공부하다 보면 &lt;b&gt;Impulse Input(임펄스 입력)&lt;/b&gt;과 &lt;b&gt;Impulse Response(임펄스 응답)&lt;/b&gt;에 대해 자주 접하게 된다. 더 나아가 이 개념과 함께&amp;nbsp;&lt;b&gt;Convolution(컨볼루션)&lt;/b&gt;에 대해서도 함께 배우게 된다. 여기서 Convolution은 우리가 알고 있는 그 Convolution Neural Network(CNN)의 Convolution이다. 다만, CNN에서 사용하는 Convolution 연산은 수학적으로 엄밀한 의미의 Convolution이 아니라, 실제로는 Cross-Correlation이긴 하지만 어쨌든 단어 자체는 익숙할 것이다.&lt;/p&gt;
&lt;p data-end=&quot;369&quot; data-start=&quot;187&quot; data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;이 글에서는 &lt;b&gt;임펄스 입력과 출력&lt;/b&gt;이 무엇인지, 시스템이 그것에 어떻게 반응하는지를 설명하고, 이와 연결된 &lt;b&gt;컨볼루션 연산의 의미&lt;/b&gt;를 정리해보려 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Impulse Input(임펄스 입력)&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Impulse Input(임펄스 입력)&lt;/b&gt; 또는 &lt;b&gt;단위 임펄스 함수(\(&amp;nbsp;\delta(t)&amp;nbsp;\),&amp;nbsp;디렉&amp;nbsp;델타&amp;nbsp;함수)&lt;/b&gt;는 모든 시간에서 0이고 단 한 순간만 1인 입력 신호이다. 이는 아주 짧은 순간(t &amp;rarr; 0)에 시스템에 가해지는 그 전체 크기가 1로 정의되는 단위 입력을 의미한다. 망치로 어떤 물체를 한 번 내려칠 때 가해지는 충격량을 예로 들 수 있다. 모든 시스템이 이 자극에 어떻게 반응하는지를 보면, 그 시스템의 본질적인 특성을 알 수 있다. 그림으로 표현하면 다음과 같이 지속 시간이 0이고, 그 크기는 무한대인 화살표로 표현할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;467&quot; data-origin-height=&quot;302&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bOyUvu/btsNYVYD194/CvVIGcRowCJZrsfPKTWQ3K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bOyUvu/btsNYVYD194/CvVIGcRowCJZrsfPKTWQ3K/img.png&quot; data-alt=&quot;Fig 1. Impulse Input&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bOyUvu/btsNYVYD194/CvVIGcRowCJZrsfPKTWQ3K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbOyUvu%2FbtsNYVYD194%2FCvVIGcRowCJZrsfPKTWQ3K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;388&quot; height=&quot;251&quot; data-origin-width=&quot;467&quot; data-origin-height=&quot;302&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Fig 1. Impulse Input&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;연속 시간의 경우: \( u(t) = \delta(t), \quad \int_{-\infty}^{\infty} \delta(t) \, dt = 1 \)&lt;br /&gt;\begin{cases} &lt;br /&gt;\infty,&amp;nbsp;&amp;amp;&amp;nbsp;t&amp;nbsp;=&amp;nbsp;0&amp;nbsp;\\ &lt;br /&gt;0,&amp;nbsp;&amp;amp;&amp;nbsp;t&amp;nbsp;\ne&amp;nbsp;0 &lt;br /&gt;\end{cases}&lt;/li&gt;
&lt;li&gt;이산 시간의 경우: \( \delta[n] \)&lt;br /&gt;\begin{cases} &lt;br /&gt;1,&amp;nbsp;&amp;amp;&amp;nbsp;n&amp;nbsp;=&amp;nbsp;0&amp;nbsp;\\ &lt;br /&gt;0,&amp;nbsp;&amp;amp;&amp;nbsp;n&amp;nbsp;\ne&amp;nbsp;0 &lt;br /&gt;\end{cases}&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다시 Fig 1 예시로 돌아와 연속 시간의 \( \delta(t) \)에 대해 생각해보면, 폭(0)과 높이(무한대)의 곱이 1이 되어야 한다는 뜻이 된다. 이는 이론적인 함수이므로 직관으론 잘 와닿지 않지만 \( \delta(t) \)를 실수 구간에 대해 적분하면 1의 된다고 정의하여 사용하게 된다고 인지하고 있으면 된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-end=&quot;650&quot; data-start=&quot;636&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Impulse Response(임펄스 응답)&lt;/b&gt;&lt;/h2&gt;
&lt;p data-end=&quot;713&quot; data-start=&quot;652&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Impulse Response(임펄스 응답)&lt;/b&gt;은 &lt;b&gt;System(시스템)&lt;/b&gt;에 &lt;b&gt;단위 임펄스 입력(\( \delta(t) \), 디렉 델타 함수)&lt;/b&gt;을 넣었을 때의 &lt;b&gt;출력&lt;/b&gt;을 의미하며 해당 시스템이 어떤 입력에 대해 어떻게 반응할지를 전부 포함하고 있는 정보이다. 특히, &lt;b&gt;LTI (선형 시불변) 시스템&lt;/b&gt;에서는 임펄스 응답이 &lt;b&gt;시스템의 모든 특성을 완전히 대변&lt;/b&gt;한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;713&quot; data-start=&quot;652&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;696&quot; data-origin-height=&quot;340&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/01Sq7/btsN03fZWQp/cFq1VFDrx1v1JHLfe6gYKK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/01Sq7/btsN03fZWQp/cFq1VFDrx1v1JHLfe6gYKK/img.png&quot; data-alt=&quot;Fig 2. LTI System&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/01Sq7/btsN03fZWQp/cFq1VFDrx1v1JHLfe6gYKK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F01Sq7%2FbtsN03fZWQp%2FcFq1VFDrx1v1JHLfe6gYKK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;581&quot; height=&quot;284&quot; data-origin-width=&quot;696&quot; data-origin-height=&quot;340&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Fig 2. LTI System&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;780&quot; data-start=&quot;715&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;748&quot; data-start=&quot;715&quot;&gt;연속 시간 시스템의 임펄스 응답: \( h(t) = \mathcal{H} \{ \delta(t) \} \)&lt;/li&gt;
&lt;li data-end=&quot;780&quot; data-start=&quot;749&quot;&gt;이산 시간 시스템의 임펄스 응답: \(&amp;nbsp;h[n]&amp;nbsp;=&amp;nbsp;\mathcal{H}&amp;nbsp;\{&amp;nbsp;\delta[n]&amp;nbsp;\}&amp;nbsp;\)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;b&gt;LTI(Linear Time-Invariant) System&lt;/b&gt;과 같은 선형시불변 성질을 가진 시스템에서 이산 시간에서의 &lt;b&gt;단위 임펄스 입력&lt;/b&gt;인 (\( \delta[n] \))를 입력하면 시스템은 시간에 따라 변하지 않고 항상 동일한 형태의 응답(response)인 (\( h[n] \))을 반환한다. 만약 (\( \delta[n-3] \))이 입력되면&amp;nbsp;&amp;nbsp;(\( h[n-3] \)이 반환될 것이다. 즉, 시간축에서 임펄스 입력이 언제 들어오든 응답의 모양은 동일하고, 단지 시간적으로 이동된 형태로 나타난다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 망치로 특정 물체를 일정한 힘으로 내려쳤을 때 처음에는 강한 진동이 발생하지만 시간이 지날수록 진동은 줄어들 것이다. 이러한 처음 내려친 순간부터 시간에 따라 진동이 시간에 따라 줄어드는 현상(또는 감쇠)을 임펄스 응답으로 표현할 수 있다. 물론 동일한 망치, 내려치는 힘, 타격이 가해지는 물체가 모두 동일한 상태라는 가정 아래이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;483&quot; data-origin-height=&quot;346&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DpMy1/btsN0vRVHhH/29krgKQ5UMhPHjwcovzCDK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DpMy1/btsN0vRVHhH/29krgKQ5UMhPHjwcovzCDK/img.png&quot; data-alt=&quot;Fig 3. Impulse Response&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DpMy1/btsN0vRVHhH/29krgKQ5UMhPHjwcovzCDK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDpMy1%2FbtsN0vRVHhH%2F29krgKQ5UMhPHjwcovzCDK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;372&quot; height=&quot;266&quot; data-origin-width=&quot;483&quot; data-origin-height=&quot;346&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Fig 3. Impulse Response&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예시는 왜 임펄스 응답을 단순히 수학적으로 자주 사용하는 \( y \)로 표현하지 않고 굳이 Impulse Response라 칭하는지에 대한 설명이기도 하다. &lt;b&gt;입력 신호인 \( x(t) \) 또는 \( x[t] \)&lt;/b&gt;는 &lt;b&gt;이상적인 임펄스(Ideal Impulse)&lt;/b&gt;로 주어져야 하기 때문이다. 위 예시에선 동일한 망치와 내려치는 힘이 &lt;b&gt;이상적인 임펄스&lt;/b&gt;에 해당하게 된다. 이 이상적인 임펄스 입력을 이용해 &lt;b&gt;모든&amp;nbsp;입력&lt;/b&gt;을&amp;nbsp;&lt;b&gt;임펄스들의&amp;nbsp;합으로&amp;nbsp;표현&lt;/b&gt;할&amp;nbsp;수&amp;nbsp;있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 &lt;b&gt;실제 출력인 \( y \)&lt;/b&gt;는 다른 노이즈에 대한 영향을 받을 수도 있다는 가정으로 현실적인 다양한 입력 신호와 외부 요인(노이즈 등)에 영향을 받아 나타나는 결과를 나타낸다. 사실 이상적인 임펄스 입력은 실제로 발생하기는 어렵지만, &lt;b&gt;시불변성&lt;/b&gt;은 시스템을 분석하고 예측하기 위한 핵심 전제이다. 시불변성은 복잡한 입력에 대해 시스템의 출력을 효율적으로 계산하고 모델링을 가능하게 해주며, 이 덕분에 시스템 모델링, 제어, 신호처리 설계가 단순해진다. 실제로 필터&amp;nbsp;설계,&amp;nbsp;오디오&amp;nbsp;이펙트,&amp;nbsp;음성&amp;nbsp;인식&amp;nbsp;모델&amp;nbsp;등은&amp;nbsp;대부분&amp;nbsp;LTI&amp;nbsp;시스템&amp;nbsp;가정&amp;nbsp;하에&amp;nbsp;동작한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 정리하자면, &lt;b&gt;이상적인 단위 임펄스 입력&lt;/b&gt;이 들어간 &lt;b&gt;LTI 시스템의 출력&lt;/b&gt;은 &lt;b&gt;어떤 입력 신호에 대해서도, 그 입력을 임펄스로 분해하고 각 임펄스에 대한 응답을 더하는 방식으로 계산&lt;/b&gt;할 수 있다. 이 연산이 바로 &lt;b&gt;Convolution(컨볼루션)&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Convolution(컨볼루션)&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;입력 신호인 \( x(t) \)는 여러 시간에 분포된 임펄스들의 조합으로 생각할 수 있다. 각 임펄스에 대한 응답이 \( h(t-\tau) \)로 일정한 모양으로 이동된다면, &lt;b&gt;전체 출력&lt;/b&gt;은 &lt;b&gt;이들의 합으로 쉽게 예측&lt;/b&gt;되는데 바로 이 과정이 &lt;b&gt;Convolution(컨볼루션)&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시간의 흐름에 따라 지속적으로 시스템에 임펄스 입력이 들어오는 상황을 &lt;b&gt;Impulse Train&lt;/b&gt;이라 하며, 다음 Fig 4의 (A)와 같이 \( t = 0,1,2,3 \)의 일정 시간마다 임펄스 입력이 있다고 가정하자. 각 입력에 대한 시스템 응답은 (B)와 같이 나타나게 되며 LTI 시스템을 이용한다면 이 각각의 영향들은 모두 &lt;b&gt;중첩(Superposition)&lt;/b&gt;되어 (C)와 유사한 형태로 &lt;b&gt;전체 시스템 응답&lt;/b&gt;을 도출하게 된다. 이 전체 시스템의 응답을 계산하는 방법이 Convolution이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;902&quot; data-origin-height=&quot;260&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/NFOTv/btsNZTeZwa7/lVrg2Zw5p9gwmyK51s1ps1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/NFOTv/btsNZTeZwa7/lVrg2Zw5p9gwmyK51s1ps1/img.png&quot; data-alt=&quot;Fig 4. Convolution&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/NFOTv/btsNZTeZwa7/lVrg2Zw5p9gwmyK51s1ps1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNFOTv%2FbtsNZTeZwa7%2FlVrg2Zw5p9gwmyK51s1ps1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;625&quot; height=&quot;180&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;902&quot; data-origin-height=&quot;260&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Fig 4. Convolution&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;연속 시스템에서 컨볼루션&lt;br /&gt;$$ y(t) = x(t) * h(t) = \int_{-\infty}^{\infty} x(\tau) \cdot h(t - \tau) \, d\tau $$&lt;/li&gt;
&lt;li&gt;이산 시스템에서 컨볼루션&lt;br /&gt;$$ y[n] = x[n] * h[n] = \sum_{k=-\infty}^{\infty} x[k] \cdot h[n - k] $$&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;연속 시스템에서 컨볼루션이라 생각하고 계산해보면 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;877&quot; data-origin-height=&quot;529&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bYzumE/btsNZHThb13/Cf1bUhbZIivOzhv5OLyEJk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bYzumE/btsNZHThb13/Cf1bUhbZIivOzhv5OLyEJk/img.png&quot; data-alt=&quot;Fig 5. Convolution 계산&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bYzumE/btsNZHThb13/Cf1bUhbZIivOzhv5OLyEJk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbYzumE%2FbtsNZHThb13%2FCf1bUhbZIivOzhv5OLyEJk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;592&quot; height=&quot;357&quot; data-origin-width=&quot;877&quot; data-origin-height=&quot;529&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Fig 5. Convolution 계산&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;[부록] CNN에서의 Convolution은 왜 Convolution이 아닌가?&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금까지 살펴본 Convolution은 우리가 아닌 CNN의 Convoultion과는 조금 다르다. 이제 조금 더 심화해서 Convolution을 살펴보자. 우선 이산 시스템에서의 Convolution의 수식을 다시 살펴보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$&amp;nbsp;y[n]&amp;nbsp;=&amp;nbsp;x[n]&amp;nbsp;*&amp;nbsp;h[n]&amp;nbsp;=&amp;nbsp;\sum_{k=-\infty}^{\infty}&amp;nbsp;x[k]&amp;nbsp;\cdot&amp;nbsp;h[n&amp;nbsp;-&amp;nbsp;k]&amp;nbsp;$$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 \( h[n-k] \)는 임펄스 응답 \( h[k] \)를 시간 반전(뒤집기) 후, \( n \)만큼 이동(Shift)한 형태이다. 다시 말해 다음과 같이 정의할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ h[n-k] = h[-(k-n)] $$&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;시간축 반전: \( h[k] \) &amp;rarr; \( h[-k] \)&lt;br /&gt;예: [1, 2, 3](\( k = 0, 1, 2 \)) [3, 2, 1] &amp;rarr; [3, 2, 1](\( k = -2, -1, 0 \))&lt;/li&gt;
&lt;li&gt;시간 이동: \( h[-k] \)&amp;nbsp; &amp;rarr; \( h[n-k] \)&lt;br /&gt;예: 반전된&amp;nbsp;신호를&amp;nbsp;시간축에서&amp;nbsp;n만큼&amp;nbsp;이동&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;468&quot; data-origin-height=&quot;135&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qzdQl/btsN1cRTyuP/Zkcd5wfyZdMEdE4WD1xS80/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qzdQl/btsN1cRTyuP/Zkcd5wfyZdMEdE4WD1xS80/img.gif&quot; data-alt=&quot;Fig 6. Convolution Visualization&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qzdQl/btsN1cRTyuP/Zkcd5wfyZdMEdE4WD1xS80/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/qzdQl/btsN1cRTyuP/Zkcd5wfyZdMEdE4WD1xS80/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;572&quot; height=&quot;165&quot; data-origin-width=&quot;468&quot; data-origin-height=&quot;135&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Fig 6. Convolution Visualization&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Convolution을 뒤집는다&lt;/b&gt;는 말은 &lt;b&gt;컨볼루션 연산에서 임펄스 응답 함수(또는 입력 신호) 중 하나를 시간 반전(reverse)시키는 과정&lt;/b&gt;을 의미하고 이동은 말 그래도 시간이 지나는 것을 의미한다. Reverse + Shift는 &lt;b&gt;컨볼루션 연산의 정의상 필수적인 절차&lt;/b&gt;이다. Convolution의 핵심은 컨볼루션은 시스템에 입력 신호가 &amp;ldquo;쌓여서 누적되는 방식&amp;rdquo;을 보는 것이기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면 &lt;b&gt;CNN에서의 연산&lt;/b&gt;은 Convolution처럼 뒤집지 않고 그대로 커널을 적용한다. 이는 &lt;b&gt;Cross-Correlation&lt;/b&gt;에 해당한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ (f \star g)[n] = \sum_{m=-\infty}^{\infty} f[m] \cdot g[n + m] $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 왜 CNN에서도 Convolution 연산이라고 표기하고 Cross-Correlation을 사용하였을까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 &lt;span style=&quot;letter-spacing: 0px;&quot;&gt;실제 이미지 처리나 딥러닝에서는 커널을 뒤집는 것이 직관적이지 않고 불편하며, 학습된 가중치를 뒤집는 것은 의미가 없다. 또한 &lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;구현과 연산 효율성을 위해 Cross-Correlation을 사용하지만, 관례적으로 &lt;b&gt;Convolution Layer&lt;/b&gt;라고 부른다고 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Convolution과 Corss-Correlation을 시각화하면 다음 영상과 같다.&lt;/p&gt;
&lt;figure data-ke-type=&quot;video&quot; data-ke-style=&quot;alignCenter&quot; data-video-host=&quot;youtube&quot; data-video-url=&quot;https://www.youtube.com/watch?v=Ma0YONjMZLI&quot; data-video-thumbnail=&quot;https://scrap.kakaocdn.net/dn/bVMjpo/hyYRnbSGzl/mw3UyGTA4trPLJfSYjDDm1/img.jpg?width=480&amp;amp;height=360&amp;amp;face=0_0_480_360,https://scrap.kakaocdn.net/dn/DFDZ9/hyYRyxElJz/jMdCkA6DKfIsdKYizkiz11/img.jpg?width=480&amp;amp;height=360&amp;amp;face=0_0_480_360&quot; data-video-width=&quot;480&quot; data-video-height=&quot;360&quot; data-video-origin-width=&quot;480&quot; data-video-origin-height=&quot;360&quot; data-ke-mobilestyle=&quot;widthContent&quot; data-video-title=&quot;Visualization of Cross Correlation and Convolution with Matlab&quot; data-original-url=&quot;&quot;&gt;&lt;iframe src=&quot;https://www.youtube.com/embed/Ma0YONjMZLI&quot; width=&quot;480&quot; height=&quot;360&quot; frameborder=&quot;&quot; allowfullscreen=&quot;true&quot;&gt;&lt;/iframe&gt;
&lt;figcaption&gt;Fig 7. Convolution and Cross-Corrlelation&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추가적으로 2D Cross-Correlation(CNN 이미지 처리)는 다음을 이용하여 계산한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ (f \star g)[n] = \sum_{m=-\infty}^{\infty} f[m] \cdot g[n + m] $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;관심 있다면 CNN을 기반으로 한 LeNet-5 아키텍처를 사용하여 CNN을 실제 문서 인식 시스템에 적용한 &lt;span style=&quot;letter-spacing: 0px;&quot;&gt;얀 르쿤의 &lt;/span&gt;&lt;a href=&quot;http://vision.stanford.edu/cs598_spring07/papers/Lecun98.pdf&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;b&gt;Gradient-based learning applied to document recognition(1998)&lt;/b&gt;&lt;/a&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt; 논문을 읽어봐도 좋을 것 같다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 1989년에 개발된 CNN 기반 아키텍처인 &lt;b&gt;LeNet 1&lt;/b&gt;의 시연 영상이다.&lt;/p&gt;
&lt;figure data-ke-type=&quot;video&quot; data-ke-style=&quot;alignCenter&quot; data-video-host=&quot;youtube&quot; data-video-url=&quot;https://www.youtube.com/watch?v=FwFduRA_L6Q&quot; data-video-thumbnail=&quot;https://scrap.kakaocdn.net/dn/huabO/hyYU60zQEc/z6ZJq6CaQEw3IKovKaByC0/img.jpg?width=640&amp;amp;height=480&amp;amp;face=0_0_640_480&quot; data-video-width=&quot;640&quot; data-video-height=&quot;480&quot; data-video-origin-width=&quot;640&quot; data-video-origin-height=&quot;480&quot; data-ke-mobilestyle=&quot;widthContent&quot; data-video-title=&quot;Convolutional Network Demo from 1989&quot; data-original-url=&quot;&quot;&gt;&lt;iframe src=&quot;https://www.youtube.com/embed/FwFduRA_L6Q&quot; width=&quot;640&quot; height=&quot;480&quot; frameborder=&quot;&quot; allowfullscreen=&quot;true&quot;&gt;&lt;/iframe&gt;
&lt;figcaption&gt;Fig 8. LeNet 1&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;관련 포스팅&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://aliencoder.tistory.com/221&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;2025.04.11 - [Data Science/SR &amp;amp; VC] - LTI(Linear Time-Invariant) Systems&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;참고 자료&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://dsaint31.tistory.com/611&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://dsaint31.tistory.com/611&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://available-space.tistory.com/119&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://available-space.tistory.com/119&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://m.blog.naver.com/waterforall/223044784511&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://m.blog.naver.com/waterforall/223044784511&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://it.wikipedia.org/wiki/Convoluzione&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://it.wikipedia.org/wiki/Convoluzione&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=Ma0YONjMZLI&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.youtube.com/watch?v=Ma0YONjMZLI&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=FwFduRA_L6Q&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.youtube.com/watch?v=FwFduRA_L6Q&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>AI/SR &amp;amp; VC</category>
      <category>CNN</category>
      <category>convolution</category>
      <category>convolution neural network</category>
      <category>cross-correlation</category>
      <category>impulse input</category>
      <category>impulse response</category>
      <author>AlienCoder</author>
      <guid isPermaLink="true">https://aliencoder.tistory.com/233</guid>
      <comments>https://aliencoder.tistory.com/233#entry233comment</comments>
      <pubDate>Sat, 17 May 2025 17:20:45 +0900</pubDate>
    </item>
    <item>
      <title>Spectrum(스펙트럼)과 Cepstrum(켑스트럼)</title>
      <link>https://aliencoder.tistory.com/231</link>
      <description>&lt;script src=&quot;https://polyfill.io/v3/polyfill.min.js?features=es6&quot;&gt;&lt;/script&gt;
&lt;script src=&quot;https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js&quot;&gt;&lt;/script&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;음성 인식 분야를 공부하며 &lt;b&gt;Spectrum(스펙트럼)&lt;/b&gt;과 &lt;b&gt;Cepstrum(켑스트럼)&lt;/b&gt;이라는 개념을 자주 접하게 된다. 두 개념은 모두 시간 영역(time domain)의 신호를 주파수 기반으로 분석하는 도구이지만, 처리 방식과 해석의 초점이 다르다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Spectrum&lt;/b&gt;: 신호의 주파수 구성 성분을 보는 방법&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Cepstrum&lt;/b&gt;: 주파수 구조 내부의 반복 패턴을 분석하는 방법&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1059&quot; data-origin-height=&quot;746&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dfk4x2/btsNXW9RPOM/kudr7dj6Eqk1FASnkb3E5K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dfk4x2/btsNXW9RPOM/kudr7dj6Eqk1FASnkb3E5K/img.png&quot; data-alt=&quot;Fig 1. Overlay of spectrum, true envelope, and cepstral envelope&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dfk4x2/btsNXW9RPOM/kudr7dj6Eqk1FASnkb3E5K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdfk4x2%2FbtsNXW9RPOM%2Fkudr7dj6Eqk1FASnkb3E5K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;597&quot; height=&quot;421&quot; data-origin-width=&quot;1059&quot; data-origin-height=&quot;746&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Fig 1. Overlay of spectrum, true envelope, and cepstral envelope&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;이 글에서는 두 개념을 쉽게 비교하고, 음성 신호 처리에서 어떤 역할을 수행하는지 설명하려 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Spectrum(스펙트럼)&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Spectrum&lt;/b&gt;은 신호의 주파수 성분을 나타낸 것이다. 즉, 어떤 신호가 어떤 주파수로 구성되어 있는지를 보여주는 것이다. 시간 영역의 신호 \( x(t) \) 또는 이산 신호 \( x[n] \)에 대해, 푸리에 변환(Fourier Transform)을 수행하면 다음과 같이 주파수 영역 신호 \( X(f) \)를 얻게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ X(f) = \int_{-\infty}^{\infty} x(t) e^{-j 2\pi f t} dt $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;혹은 이산 푸리에 변환(DFT)을 사용할 경우는 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ X[k] = \sum_{n=0}^{N-1} x[n] e^{-j \frac{2\pi}{N}kn} $$&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;\( |X(f)| \): 진폭 스펙트럼(얼마나 강한 주파수인지)&lt;/li&gt;
&lt;li&gt;\( \text{arg(X(f))} \): 위상 스펙트럼(어느 시점에 도달하는지)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스펙트럼은 일반적으로 음성의 주된 구성 성분(포먼트, 기본 주파수 등)을 분석할 때 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Cepstrum(켑스트럼)&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Cepstrum&lt;/b&gt;은 Spectrum의 철자를 일부러 거꾸로 만든 단어로, 정의 자체도 &lt;b&gt;스펙트럼의 로그를 다시 푸리에 변환한 것&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;945&quot; data-origin-height=&quot;142&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/d6m2Ec/btsNWfixTla/DaDK9YQj03L44c0EPpvT9K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/d6m2Ec/btsNWfixTla/DaDK9YQj03L44c0EPpvT9K/img.png&quot; data-alt=&quot;Fig 2. Basic steps in cepstral analysis.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/d6m2Ec/btsNWfixTla/DaDK9YQj03L44c0EPpvT9K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fd6m2Ec%2FbtsNWfixTla%2FDaDK9YQj03L44c0EPpvT9K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;673&quot; height=&quot;101&quot; data-origin-width=&quot;945&quot; data-origin-height=&quot;142&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Fig 2. Basic steps in cepstral analysis.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-end=&quot;198&quot; data-start=&quot;89&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1단계 (STEP 1): &lt;b&gt;시간 영역 &amp;rarr; 주파수 영역 (FFT)&lt;/b&gt; &lt;/b&gt;&lt;/h4&gt;
&lt;p data-end=&quot;198&quot; data-start=&quot;89&quot; data-ke-size=&quot;size16&quot;&gt;$$ X[k] = \sum_{n=0}^{N-1} x[n] \cdot e^{-j \frac{2\pi}{N}kn} = \text{DFT}(x[n]) $$&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;198&quot; data-start=&quot;89&quot;&gt;먼저 시간 영역의 신호에 &lt;b&gt;고속 푸리에 변환(FFT: Fast Fourier Transform)&lt;/b&gt;을 적용하여, 시간 데이터를 주파수 데이터로 변환한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-end=&quot;457&quot; data-start=&quot;205&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2단계 (STEP 2): &lt;b&gt;진폭 &amp;rarr; 로그 스펙트럼 변환&lt;/b&gt; &lt;/b&gt;&lt;/h4&gt;
&lt;p data-end=&quot;457&quot; data-start=&quot;205&quot; data-ke-size=&quot;size16&quot;&gt;$$ \log |X[k]|, \quad \text{위상 성분 제거} $$&lt;/p&gt;
&lt;p data-end=&quot;457&quot; data-start=&quot;205&quot; data-ke-size=&quot;size16&quot;&gt;$$ \log_{10} |X[k]| $$&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;457&quot; data-start=&quot;205&quot;&gt;주파수 영역으로 변환된 후, &lt;b&gt;FFT 결과에서 위상(phase) 성분을 제거&lt;/b&gt;하고, 진폭(amplitude) 정보에 로그(\( log_{10} \))를 취한다.&lt;/li&gt;
&lt;li data-end=&quot;457&quot; data-start=&quot;205&quot;&gt;이 과정은 신호의 다이내믹 레인지(dynamic range)를 줄여주며, 노이즈와 신호 간의 진폭 차이로 인해 가려질 수 있는 조화 성분(harmonics)을 더 잘 드러나게 해준다.&lt;/li&gt;
&lt;li data-end=&quot;457&quot; data-start=&quot;205&quot;&gt;즉, 로그 압축을 통해 미약한 주기 성분도 부각된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-end=&quot;642&quot; data-start=&quot;464&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;3단계 (STEP 3): &lt;b&gt;주파수 영역 &amp;rarr; 퀘프런시 영역 (IFFT)&lt;/b&gt; &lt;/b&gt;&lt;/h4&gt;
&lt;p data-end=&quot;642&quot; data-start=&quot;464&quot; data-ke-size=&quot;size16&quot;&gt;$$ c[n] = \sum_{k=0}^{N-1} \log |X[k]| \cdot e^{j \frac{2\pi}{N}kn} = \text{IDFT} \left( \log |X[k]| \right) $$&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;642&quot; data-start=&quot;464&quot;&gt;이제 로그를 취한 주파수 신호에 역 푸리에 변환(Inverse Fourier Transform)을 적용하여 이를 &lt;b&gt;퀘프런시(quefrency)&lt;/b&gt; 영역으로 변환한다.&lt;/li&gt;
&lt;li data-end=&quot;642&quot; data-start=&quot;464&quot;&gt;수학적으로는 &lt;b&gt;푸리에 변환의 역과 동일한 연산&lt;/b&gt;이지만, 방향이 반대이므로 위상 성분이 역으로 회전한다.&lt;/li&gt;
&lt;li data-end=&quot;858&quot; data-start=&quot;644&quot;&gt;보통 IFFT는 주파수 영역 데이터를 다시 시간 영역으로 복원하지만, 여기서는 진폭만 유지하고 로그를 적용한 후 IFFT를 수행하기 때문에 시간 영역이 아닌 퀘프런시 영역으로 이동하게 된다.&lt;/li&gt;
&lt;li data-end=&quot;858&quot; data-start=&quot;644&quot;&gt;즉, &lt;b&gt;푸리에 변환의 출력에 다시 역 푸리에 변환을 수행&lt;/b&gt;하는 구조이기 때문에 켑스트럼(cepstrum)은 종종 스펙트럼의 스펙트럼이라고 불린다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;퀘프런시(Quefrency)&lt;/b&gt;란 켑스트럼(Cepstrum)에서 정의되는 가상의 단위로, 시간(Time)과 주파수(Frequency)라는 말을 뒤집어 만든 단어이다. 예를 들어 신호에 주기적인 패턴(예: 사람의 음성에서 피치)이 있다면 다음과 같이 나타난다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;스펙트럼에선 배음 구조(harmonics)로 나타남&lt;/li&gt;
&lt;li&gt;켑스트럼에선 이 구조가 하나의 봉우리(peak)로 나타남&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 봉우리가 위치한 점이 Quefrency 값이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다시 돌아와 위 3단계의 과정은 Discrete Cepstrum에 대한 계산 과정이며 최종적으로 도출되는 수식은 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ c[n] = \text{IDFT} \left( \log \left| \text{DFT}(x[n]) \right| \right) $$&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;\( \mathcal{F} \)&lt;/b&gt;: 푸리에 변환(Fourier&amp;nbsp;Transform)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;\( \mathcal{F}^{-1} \)&lt;/b&gt;: 역 푸리에 변환(Inverse&amp;nbsp;Fourier&amp;nbsp;Transform)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추가적으로 아날로그 정의는 다음과 같다. &lt;br /&gt;$$ \text{Cepstrum} = \mathcal{F}^{-1} \left( \log |\mathcal{F}\{x(t)\}| \right) $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;음성 처리에서의 Cepstrum은 다음과 같이 활용할 수 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;2150&quot; data-start=&quot;2022&quot;&gt;&lt;b&gt;Source-Filter 모델 분석&lt;/b&gt;&lt;br /&gt;- 음성을 음원(Source) + 공명 필터(Filter)로 분해할 수 있다.&lt;br /&gt;- 켑스트럼에서 저역수는 필터(포먼트), 고역수는 음원(피치 등)을 나타낸다.&lt;/li&gt;
&lt;li data-end=&quot;2290&quot; data-start=&quot;2152&quot;&gt;&lt;b&gt;음성 인식 전처리 (MFCC)&lt;/b&gt;&lt;br /&gt;- MFCC(Mel-Frequency Cepstral Coefficient)는 &lt;b&gt;멜 스케일로 스펙트럼을 변형한 후 켑스트럼 계수를 추출&lt;/b&gt;한다.&lt;br /&gt;- 음향 모델의 입력으로 널리 사용된다.&lt;/li&gt;
&lt;li data-end=&quot;2367&quot; data-start=&quot;2292&quot;&gt;&lt;b&gt;피치 추정&lt;/b&gt;&lt;br /&gt;- 반복적인 구조(예: 음성의 기본 주기)를 quefrency 영역에서 분석하면 피치 추정이 가능하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;관련 포스팅&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://aliencoder.tistory.com/217&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;2025.04.08 - [Data Science/SR &amp;amp; VC] - 푸리에 급수(Fourier Series)와 푸리에 계수(Fourier Coefficient)&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://aliencoder.tistory.com/218&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;2025.04.08 - [Data Science/SR &amp;amp; VC] - 오일러 공식(Euler's Formula)과 푸리에 변환(Fourier Transform)&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://aliencoder.tistory.com/219&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;2025.04.08 - [Data Science/SR &amp;amp; VC] - Discrete-Time Fourier Transform(DTFT)과 Discrete Fourier Transform(DFT)&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;참고 자료&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://ccrma.stanford.edu/~jos/sasp/Spectral_Envelope_Cepstral_Windowing.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://ccrma.stanford.edu/~jos/sasp/Spectral_Envelope_Cepstral_Windowing.html&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://community.sw.siemens.com/s/article/Cepstrum-Analysis&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://community.sw.siemens.com/s/article/Cepstrum-Analysis&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://blog.naver.com/yeominrock/167235723&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://blog.naver.com/yeominrock/167235723&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>AI/SR &amp;amp; VC</category>
      <category>cepstrum</category>
      <category>quefrency</category>
      <category>spectrum</category>
      <author>AlienCoder</author>
      <guid isPermaLink="true">https://aliencoder.tistory.com/231</guid>
      <comments>https://aliencoder.tistory.com/231#entry231comment</comments>
      <pubDate>Sat, 17 May 2025 08:13:18 +0900</pubDate>
    </item>
    <item>
      <title>[Spring Boot] Transaction과 @Transactional</title>
      <link>https://aliencoder.tistory.com/232</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;640&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/beziAU/btsLJCUjM9q/1j7sNwamcIoIsAjwJrD1Vk/tfile.dat&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/beziAU/btsLJCUjM9q/1j7sNwamcIoIsAjwJrD1Vk/tfile.dat&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/beziAU/btsLJCUjM9q/1j7sNwamcIoIsAjwJrD1Vk/tfile.dat&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbeziAU%2FbtsLJCUjM9q%2F1j7sNwamcIoIsAjwJrD1Vk%2Ftfile.dat&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;732&quot; height=&quot;366&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;640&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring Boot를 사용하면 Transactional이라는 어노테이션을 자주 접하게 된다. &lt;b&gt;@Transactional&lt;/b&gt;은 스프링 프레임워크에서 트랜잭션을 선언적으로 처리하기 위한 어노테이션이다. 이 어노테이션을 메서드 또는 클래스에 붙이면, 해당 범위 내에서 실행되는 로직은 트랜잭션으로 감싸지며, 성공하면 자동 커밋되고, 예외 발생 시 자동 롤백되는 기능을 수행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Transactional 어노테이션의 역할을 알기 전, Transaction이 무엇인지 먼저 짚고 넘어가자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Transaction(트랜잭션)&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Transaction(트랜잭션)&lt;/b&gt;은 &lt;b&gt;하나의 작업 단위(Unit&amp;nbsp;of&amp;nbsp;Work)&lt;/b&gt;를 구성하는 여러 개의 연산 집합(예: SELECT, INSERT, UPDATE, DELETE)으로, 모두 성공하거나 모두 실패해야 한다는 원칙을 가진다. 즉, &lt;b&gt;데이터베이스&lt;/b&gt;의&amp;nbsp;&lt;b&gt;상태를&amp;nbsp;변화&lt;/b&gt;시키기&amp;nbsp;위해서&amp;nbsp;수행하는&amp;nbsp;&lt;b&gt;작업의&amp;nbsp;단위&lt;/b&gt;를&amp;nbsp;의미한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 Transaction은 &lt;b&gt;ACID(원자성, 일관성, 고립성, 지속성)&lt;/b&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt; 원칙을 따르며, 데이터 정합성을 보장하기 위해 반드시 필요한 개념이다. 이 ACID&amp;nbsp;원칙이라&amp;nbsp;불리는&amp;nbsp;네&amp;nbsp;가지&amp;nbsp;특징은&amp;nbsp;데이터&amp;nbsp;무결성과&amp;nbsp;일관성을&amp;nbsp;보장하기&amp;nbsp;위한&amp;nbsp;규칙이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ACID 원칙은 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;원자성(Atomicity)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;원자성(Atomicity)&lt;/b&gt;은 트랜잭션은 &lt;b&gt;더 이상 나눌 수 없는 최소 단위&lt;/b&gt;이다. 트랜잭션&amp;nbsp;내&amp;nbsp;모든&amp;nbsp;작업은&amp;nbsp;모두&amp;nbsp;수행되거나&amp;nbsp;모두&amp;nbsp;수행되지&amp;nbsp;않아야&amp;nbsp;한다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;A 계좌에서 10만원 출금 &amp;rarr; B 계좌에 10만원 입금 &lt;br /&gt;&amp;rarr; 둘 중 하나라도 실패하면 전체 작업이 취소되어야 함&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;일관성(Consistency)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;일관성(Consistency)&lt;/b&gt;은 트랜잭션 수행 전과 후에 &lt;b&gt;데이터베이스는 항상 일관된 상태를 유지&lt;/b&gt;해야 한다. 즉,&amp;nbsp;제약조건(무결성&amp;nbsp;제약,&amp;nbsp;외래키,&amp;nbsp;도메인&amp;nbsp;조건&amp;nbsp;등)이&amp;nbsp;항상&amp;nbsp;만족되어야&amp;nbsp;한다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;은행&amp;nbsp;시스템에서&amp;nbsp;잔고의&amp;nbsp;합이&amp;nbsp;항상&amp;nbsp;일정해야&amp;nbsp;함 &lt;br /&gt;&amp;rarr; 트랜잭션 수행 후에도 제약조건이 깨지지 않아야 함&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;고립성(Isolation)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;고립성(Isolation)&lt;/b&gt;은 여러 트랜잭션이 동시에 실행되더라도 &lt;b&gt;각 트랜잭션은 독립적으로 실행&lt;/b&gt;되어야 한다. 다른 트랜잭션의 중간 결과에 영향을 받거나 보여서는 안 된다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;T1:&amp;nbsp;A&amp;nbsp;계좌&amp;nbsp;잔고&amp;nbsp;조회&amp;nbsp;&amp;rarr;&amp;nbsp;출금 &lt;br /&gt;T2:&amp;nbsp;A&amp;nbsp;계좌&amp;nbsp;잔고&amp;nbsp;수정 &lt;br /&gt;&amp;rarr;&amp;nbsp;서로&amp;nbsp;영향을&amp;nbsp;주지&amp;nbsp;않고&amp;nbsp;순차적으로&amp;nbsp;실행된&amp;nbsp;것처럼&amp;nbsp;보여야&amp;nbsp;함&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;지속성(Durability)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;지속성(Durability)&lt;/b&gt;은 트랜잭션이 성공적으로 커밋되면 그 &lt;b&gt;결과는 영구적으로 데이터베이스에 저장&lt;/b&gt;되어야 한다. 시스템&amp;nbsp;오류나&amp;nbsp;장애가&amp;nbsp;발생해도&amp;nbsp;변경된&amp;nbsp;내용은&amp;nbsp;손실되지&amp;nbsp;않는다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;T1:&amp;nbsp;주문&amp;nbsp;완료&amp;nbsp;후&amp;nbsp;DB에&amp;nbsp;저장됨 &lt;br /&gt;&amp;rarr;&amp;nbsp;서버가&amp;nbsp;다운되더라도&amp;nbsp;주문&amp;nbsp;내역은&amp;nbsp;남아&amp;nbsp;있어야&amp;nbsp;함&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;코드 예시&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1747120252653&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Transactional
public void transfer(Long fromId, Long toId, int amount) {
    Account from = accountRepository.findById(fromId).orElseThrow();
    Account to = accountRepository.findById(toId).orElseThrow();

    from.withdraw(amount);
    to.deposit(amount);

    // 위 과정 전체가 하나의 트랜잭션
    // 중간에 예외 발생 시 전체가 롤백됨
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이&amp;nbsp;예시는&amp;nbsp;Atomicity와&amp;nbsp;Consistency를&amp;nbsp;중심으로,&amp;nbsp;스프링에서&amp;nbsp;트랜잭션의&amp;nbsp;개념이&amp;nbsp;어떻게&amp;nbsp;코드에&amp;nbsp;적용되는지를&amp;nbsp;보여준다. &lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;@Transactional&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 설명했듯 Spring Framework에서 제공하는 @Transactional 어노테이션은 트랜잭션 처리를 선언적으로 제어할 수 있게 해주는 핵심 기능이다. 직접 트랜잭션을 시작하고 커밋하거나 롤백하는 로직을 작성하지 않아도, 어노테이션만으로&amp;nbsp;트랜잭션의&amp;nbsp;시작과&amp;nbsp;종료를&amp;nbsp;프레임워크가&amp;nbsp;대신&amp;nbsp;처리해 준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자주 사용되는 옵션들은 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;readOnly:&amp;nbsp;읽기&amp;nbsp;전용&amp;nbsp;트랜잭션.&amp;nbsp;Hibernate&amp;nbsp;플러시&amp;nbsp;전략&amp;nbsp;최적화&lt;/li&gt;
&lt;li&gt;rollbackFor:&amp;nbsp;롤백할&amp;nbsp;예외&amp;nbsp;클래스&amp;nbsp;지정&lt;/li&gt;
&lt;li&gt;noRollbackFor:&amp;nbsp;롤백&amp;nbsp;제외할&amp;nbsp;예외&amp;nbsp;지정&lt;/li&gt;
&lt;li&gt;propagation:&amp;nbsp;트랜잭션&amp;nbsp;전파&amp;nbsp;전략&amp;nbsp;설정&amp;nbsp;(REQUIRED,&amp;nbsp;REQUIRES_NEW&amp;nbsp;등)&lt;/li&gt;
&lt;li&gt;isolation:&amp;nbsp;트랜잭션&amp;nbsp;격리&amp;nbsp;수준&amp;nbsp;설정&amp;nbsp;(READ_COMMITTED,&amp;nbsp;SERIALIZABLE&amp;nbsp;등)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음은 @Transactional이 수행하는 주요 역할들이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-end=&quot;362&quot; data-start=&quot;340&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;트랜잭션 자동 시작 및 커밋&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;455&quot; data-start=&quot;364&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;418&quot; data-start=&quot;364&quot;&gt;@Transactional이 붙은 메서드가 실행되면, &lt;b&gt;자동으로 트랜잭션이 시작&lt;/b&gt;된다.&lt;/li&gt;
&lt;li data-end=&quot;455&quot; data-start=&quot;419&quot;&gt;메서드가 예외 없이 종료되면 &lt;b&gt;트랜잭션은 자동 커밋&lt;/b&gt;된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1747120951581&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Transactional
public void updateUsername(Long userId, String newName) {
    User user = em.find(User.class, userId); // 영속 상태
    user.setUsername(newName);              // 변경 (더티 상태)
    // 트랜잭션 커밋 시 자동으로 UPDATE 쿼리 실행됨
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드는 @Transactional이 메서드에 선언되어 있어, 메서드 진입 시 트랜잭션이 자동 시작된다. 따라서 예외&amp;nbsp;없이&amp;nbsp;정상&amp;nbsp;종료되면&amp;nbsp;트랜잭션이&amp;nbsp;자동&amp;nbsp;커밋되어&amp;nbsp;변경된&amp;nbsp;필드가&amp;nbsp;DB에&amp;nbsp;반영된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-end=&quot;474&quot; data-start=&quot;457&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;예외 발생 시 롤백&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;642&quot; data-start=&quot;476&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;543&quot; data-start=&quot;476&quot;&gt;기본 설정에서는 RuntimeException 또는 Error가 발생하면 &lt;b&gt;자동으로 트랜잭션을 롤백&lt;/b&gt;한다.&lt;/li&gt;
&lt;li data-end=&quot;642&quot; data-start=&quot;544&quot;&gt;&lt;b&gt;Checked Exception(예: IOException)&lt;/b&gt; 발생 시에는 기본적으로 롤백되지 않지만, rollbackFor 속성으로 롤백 대상을 지정할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1747120958969&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Transactional
public void withdrawPoint(Long userId, int amount) {
    User user = em.find(User.class, userId);
    if (user.getPoint() &amp;lt; amount) {
        throw new IllegalArgumentException(&quot;포인트 부족&quot;); // 런타임 예외 &amp;rarr; 롤백됨
    }
    user.setPoint(user.getPoint() - amount); // 감산
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제는 IllegalArgumentException은 RuntimeException 계열이므로 트랜잭션이 자동 롤백된다. 그러므로 user.setPoint(...)가 수행되었더라도 DB에는 반영되지 않는다. 커밋이 정상적으로 수행되지 않기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-end=&quot;659&quot; data-start=&quot;644&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;더티 체킹(Dirty Checking) 연동&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;776&quot; data-start=&quot;661&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;773&quot; data-start=&quot;661&quot;&gt;트랜잭션이 종료될 때, JPA가 엔티티의 변경 사항을 감지하여 &lt;b&gt;자동으로 UPDATE 쿼리를 생성하고 실행&lt;/b&gt;한다.&lt;br /&gt;이 메커니즘을 더티 체킹(Dirty Checking)이라고 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1747121020641&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Transactional
public void changeEmail(Long userId, String newEmail) {
    User user = em.find(User.class, userId); // 영속 상태
    user.setEmail(newEmail);                // 필드 수정 (하지만 DB에는 아직 반영되지 않음)
    // 트랜잭션 커밋 시 Hibernate가 스냅샷과 비교 &amp;rarr; UPDATE 실행
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예시는 Hibernate는 user 객체의 초기 상태를 기억해두고 있다가, 트랜잭션 커밋 시점에 변경 사항을 감지하는 내용이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 때, &lt;b&gt;UPDATE user SET email=? WHERE id=? 쿼리&lt;/b&gt;를 자동 생성 및 실행한다. 즉, 개발자가 직접 em.update() 등을 호출하지 않아도 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;예제:&amp;nbsp;사용자&amp;nbsp;포인트&amp;nbsp;충전&amp;nbsp;서비스&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음은 @Transactional의 세 가지 핵심 기능을 하나의 예제 코드 흐름으로 통합하여 보여주는 &lt;b&gt;Spring Boot + JPA&lt;/b&gt; 기반 예시이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1747121095492&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Service
public class PointService {

    private final EntityManager em;

    public PointService(EntityManager em) {
        this.em = em;
    }

    @Transactional
    public void chargePoint(Long userId, int amount) {
        // 트랜잭션 자동 시작됨

        User user = em.find(User.class, userId);  // 1. 영속 상태로 조회
        if (user == null) throw new IllegalArgumentException(&quot;사용자 없음&quot;);

        // 2. 값 변경 &amp;rarr; 더티 체킹 대상
        user.setPoint(user.getPoint() + amount);

        // 3. 예외 조건 예시
        if (amount &amp;lt; 0) {
            throw new IllegalArgumentException(&quot;충전 금액은 0 이상이어야 합니다.&quot;);
        }

        // 트랜잭션 종료 시점 &amp;rarr; 커밋 or 롤백
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 커밋을 진행하게 되면 다음 쿼리가 실행되는 것으로 기대할 수 있다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;update&amp;nbsp;user&amp;nbsp;set&amp;nbsp;point&amp;nbsp;=&amp;nbsp;?&amp;nbsp;where&amp;nbsp;id&amp;nbsp;=&amp;nbsp;?&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드 흐름에서 예외 없이 종료되면 위와 같은 UPDATE 쿼리가 자동 실행된다. 또한 예외&amp;nbsp;발생&amp;nbsp;시에는&amp;nbsp;트랜잭션이&amp;nbsp;롤백되므로&amp;nbsp;이&amp;nbsp;쿼리는&amp;nbsp;실행되지&amp;nbsp;않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;관련 포스팅&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://aliencoder.tistory.com/226&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;2025.05.13 - [Languages/Java(Spring Boot)] - [Spring Boot] 더티 체킹(Dirty Checking)과 JPA의 Entity Lifecycle&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;참고 자료&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://joojimin.tistory.com/68&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://joojimin.tistory.com/68&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://resilient-923.tistory.com/415&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://resilient-923.tistory.com/415&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Languages/Java(Spring Boot)</category>
      <category>@transactional</category>
      <category>ACID</category>
      <category>atomicity</category>
      <category>Consistency</category>
      <category>durability</category>
      <category>isolation</category>
      <category>transaction</category>
      <category>transactional</category>
      <author>AlienCoder</author>
      <guid isPermaLink="true">https://aliencoder.tistory.com/232</guid>
      <comments>https://aliencoder.tistory.com/232#entry232comment</comments>
      <pubDate>Fri, 16 May 2025 12:01:00 +0900</pubDate>
    </item>
    <item>
      <title>[Pytorch] Broadcasting: 차원이 다를 때 연산</title>
      <link>https://aliencoder.tistory.com/230</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;python.jpg&quot; data-origin-width=&quot;850&quot; data-origin-height=&quot;440&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Hz4yB/btsLLTNx0s7/4yc93qR9ofHkKZfK2p9iI0/tfile.dat&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Hz4yB/btsLLTNx0s7/4yc93qR9ofHkKZfK2p9iI0/tfile.dat&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Hz4yB/btsLLTNx0s7/4yc93qR9ofHkKZfK2p9iI0/tfile.dat&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHz4yB%2FbtsLLTNx0s7%2F4yc93qR9ofHkKZfK2p9iI0%2Ftfile.dat&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;711&quot; height=&quot;368&quot; data-filename=&quot;python.jpg&quot; data-origin-width=&quot;850&quot; data-origin-height=&quot;440&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;Pytorch를 이용하여 모델을 구현하다 보면 텐서 간의 덧셈, 곱셈 등 연산을 수행할 때 차원이 서로 다른 경우를 자주 만나게 된다. 그런데도&amp;nbsp;에러&amp;nbsp;없이&amp;nbsp;연산이&amp;nbsp;가능한&amp;nbsp;이유는&amp;nbsp;바로&amp;nbsp;&lt;b&gt;Broadcasting&lt;/b&gt; 때문이다. Broadcasting은 Numpy에서 도입된 개념으로, PyTorch에서도 동일하게 적용된다. 이&amp;nbsp;기능&amp;nbsp;덕분에&amp;nbsp;복잡한&amp;nbsp;차원&amp;nbsp;조작&amp;nbsp;없이도&amp;nbsp;깔끔한&amp;nbsp;수식&amp;nbsp;구현이&amp;nbsp;가능하다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Broadcasting이란 서로 shape가 다른 두 tensor 간의 연산에서 작은 텐서의 shape을 자동으로 확장하여 큰 텐서와 연산이 가능하도록 맞춰주는 암시적(implicit) 규칙이다. 이는 개발자가 명시적으로 .expand()나 .repeat()를 호출하지 않아도 자동으로 연산되게 도와주는 기능이다. 아래의&amp;nbsp;조건이&amp;nbsp;맞으면&amp;nbsp;두&amp;nbsp;개의&amp;nbsp;tensor는&amp;nbsp;broadcatable 하다. &lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;각 tensor는 최소 one dimension이상 가지고 있어야 한다. &lt;/li&gt;
&lt;li&gt;dimension&amp;nbsp;sizes를&amp;nbsp;iterate 할&amp;nbsp;때에&amp;nbsp;dimension이&amp;nbsp;같거나&amp;nbsp;둘&amp;nbsp;중&amp;nbsp;하나가&amp;nbsp;1이거나&amp;nbsp;둘&amp;nbsp;중&amp;nbsp;하나가&amp;nbsp;없어야&amp;nbsp;한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;656&quot; data-start=&quot;617&quot; data-ke-size=&quot;size16&quot;&gt;PyTorch의 broadcasting은 다음의 규칙에 따라 동작한다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-end=&quot;742&quot; data-start=&quot;658&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li data-end=&quot;684&quot; data-start=&quot;658&quot;&gt;&lt;b&gt;오른쪽에서부터&lt;/b&gt; 차원을 비교한다.&lt;/li&gt;
&lt;li data-end=&quot;721&quot; data-start=&quot;685&quot;&gt;두 차원이 같거나, 둘 중 하나가 1이면 연산 가능하다.&lt;/li&gt;
&lt;li data-end=&quot;742&quot; data-start=&quot;722&quot;&gt;그렇지 않으면 에러가 발생한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-end=&quot;766&quot; data-start=&quot;744&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;766&quot; data-start=&quot;744&quot; data-ke-size=&quot;size16&quot;&gt;예를 들어 다음 두 텐서가 있다고 하자.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;834&quot; data-start=&quot;768&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;791&quot; data-start=&quot;768&quot;&gt;A.shape = (4, 3, 2)&lt;/li&gt;
&lt;li data-end=&quot;834&quot; data-start=&quot;792&quot;&gt;B.shape = ( , 1, 2) &amp;larr; 생략된 부분은 자동으로 맞춰짐&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;852&quot; data-start=&quot;836&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;852&quot; data-start=&quot;836&quot; data-ke-size=&quot;size16&quot;&gt;두 텐서는 다음처럼 비교된다.&lt;/p&gt;
&lt;blockquote data-end=&quot;852&quot; data-start=&quot;836&quot; data-ke-style=&quot;style3&quot;&gt;A: (4, 3, 2) &lt;br /&gt;B: (1, 1, 2) &amp;larr; 자동으로 앞쪽 차원에 1이 추가됨&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드를 통해 알아보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;예제&amp;nbsp;1:&amp;nbsp;스칼라와&amp;nbsp;텐서의&amp;nbsp;연산&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1747274354219&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import torch

x = torch.tensor([[1, 2], [3, 4]])  # shape: (2, 2)
y = 10                              # scalar

z = x + y  # y가 (2, 2)처럼 확장됨
print(z)
# tensor([[11, 12],
#        [13, 14]])&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;예제&amp;nbsp;2:&amp;nbsp;(3,&amp;nbsp;1)&amp;nbsp;vs&amp;nbsp;(1,&amp;nbsp;4)&amp;nbsp;텐서의&amp;nbsp;덧셈 &lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1747274363428&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;a = torch.randn(3, 1)  # shape: (3, 1)
# tensor([[ 0.9777],
#         [ 0.4755],
#         [-0.3185]])
b = torch.randn(1, 4)  # shape: (1, 4)
# tensor([[-0.1519,  0.3757,  1.1791,  0.3374]])

c = a + b  # shape: (3, 4)
# tensor([[ 0.8258,  1.3533,  2.1567,  1.3150],
#         [ 0.3236,  0.8512,  1.6546,  0.8129],
#         [-0.4704,  0.0572,  0.8606,  0.0189]])&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;참고 자료&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://velog.io/@optjyy/pytorch-broad-casting&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://velog.io/@optjyy/pytorch-broad-casting&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Languages/Python</category>
      <category>Broadcasting</category>
      <category>numpy</category>
      <category>pytorch</category>
      <author>AlienCoder</author>
      <guid isPermaLink="true">https://aliencoder.tistory.com/230</guid>
      <comments>https://aliencoder.tistory.com/230#entry230comment</comments>
      <pubDate>Fri, 16 May 2025 09:03:11 +0900</pubDate>
    </item>
    <item>
      <title>Logit(로짓)과 Odds(오즈)</title>
      <link>https://aliencoder.tistory.com/229</link>
      <description>&lt;script src=&quot;https://polyfill.io/v3/polyfill.min.js?features=es6&quot;&gt;&lt;/script&gt;
&lt;script src=&quot;https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js&quot;&gt;&lt;/script&gt;
&lt;p data-end=&quot;308&quot; data-start=&quot;139&quot; data-ke-size=&quot;size16&quot;&gt;딥러닝이나 머신러닝 모델을 다루다 보면 &lt;b&gt;Logit(로짓)&lt;/b&gt;과 &lt;b&gt;Odds(오즈)&lt;/b&gt;라는 용어를 자주 접하게 된다. 이 두 개념은 특히 분류 문제에서 모델의 예측 결과를 해석하거나 후처리하는 데 중요한 역할을 한다. 이 글에서는 logits와 odds의 정의부터 계산 방식, 그리고 실전 활용 예까지 정리하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Odds(오즈)&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Odds(오즈)&lt;/b&gt;는 어떤 사건이 일어날 &lt;b&gt;확률 대비 일어나지 않을 확률의 비율&lt;/b&gt;이다. 확률과는 다른 개념으로, 주로 통계학과 로지스틱 회귀에서 사용된다. 정의는 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ p⁡(x) = wx+b $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ \text{odds} =\frac{p(x)}{1-p(x)} $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 \( p \)는 특정 사건이 발생할 확률이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;예제 1&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;\( p = 0.8 \)이라면 odds는 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ \text{odds} = \frac{0.8}{1 - 0.8} = \frac{0.8}{0.2} = 4 $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 사건이 발생할 가능성이 발생하지 않을 가능성보다 &lt;b&gt;4배 크다&lt;/b&gt;는 의미이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;예제 2&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;\( p = 0.5 \)라면&amp;nbsp;odds는&amp;nbsp;다음과&amp;nbsp;같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ \text{odds} = \frac{0.5}{1 - 0.5} = \frac{0.5}{0.5} = 1 $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 발생/비발생 확률이 &lt;b&gt;동일하다&lt;/b&gt;는 의미이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Logit(로짓)&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Logit&lt;/b&gt;은 통계학에서 표준 로지스틱 분포의 분위수함수이며, 오즈비(odds ratio)의 자연로그이다. 즉, 로짓이란 오즈의 로그값이다. 로짓(logit)이란 말도 &lt;u&gt;&lt;b&gt;log + odds&lt;/b&gt;&lt;/u&gt;에서 나온 말이다. 딥러닝에선 주로 Softmax 또는 Sigmoid 함수에 입력되기 직전의 값으로 사용된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;330&quot; data-origin-height=&quot;215&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/NCS8n/btsNYozcpvo/LBudJIHdAUAYic5hvM5bB0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/NCS8n/btsNYozcpvo/LBudJIHdAUAYic5hvM5bB0/img.png&quot; data-alt=&quot;Fig 1. Logit&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/NCS8n/btsNYozcpvo/LBudJIHdAUAYic5hvM5bB0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNCS8n%2FbtsNYozcpvo%2FLBudJIHdAUAYic5hvM5bB0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;492&quot; height=&quot;321&quot; data-origin-width=&quot;330&quot; data-origin-height=&quot;215&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Fig 1. Logit&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Odds에 log를 취하여 Logit을 구하는 것은 확률(\( p \))을 실수선 전역으로 확장하여, 선형 모델이 분류 문제를 다룰 수 있게 해준다는 의미가 있다. 즉,&amp;nbsp;logit은&amp;nbsp;확률을&amp;nbsp;예측&amp;nbsp;가능한&amp;nbsp;연속적&amp;nbsp;수치로&amp;nbsp;바꾸는&amp;nbsp;핵심&amp;nbsp;도구이다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;확률(\( p \)): \( 0 &amp;lt; p &amp;lt; 1 \)&lt;/li&gt;
&lt;li&gt;Logit: \( -\infty &amp;lt; \text{logit}(p) &amp;lt; \infty \)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 통해 선형 회귀처럼 출력값이 제한 없는 모델에서도 확률 기반 분류 문제를 처리할 수 있게 해준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Logit의 수식은 다음과 같이 정의할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ \text{logit}(p) = log \left( \frac{p}{1-p} \right)&amp;nbsp;= log(odds) = z $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 \( p \) 는 어떤 클래스에 속할 확률이며, logit은 이 확률을 log(odds)로 변환한 값이다. 다중 분류(multi-class classification)에서는 softmax 전에 나오는 각 클래스에 대한 실수 값 출력 벡터가 logit이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 반대로 오즈는 로짓의 지수(exp)를 취하면 되면 다음과 같다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$&amp;nbsp;\text{odds}&amp;nbsp;=&amp;nbsp;e^{\text{logit}(p)}&amp;nbsp;$$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다시&amp;nbsp;말해,&amp;nbsp;오즈는&amp;nbsp;확률을&amp;nbsp;로그로&amp;nbsp;변환한&amp;nbsp;logit&amp;nbsp;값을&amp;nbsp;다시&amp;nbsp;원래대로&amp;nbsp;복원한&amp;nbsp;값이라는&amp;nbsp;뜻이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Sigmoid(시그모이드)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이진 분류에 자주 사용되는 &lt;b&gt;Sigmoid 함수&lt;/b&gt;는 로짓 함수의 역함수이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;485&quot; data-origin-height=&quot;323&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ejsLqs/btsNZTko3Uo/mGefapOQ9bllbEIX4TGZkk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ejsLqs/btsNZTko3Uo/mGefapOQ9bllbEIX4TGZkk/img.png&quot; data-alt=&quot;Fig 2. Sigmoid&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ejsLqs/btsNZTko3Uo/mGefapOQ9bllbEIX4TGZkk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FejsLqs%2FbtsNZTko3Uo%2FmGefapOQ9bllbEIX4TGZkk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;523&quot; height=&quot;348&quot; data-origin-width=&quot;485&quot; data-origin-height=&quot;323&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Fig 2. Sigmoid&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Logit을 자연로그로 표현하면 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ \text{logit}(p) = \ln\left( \frac{p}{1 - p} \right) = L $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 역함수를 만들면 다음과 같다. 이는 Sigmoid 함수의 형태이기도 하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ p = \frac{e^L}{1 + e^L} = \frac{1}{1 + e^{-L}} $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;예제&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;이진 분류 모델&lt;/b&gt;(여기선 &lt;b&gt;Sigmoid &lt;/b&gt;사용)을 사용한다고 가정한다. Sigmoid의 정의는 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ p = \sigma(z) = \frac{1}{1 + e^{-z}} $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 \( z \)는 logit값이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모델 출력이 logit = 2.197 일 때 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ \sigma(z) = \frac{1}{1 + e^{-2.197}} \approx 0.9 $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ \text{odds} = \frac{0.9}{0.1} = 9 $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 이 클래스일 가능성이 &lt;b&gt;아닐 가능성보다 9배 높다&lt;/b&gt;는 뜻이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;관련 포스팅&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://aliencoder.tistory.com/49&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;2022.03.02 - [Data Science/Data Analysis] - [Python] 로지스틱 회귀(Logistic Regression)와 이진 교차 엔트로피(Binary Cross Entropy)의 이해와 구현&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;참고 자료&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.linkedin.com/pulse/understanding-sigmoid-function-logistic-regression-piduguralla/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.linkedin.com/pulse/understanding-sigmoid-function-logistic-regression-piduguralla/&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Sigmoid_function&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://en.wikipedia.org/wiki/Sigmoid_function&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://wikidocs.net/177873&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://wikidocs.net/177873&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://velog.io/@zlddp723/%EB%A8%B8%EC%8B%A0%EB%9F%AC%EB%8B%9D%EC%9D%84-%EC%9C%84%ED%95%9C-%EA%B8%B0%EC%B4%88%EC%88%98%ED%95%99-Logits-sigmoid-softmax&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://velog.io/@zlddp723/%EB%A8%B8%EC%8B%A0%EB%9F%AC%EB%8B%9D%EC%9D%84-%EC%9C%84%ED%95%9C-%EA%B8%B0%EC%B4%88%EC%88%98%ED%95%99-Logits-sigmoid-softmax&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>AI/Data Analysis</category>
      <category>logit</category>
      <category>odds</category>
      <category>sigmoid</category>
      <author>AlienCoder</author>
      <guid isPermaLink="true">https://aliencoder.tistory.com/229</guid>
      <comments>https://aliencoder.tistory.com/229#entry229comment</comments>
      <pubDate>Thu, 15 May 2025 10:29:33 +0900</pubDate>
    </item>
    <item>
      <title>Penultimate Layer</title>
      <link>https://aliencoder.tistory.com/228</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;
&lt;script src=&quot;https://polyfill.io/v3/polyfill.min.js?features=es6&quot;&gt;&lt;/script&gt;
&lt;script src=&quot;https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js&quot;&gt;&lt;/script&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Penultimate: 마지막(끝)에서 두 번째&lt;/li&gt;
&lt;li&gt;Penultimate Layer: 제일 마지막 Layer 인 Classifier (Linear + softmax) 바로 직전 layer&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간혹 대학원 수업 도중 Penultimate Layer에 대한 내용이 언급되고는 하였는데, 해당 Layer의 의미에 대해 정리해보려 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt; Penultimate Layer &lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;딥러닝 모델에서 &lt;b&gt;Penultimate Layer&lt;/b&gt;는 말 그대로 끝에서 두 번째 층을 의미한다. 특히 분류(Classification) 문제에서 이 층은 모델의 최종 표현(feature representation)을 생성하는 데 중요한 역할을 한다. 출력층 바로 앞에 위치하며, 모델이 입력 데이터를 어떻게 이해하고 있는지를 간접적으로 보여주는 층이기도 하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;526&quot; data-start=&quot;401&quot; data-ke-size=&quot;size16&quot;&gt;Penultimate Layer는 보통 &lt;b&gt;추상적인 고차원 표현&lt;/b&gt;을 포함하고 있으며, 이 표현은 최종 출력 전에 모든 이전 층의 학습 결과를 집약한 형태라고 볼 수 있다. 이 층의 출력은 다음과 같은 이유로 매우 유용하다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;897&quot; data-start=&quot;528&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;643&quot; data-start=&quot;528&quot;&gt;&lt;b&gt;특징 추출(feature extraction)&lt;/b&gt;에 사용 가능하다.&lt;br /&gt;&amp;rarr; Pretrained 모델의 Penultimate Layer 출력을 이용하면 다양한 다운스트림 작업에 활용할 수 있다.&lt;/li&gt;
&lt;li data-end=&quot;764&quot; data-start=&quot;645&quot;&gt;&lt;b&gt;시각화와 해석 가능성(interpretability)&lt;/b&gt;을 높인다.&lt;br /&gt;&amp;rarr; 예: t-SNE나 PCA를 활용해 이 층의 출력을 2D로 시각화하면, 클래스 간의 분포나 클러스터링 경향을 파악할 수 있다.&lt;/li&gt;
&lt;li data-end=&quot;897&quot; data-start=&quot;766&quot;&gt;&lt;b&gt;Few-shot Learning이나 Transfer Learning&lt;/b&gt;에서 핵심 역할을 한다.&lt;br /&gt;&amp;rarr; 전체 모델을 fine-tune 하지 않고 penultimate representation만 활용하는 방식이 자주 쓰인다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음과 같이 Penultimate Layer 출력을 얻으면 이를 기반으로 새로운 분류기를 학습하거나 시각화하는 데 사용할 수 있다. PyTorch를 기준으로 Penultimate Layer 출력을 추출하는 예시이다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1747201813147&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import torch
import torchvision.models as models

model = models.resnet50(pretrained=True)
model.eval()

def get_penultimate_output(x):
    modules = list(model.children())[:-1]  # 마지막 FC 층 제외
    penultimate_model = torch.nn.Sequential(*modules)
    with torch.no_grad():
        features = penultimate_model(x)
        return features.squeeze()

# 입력 이미지 예시
# output = get_penultimate_output(input_tensor)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 예시를 몇 가지 더 살펴보면 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;예시 1: 이미지 분류 모델에서의 Penultimate Layer&lt;/b&gt;&lt;/h3&gt;
&lt;p data-end=&quot;256&quot; data-start=&quot;129&quot; data-ke-size=&quot;size16&quot;&gt;이미지 분류 분야에서 사용되는 &lt;b&gt;ResNet&lt;/b&gt;, &lt;b&gt;EfficientNet&lt;/b&gt;, &lt;b&gt;ViT (Vision Transformer)&lt;/b&gt; 등의 모델에서도 Penultimate Layer의 위치는 구조적으로 명확하게 정의되어 있다.&lt;/p&gt;
&lt;p data-end=&quot;276&quot; data-start=&quot;258&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;276&quot; data-start=&quot;258&quot; data-ke-size=&quot;size16&quot;&gt;일반적으로 다음과 같이 정의한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;605&quot; data-start=&quot;278&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;458&quot; data-start=&quot;278&quot;&gt;CNN 기반 모델(예: ResNet)에서는 Fully Connected (FC) 분류기 직전의 global average pooling 또는 projection 층의 출력을 Penultimate Layer로 본다. 이 출력은 이미지 전체를 요약한 최종 시각 특징 벡터로, 분류 결과에 직접적으로 영향을 미친다.&lt;/li&gt;
&lt;li data-end=&quot;605&quot; data-start=&quot;460&quot;&gt;&lt;b&gt;ViT와 같은 Transformer 기반 모델&lt;/b&gt;에서는 CLS 토큰의 마지막 블록 이전(hidden layer) 출력이 Penultimate Layer에 해당한다. 이 위치는 문맥이 충분히 반영된 상태에서 마지막 예측 전의 핵심 표현 정보를 담고 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;696&quot; data-start=&quot;607&quot; data-ke-size=&quot;size16&quot;&gt;이 층의 출력은 일반적으로 (batch_size, feature_dim) 형태의 고차원 벡터이며, 입력 이미지를 대표하는 압축된 시각 정보라고 볼 수 있다.&lt;/p&gt;
&lt;p data-end=&quot;696&quot; data-start=&quot;607&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;edited_edited_diagram_1.png&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;704&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dqwoIg/btsNXo6CnIi/TZn1NdFWFkI6MMmYn4E5h0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dqwoIg/btsNXo6CnIi/TZn1NdFWFkI6MMmYn4E5h0/img.png&quot; data-alt=&quot;Fig 1. CNN Penultimate Layer&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dqwoIg/btsNXo6CnIi/TZn1NdFWFkI6MMmYn4E5h0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdqwoIg%2FbtsNXo6CnIi%2FTZn1NdFWFkI6MMmYn4E5h0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;528&quot; height=&quot;363&quot; data-filename=&quot;edited_edited_diagram_1.png&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;704&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Fig 1. CNN Penultimate Layer&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-end=&quot;696&quot; data-start=&quot;607&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;696&quot; data-start=&quot;607&quot; data-ke-size=&quot;size16&quot;&gt;이미지 분류 모델에서의 Penultimate Layer 활용 사례는 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1090&quot; data-start=&quot;714&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;855&quot; data-start=&quot;714&quot;&gt;&lt;b&gt;이미지 임베딩 추출&lt;/b&gt;&lt;br /&gt;&amp;rarr; Penultimate Layer에서 추출한 벡터는 이미지 검색(Image Retrieval), 유사도 측정(Similarity Matching), 시각적 군집화(Clustering) 등에 직접 사용 가능하다.&lt;/li&gt;
&lt;li data-end=&quot;965&quot; data-start=&quot;857&quot;&gt;&lt;b&gt;중간 피처 기반 전이 학습&lt;/b&gt;&lt;br /&gt;&amp;rarr; 사전학습된 분류 모델의 Penultimate 출력을 그대로 사용하여 다른 비전 태스크(예: 객체 탐지, 스타일 분류)에 전이 학습할 수 있다.&lt;/li&gt;
&lt;li data-end=&quot;1090&quot; data-start=&quot;967&quot;&gt;&lt;b&gt;시각화 및 설명 가능성 향상&lt;/b&gt;&lt;br /&gt;&amp;rarr; 이 층의 출력을 기반으로 t-SNE, PCA를 적용하면 클래스 간 시각적 분포를 분석할 수 있으며, 이는 모델이 어떤 시각 패턴을 학습했는지 해석하는 데 도움이 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;1185&quot; data-start=&quot;1107&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-end=&quot;1185&quot; data-start=&quot;1107&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;예시 2: Transformer / Attention 모델에서의 Penultimate Layer&lt;/b&gt;&lt;/h3&gt;
&lt;p data-end=&quot;1185&quot; data-start=&quot;1107&quot; data-ke-size=&quot;size16&quot;&gt;Transformer 계열 모델에서 Penultimate Layer는 다음과 같은 위치에 해당한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1185&quot; data-start=&quot;1107&quot;&gt;Encoder 기준: N개의 Encoder Layer 중, 마지막 층 바로 전인 (N-1) 번째 Layer 출력이 penultimate이다.&lt;/li&gt;
&lt;li data-end=&quot;1185&quot; data-start=&quot;1107&quot;&gt;Decoder&amp;nbsp;기준:&amp;nbsp;마찬가지로,&amp;nbsp;Decoder의&amp;nbsp;마지막&amp;nbsp;층&amp;nbsp;바로&amp;nbsp;앞의&amp;nbsp;출력이&amp;nbsp;해당된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;1185&quot; data-start=&quot;1107&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;727&quot; data-origin-height=&quot;1024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/QD9wn/btsNXSMOs9x/0DMx3pbNNwZju9pjGhFk5K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/QD9wn/btsNXSMOs9x/0DMx3pbNNwZju9pjGhFk5K/img.png&quot; data-alt=&quot;Fig 2. Transformer Penultimate Layer&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/QD9wn/btsNXSMOs9x/0DMx3pbNNwZju9pjGhFk5K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQD9wn%2FbtsNXSMOs9x%2F0DMx3pbNNwZju9pjGhFk5K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;353&quot; height=&quot;497&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;727&quot; data-origin-height=&quot;1024&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Fig 2. Transformer Penultimate Layer&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-end=&quot;1185&quot; data-start=&quot;1107&quot; data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;Transformer / Attention 모델에서의 Penultimate Layer의 출력은 보통 다음과 같은 특징을 가진다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1185&quot; data-start=&quot;1107&quot;&gt;&lt;b&gt;Contextual&amp;nbsp;Representation의&amp;nbsp;정점&amp;nbsp;직전 &lt;/b&gt;&lt;br /&gt;&amp;rarr;&amp;nbsp;Attention을&amp;nbsp;통해&amp;nbsp;문맥이&amp;nbsp;모두&amp;nbsp;반영된&amp;nbsp;상태에서,&amp;nbsp;마지막&amp;nbsp;예측&amp;nbsp;바로&amp;nbsp;직전의&amp;nbsp;정보를&amp;nbsp;담고&amp;nbsp;있다. &lt;br /&gt;&amp;rarr; 특히 BERT나 GPT에서도 이 층의 출력은 hidden state로서 전이학습에서 자주 활용된다.&lt;/li&gt;
&lt;li data-end=&quot;1185&quot; data-start=&quot;1107&quot;&gt;&lt;b&gt;Token-level&amp;nbsp;임베딩으로&amp;nbsp;활용 &lt;/b&gt;&lt;br /&gt;&amp;rarr; 예를 들어, 각 토큰에 대한 penultimate hidden state는 token classification, tagging, 혹은 token similarity 분석에 사용될 수 있다.&lt;/li&gt;
&lt;li data-end=&quot;1185&quot; data-start=&quot;1107&quot;&gt;&lt;b&gt;Fine-tuning&amp;nbsp;지점으로&amp;nbsp;적합 &lt;/b&gt;&lt;br /&gt;&amp;rarr;&amp;nbsp;마지막&amp;nbsp;출력층은&amp;nbsp;task-specific 하지만,&amp;nbsp;penultimate는&amp;nbsp;보다&amp;nbsp;일반적인&amp;nbsp;표현을&amp;nbsp;포함하고&amp;nbsp;있어&amp;nbsp;다양한&amp;nbsp;task에&amp;nbsp;범용적으로&amp;nbsp;활용&amp;nbsp;가능하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;1185&quot; data-start=&quot;1107&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-end=&quot;1185&quot; data-start=&quot;1107&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;예시 3: 음성 인식 모델에서의 Penultimate Layer&lt;/b&gt;&lt;/h3&gt;
&lt;p data-end=&quot;248&quot; data-start=&quot;138&quot; data-ke-size=&quot;size16&quot;&gt;음성 인식 모델, 특히 &lt;b&gt;Conformer&lt;/b&gt;나 &lt;b&gt;RNN-T&lt;/b&gt;, &lt;b&gt;Transformer-CTC&lt;/b&gt;와 같은 구조에서는 Penultimate Layer의 위치가 보다 구조적으로 정의되어야 한다.&lt;/p&gt;
&lt;p data-end=&quot;248&quot; data-start=&quot;138&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;248&quot; data-start=&quot;138&quot; data-ke-size=&quot;size16&quot;&gt;일반적으로 다음과 같이 정의한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;506&quot; data-start=&quot;270&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;395&quot; data-start=&quot;270&quot;&gt;&lt;b&gt;Conformer-CTC 모델&lt;/b&gt;에서는 Encoder의 마지막 블록 직전 층의 출력을 Penultimate Layer로 볼 수 있다. 이 출력은 최종 CTC 로스 계산 이전에 가장 정보가 응축된 고차원 특징 벡터이다.&lt;/li&gt;
&lt;li data-end=&quot;506&quot; data-start=&quot;396&quot;&gt;이 층의 출력은 보통 시간축을 따라 존재하며, 각 프레임에 대해 d차원의 벡터를 반환한다. 즉, \( T \times d_{model} \) 형태를 가지며, 각 시점의 음성 특징이 높은 표현력으로 인코딩 되어 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;527&quot; data-start=&quot;508&quot; data-ke-size=&quot;size16&quot;&gt;음성 인식 모델에서의 Penultimate Layer의 활용 사례는 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;865&quot; data-start=&quot;529&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;667&quot; data-start=&quot;529&quot;&gt;&lt;b&gt;음성 임베딩 추출&lt;/b&gt;&lt;br /&gt;&amp;rarr; Penultimate Layer에서 추출한 벡터는 화자 인식(Speaker ID), 감정 인식(Emotion Recognition), 키워드 검출(Keyword Spotting) 등에 바로 사용 가능하다.&lt;/li&gt;
&lt;li data-end=&quot;777&quot; data-start=&quot;669&quot;&gt;&lt;b&gt;중간 피처 기반 전이 학습&lt;/b&gt;&lt;br /&gt;&amp;rarr; ASR 모델에서 학습한 Penultimate 표현을 그대로 사용하여 downstream task에 transfer learning을 적용한다.&lt;/li&gt;
&lt;li data-end=&quot;865&quot; data-start=&quot;779&quot;&gt;&lt;b&gt;디코더 이전 단계의 정보 가시화&lt;/b&gt;&lt;br /&gt;&amp;rarr; 이 층에서 추출된 정보를 분석하면 모델이 어떤 음향 패턴을 &amp;lsquo;이해&amp;rsquo;하고 있는지를 파악할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>AI/ML &amp;amp; DL</category>
      <category>penultimate</category>
      <category>penultimate layer</category>
      <author>AlienCoder</author>
      <guid isPermaLink="true">https://aliencoder.tistory.com/228</guid>
      <comments>https://aliencoder.tistory.com/228#entry228comment</comments>
      <pubDate>Wed, 14 May 2025 17:25:07 +0900</pubDate>
    </item>
    <item>
      <title>[Spring Boot] 더티 체킹(Dirty Checking)과 JPA의 Entity Lifecycle</title>
      <link>https://aliencoder.tistory.com/226</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;640&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/beziAU/btsLJCUjM9q/1j7sNwamcIoIsAjwJrD1Vk/tfile.dat&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/beziAU/btsLJCUjM9q/1j7sNwamcIoIsAjwJrD1Vk/tfile.dat&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/beziAU/btsLJCUjM9q/1j7sNwamcIoIsAjwJrD1Vk/tfile.dat&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbeziAU%2FbtsLJCUjM9q%2F1j7sNwamcIoIsAjwJrD1Vk%2Ftfile.dat&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;732&quot; height=&quot;366&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;640&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;더티&amp;nbsp;체킹(Dirty&amp;nbsp;Checking)&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;더티 체킹(Dirty Checking)&lt;/b&gt;이란 객체 지향 프로그래밍 환경에서 객체의 상태가 변경되었는지 감지하여 데이터베이스에 자동 반영하는 메커니즘이다. 즉,&amp;nbsp;개발자가&amp;nbsp;직접&amp;nbsp;SQL&amp;nbsp;UPDATE&amp;nbsp;명령을&amp;nbsp;작성하지&amp;nbsp;않아도,&amp;nbsp;객체의&amp;nbsp;속성을&amp;nbsp;변경한&amp;nbsp;후&amp;nbsp;트랜잭션을&amp;nbsp;커밋하면&amp;nbsp;자동으로&amp;nbsp;변경&amp;nbsp;사항이&amp;nbsp;DB에&amp;nbsp;반영되는&amp;nbsp;기능이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주로 &lt;b&gt;JPA(Java Persistence API), Hibernate, SQLAlchemy&lt;/b&gt; 등 &lt;b&gt;ORM(Object-Relational Mapping)&lt;/b&gt; 프레임워크에서 사용된다. 더티 체킹은 Python 환경에서도 사용되긴 하지만 주로 &lt;b&gt;Spring&amp;nbsp;Boot&amp;nbsp;+&amp;nbsp;JPA(Hibernate)&lt;/b&gt;&amp;nbsp;환경에서&amp;nbsp;가장&amp;nbsp;많이&amp;nbsp;사용되는&amp;nbsp;패턴&amp;nbsp;중&amp;nbsp;하나이다.&amp;nbsp;실무에서는&amp;nbsp;더티&amp;nbsp;체킹을&amp;nbsp;사용할&amp;nbsp;때&amp;nbsp;대부분&amp;nbsp;Spring&amp;nbsp;Boot&amp;nbsp;환경에서&amp;nbsp;@Transactional,&amp;nbsp;Entity&amp;nbsp;변경,&amp;nbsp;자동&amp;nbsp;UPDATE&amp;nbsp;흐름으로&amp;nbsp;사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;517&quot; data-start=&quot;469&quot; data-ke-size=&quot;size16&quot;&gt;전통적인 DB 프로그래밍에서는 데이터 변경이 일어나면 다음과 같은 과정을 거쳐야 했다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-end=&quot;586&quot; data-start=&quot;519&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li data-end=&quot;532&quot; data-start=&quot;519&quot;&gt;데이터를 조회한다.&lt;/li&gt;
&lt;li data-end=&quot;548&quot; data-start=&quot;533&quot;&gt;필요한 값을 변경한다.&lt;/li&gt;
&lt;li data-end=&quot;586&quot; data-start=&quot;549&quot;&gt;변경된 내용을 SQL UPDATE로 직접 DB에 반영한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;616&quot; data-start=&quot;588&quot; data-ke-size=&quot;size16&quot;&gt;하지만 ORM에서는 다음과 같은 방식으로 처리된다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-end=&quot;694&quot; data-start=&quot;618&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li data-end=&quot;630&quot; data-start=&quot;618&quot;&gt;객체를 조회한다.&lt;/li&gt;
&lt;li data-end=&quot;647&quot; data-start=&quot;631&quot;&gt;객체의 필드를 수정한다.&lt;/li&gt;
&lt;li data-end=&quot;694&quot; data-start=&quot;648&quot;&gt;트랜잭션을 커밋하면 ORM이 변경된 필드를 자동 감지하여 DB에 업데이트한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-end=&quot;745&quot; data-start=&quot;696&quot; data-ke-size=&quot;size16&quot;&gt;이때 2와 3 사이의 과정을 더티 체킹(Dirty Checking)이라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Dirty Checking의 내부 작동 방식 순서는 다음과 같다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;ORM은&amp;nbsp;영속&amp;nbsp;상태의&amp;nbsp;객체의&amp;nbsp;최초&amp;nbsp;상태(스냅샷)를&amp;nbsp;메모리에&amp;nbsp;저장해 둔다.&lt;/li&gt;
&lt;li&gt;트랜잭션&amp;nbsp;커밋&amp;nbsp;시점에&amp;nbsp;현재&amp;nbsp;상태와&amp;nbsp;스냅샷을&amp;nbsp;비교한다.&lt;/li&gt;
&lt;li&gt;변경된 필드가 있으면 UPDATE 쿼리를 생성하여 DB에 반영한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이로 인해 개발자는 직접 UPDATE 쿼리를 작성하지 않고도 변경을 안전하게 반영할 수 있으며, 비즈니스 로직에 집중할 수 있는 장점이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단, 더티&amp;nbsp;체킹은&amp;nbsp;객체가&amp;nbsp;&lt;b&gt;영속(persistent)&amp;nbsp;상태&lt;/b&gt;일 때만 작동한다. 이 부분은 JPA의 Entity의 Lifecycle과 관련된 부분인데 예제 이후 다시 살펴보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;예제 1(Java - JPA 기준)&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1747102387241&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Transactional
public void updateUsername(Long userId, String newName) {
    User user = em.find(User.class, userId);  // 영속 상태
    user.setUsername(newName);               // 값 변경 (더티 상태)
    // 트랜잭션 커밋 시 자동으로 UPDATE 쿼리 실행
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드에서 user.setUsername(newName) 호출은 메모리 상 객체 값을 변경한 것이며, 이 시점에서는 DB에 아무 영향도 없다. 그러나 트랜잭션이 커밋되면, JPA는 해당 객체의 &lt;b&gt;초기 상태(snapshot)&lt;/b&gt;와 현재 상태를 비교하여 변경된 필드를 감지하고, 그에 맞는 SQL UPDATE문을 자동 생성하여 실행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;예제 2(Python - SQLAlchemy 기준)&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1747102428774&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from sqlalchemy.orm import Session

with Session(engine) as session:
    user = session.get(User, 1)
    user.name = &quot;new_name&quot;  # 값 변경
    session.commit()        # 더티 체킹 &amp;rarr; UPDATE 쿼리 자동 실행&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SQLAlchemy 역시 JPA와 동일한 방식으로 객체의 변경을 추적하고, 커밋 시 DB에 반영한다. 변경이 없는 경우에는 UPDATE 쿼리를 실행하지 않으며, 이를 스냅샷 기반 변경 감지라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;JPA의 Entity(엔티티) Lifecycle&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JPA에서는 엔티티 객체의 상태를 아래 네 가지로 구분한다. 이 상태에 따라 더티 체킹의 동작에 대해 주의할 점이 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;New(Transient, 비영속): 아직 EntityManager에 저장되지 않은 객체. DB와 전혀 연관 없음&lt;/li&gt;
&lt;li&gt;Persistent(영속):&amp;nbsp;EntityManager가&amp;nbsp;관리&amp;nbsp;중인&amp;nbsp;상태.&amp;nbsp;DB와&amp;nbsp;연결됨&lt;/li&gt;
&lt;li&gt;Detached(준영속):&amp;nbsp;한때&amp;nbsp;영속이었지만,&amp;nbsp;현재는&amp;nbsp;EntityManager가&amp;nbsp;관리하지&amp;nbsp;않음&lt;/li&gt;
&lt;li&gt;Removed(삭제&amp;nbsp;예정):&amp;nbsp;삭제가&amp;nbsp;예정된&amp;nbsp;상태.&amp;nbsp;트랜잭션&amp;nbsp;커밋&amp;nbsp;시&amp;nbsp;DB에서&amp;nbsp;삭제됨&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 상태를 코드로 간단하게 살펴보면 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;비영속(Transient)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Transient 상태는 아직 DB에 저장되지 않았고, EntityManager가 관리하지 않는 상태를 말한다.&lt;/p&gt;
&lt;pre id=&quot;code_1747118420202&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;User user = new User(&quot;new name&quot;); // 아직 persist 안됨&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;영속(Persistent)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Persistent 상태는 EntityManager.find() 또는 JPQL로 조회한 엔티티는 영속 상태가 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1747117860656&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;User user = em.find(User.class, 1L); // 영속 상태
user.setUsername(&quot;new name&quot;);       // 변경
em.flush();                         // 변경 감지 &amp;rarr; UPDATE&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;준영속(Detached)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Detached 상태는 EntityManager가 더 이상 해당 객체를 추적하지 않기 때문에, 커밋 시에도 아무 쿼리도 발생하지 않는다.&lt;/p&gt;
&lt;pre id=&quot;code_1747118015489&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;User user = em.find(User.class, 1L);
em.detach(user); // 이제 더 이상 관리하지 않음
user.setUsername(&quot;change&quot;); // 변경해도 감지 안 됨&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;삭제 예정(Removed)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Removed 상태는 JPA에서 EntityManager.remove()를 호출한 엔티티가 갖게 되는 상태이다. 따라서 이 상태의 엔티티는 트랜잭션 커밋 시 실제 DB에서 삭제되며, 더티 체킹은 무시되고 삭제 쿼리만 발생한다.&lt;/p&gt;
&lt;pre id=&quot;code_1747118500801&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;User user = em.find(User.class, 1L);  // 영속 상태
em.remove(user);                     // Removed 상태 전환
user.setUsername(&quot;change&quot;);          // 무시됨, 더티 체킹 비활성&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Lifecycle Flow&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 JPA Entity의 4가지 상태를 도식화하면 다음과 같이 나타낼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;546&quot; data-origin-height=&quot;197&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/IStHO/btsNWS6vfiI/OYw71wcsjN5oHTvMOjmOAK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/IStHO/btsNWS6vfiI/OYw71wcsjN5oHTvMOjmOAK/img.png&quot; data-alt=&quot;Fig 1. JPA Entity Object Lifecycle&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/IStHO/btsNWS6vfiI/OYw71wcsjN5oHTvMOjmOAK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FIStHO%2FbtsNWS6vfiI%2FOYw71wcsjN5oHTvMOjmOAK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;604&quot; height=&quot;218&quot; data-origin-width=&quot;546&quot; data-origin-height=&quot;197&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Fig 1. JPA Entity Object Lifecycle&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 상태들을 하나의 흐름으로 코드를 구성해보면 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1747122026416&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Transactional
public void lifecycleDemo() {
    // 1. 비영속
    User user = new User(&quot;kim&quot;);
    // 2. 영속
    em.persist(user);
    // 3. 준영속
    em.detach(user); // 또는 em.clear();
    // 4. 다시 영속
    em.merge(user);
    // 5. 삭제 예정
    em.remove(user);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;position: absolute;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 4가지 상태 중 더티 체킹은 객체가 &lt;b&gt;영속(persistent) 상태일 때만 작동&lt;/b&gt;한다.&amp;nbsp;즉,&amp;nbsp;비영속(transient)&amp;nbsp;상태나&amp;nbsp;준영속(detached)&amp;nbsp;상태에서는&amp;nbsp;더티&amp;nbsp;체킹이&amp;nbsp;일어나지&amp;nbsp;않는다.&amp;nbsp;또한&amp;nbsp;성능&amp;nbsp;최적화를&amp;nbsp;위해&amp;nbsp;변경이&amp;nbsp;많거나&amp;nbsp;조건이&amp;nbsp;복잡한&amp;nbsp;경우에는&amp;nbsp;직접&amp;nbsp;SQL을&amp;nbsp;작성하거나&amp;nbsp;벌크&amp;nbsp;연산을&amp;nbsp;고려하는&amp;nbsp;것이&amp;nbsp;바람직하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;참고 자료&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.javaguides.net/2019/01/jpa-entity-object-life-cycle-new-managed-removed-detached.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.javaguides.net/2019/01/jpa-entity-object-life-cycle-new-managed-removed-detached.html&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://jojoldu.tistory.com/415&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://jojoldu.tistory.com/415&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Languages/Java(Spring Boot)</category>
      <category>dirty checking</category>
      <category>jpa entity lifecycle</category>
      <category>object-relational mapping</category>
      <category>ORM</category>
      <category>더티 체킹</category>
      <author>AlienCoder</author>
      <guid isPermaLink="true">https://aliencoder.tistory.com/226</guid>
      <comments>https://aliencoder.tistory.com/226#entry226comment</comments>
      <pubDate>Wed, 14 May 2025 09:00:39 +0900</pubDate>
    </item>
    <item>
      <title>[Linux] 파일 디스크립터(File Descriptor)</title>
      <link>https://aliencoder.tistory.com/225</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;파일 디스크립터(File Descriptor)&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;파일&amp;nbsp;디스크립터(File&amp;nbsp;Descriptor,&amp;nbsp;FD)&lt;/b&gt;는 운영체제가 프로세스가 접근할 수 있는 파일, 소켓, 파이프 등을 구분하기 위해 부여하는 정수 값이다. 리눅스나 유닉스 계열 시스템에서 자주 사용되며, 시스템 콜(system call)을 통해 파일이나 네트워크 자원에 접근할 때 핵심적으로 활용된다.&amp;nbsp;&lt;br /&gt;&lt;br /&gt;간단히&amp;nbsp;말해,&amp;nbsp;프로세스&amp;nbsp;입장에서&amp;nbsp;파일&amp;nbsp;디스크립터는&amp;nbsp;파일을&amp;nbsp;가리키는&amp;nbsp;핸들(handle)&amp;nbsp;역할을&amp;nbsp;한다.&amp;nbsp;이&amp;nbsp;정수값을&amp;nbsp;통해&amp;nbsp;프로세스는&amp;nbsp;운영체제에게&amp;nbsp;어떤&amp;nbsp;파일에&amp;nbsp;어떤&amp;nbsp;작업을&amp;nbsp;수행할지를&amp;nbsp;요청할&amp;nbsp;수&amp;nbsp;있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로세스가&amp;nbsp;시작되면,&amp;nbsp;기본적으로&amp;nbsp;세&amp;nbsp;개의&amp;nbsp;파일&amp;nbsp;디스크립터가&amp;nbsp;자동으로&amp;nbsp;열리게&amp;nbsp;된다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-end=&quot;777&quot; data-start=&quot;513&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center; width: 32.093%;&quot;&gt;파일&amp;nbsp;디스크립터&amp;nbsp;번호&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 33.4884%;&quot;&gt;이름&lt;/td&gt;
&lt;td style=&quot;text-align: center; width: 34.186%;&quot;&gt;설명&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;671&quot; data-start=&quot;618&quot;&gt;
&lt;td style=&quot;width: 32.093%; text-align: center;&quot; data-col-size=&quot;sm&quot; data-end=&quot;640&quot; data-start=&quot;618&quot;&gt;0&lt;/td&gt;
&lt;td style=&quot;width: 33.4884%; text-align: center;&quot; data-end=&quot;653&quot; data-start=&quot;640&quot; data-col-size=&quot;sm&quot;&gt;stdin&lt;/td&gt;
&lt;td style=&quot;width: 34.186%; text-align: center;&quot; data-end=&quot;671&quot; data-start=&quot;653&quot; data-col-size=&quot;sm&quot;&gt;표준 입력&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;725&quot; data-start=&quot;672&quot;&gt;
&lt;td style=&quot;width: 32.093%; text-align: center;&quot; data-col-size=&quot;sm&quot; data-end=&quot;694&quot; data-start=&quot;672&quot;&gt;1&lt;/td&gt;
&lt;td style=&quot;width: 33.4884%; text-align: center;&quot; data-end=&quot;707&quot; data-start=&quot;694&quot; data-col-size=&quot;sm&quot;&gt;stdout&lt;/td&gt;
&lt;td style=&quot;width: 34.186%; text-align: center;&quot; data-end=&quot;725&quot; data-start=&quot;707&quot; data-col-size=&quot;sm&quot;&gt;표준 출력&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;777&quot; data-start=&quot;726&quot;&gt;
&lt;td style=&quot;width: 32.093%; text-align: center;&quot; data-col-size=&quot;sm&quot; data-end=&quot;748&quot; data-start=&quot;726&quot;&gt;2&lt;/td&gt;
&lt;td style=&quot;width: 33.4884%; text-align: center;&quot; data-col-size=&quot;sm&quot; data-end=&quot;761&quot; data-start=&quot;748&quot;&gt;stderr&lt;/td&gt;
&lt;td style=&quot;width: 34.186%; text-align: center;&quot; data-col-size=&quot;sm&quot; data-end=&quot;777&quot; data-start=&quot;761&quot;&gt;표준 오류 출력&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를&amp;nbsp;들어,&amp;nbsp;printf()&amp;nbsp;함수는&amp;nbsp;내부적으로&amp;nbsp;stdout,&amp;nbsp;즉&amp;nbsp;파일&amp;nbsp;디스크립터&amp;nbsp;1번을&amp;nbsp;사용하여&amp;nbsp;화면에&amp;nbsp;출력을&amp;nbsp;한다.&amp;nbsp;마찬가지로&amp;nbsp;scanf()는&amp;nbsp;stdin,&amp;nbsp;즉&amp;nbsp;0번&amp;nbsp;디스크립터를&amp;nbsp;통해&amp;nbsp;입력을&amp;nbsp;받는다. &lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;예제&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C 언어나 Python에서는 파일을 열 때 내부적으로 파일 디스크립터가 생성된다. 다음은 C 언어에서 open() 시스템 콜을 사용하는 예이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;C&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1747100375491&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;fcntl.h&amp;gt;
#include &amp;lt;unistd.h&amp;gt;
#include &amp;lt;stdio.h&amp;gt;

int main() {
    int fd = open(&quot;example.txt&quot;, O_RDONLY);
    if (fd == -1) {
        perror(&quot;파일 열기 실패&quot;);
        return 1;
    }

    printf(&quot;할당된 파일 디스크립터: %d\n&quot;, fd);
    close(fd);
    return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Python&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1747100562356&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 파일 열기
f = open(&quot;example.txt&quot;, &quot;r&quot;)

# 파일 디스크립터 확인
print(f&quot;파일 디스크립터: {f.fileno()}&quot;)

# 파일 닫기
f.close()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이&amp;nbsp;예제는&amp;nbsp;example.txt&amp;nbsp;파일을&amp;nbsp;읽기&amp;nbsp;전용으로&amp;nbsp;열고,&amp;nbsp;할당된&amp;nbsp;파일&amp;nbsp;디스크립터를&amp;nbsp;출력한&amp;nbsp;후&amp;nbsp;닫는&amp;nbsp;코드이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 리눅스 커맨드를 이용하여 파일 디스크립터를 확인하는 방법이다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;실행중인 프로세스 PID 확인&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1747100618500&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;ps aux | grep python&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1325&quot; data-origin-height=&quot;76&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tz3bv/btsNUbGjaXP/W8GmwTtw6AY3UV7CrZTH0K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tz3bv/btsNUbGjaXP/W8GmwTtw6AY3UV7CrZTH0K/img.png&quot; data-alt=&quot;Fig 1. ps commnad&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tz3bv/btsNUbGjaXP/W8GmwTtw6AY3UV7CrZTH0K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Ftz3bv%2FbtsNUbGjaXP%2FW8GmwTtw6AY3UV7CrZTH0K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;702&quot; height=&quot;40&quot; data-origin-width=&quot;1325&quot; data-origin-height=&quot;76&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Fig 1. ps commnad&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;PID로 해당 프로세스의 FD 디렉토리 확인&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1747100643089&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;ls -l /proc/12345/fd/&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 12345는 해당 Python 프로세스의 PID이다. 이 디렉토리에는 해당 프로세스가 열고 있는 모든 파일 디스크립터가 심볼릭 링크로 나열된다. 다음과 같은 형태의 출력이 나올 것이다.&lt;/p&gt;
&lt;pre id=&quot;code_1747100962290&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;lrwx------ 1 user user 64 May 13 10:15 0 -&amp;gt; /dev/pts/0
lrwx------ 1 user user 64 May 13 10:15 1 -&amp;gt; /dev/pts/0
lrwx------ 1 user user 64 May 13 10:15 2 -&amp;gt; /dev/pts/0
lr-x------ 1 user user 64 May 13 10:15 3 -&amp;gt; /home/user/example.txt&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 0, 1, 2는 각각 stdin, stdout, stderr을 의미하며, 3은 example.txt를 open()해서 열었을 때 생성된 파일 디스크립터를 의미한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;파일 디스크립터와 리소스 관리&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;운영체제는&amp;nbsp;하나의&amp;nbsp;프로세스에&amp;nbsp;대해&amp;nbsp;열&amp;nbsp;수&amp;nbsp;있는&amp;nbsp;파일&amp;nbsp;디스크립터&amp;nbsp;수에&amp;nbsp;제한을&amp;nbsp;둔다.&amp;nbsp;이&amp;nbsp;제한은&amp;nbsp;ulimit&amp;nbsp;-n&amp;nbsp;명령어를&amp;nbsp;통해&amp;nbsp;확인하거나&amp;nbsp;변경할&amp;nbsp;수&amp;nbsp;있다.&amp;nbsp;파일&amp;nbsp;디스크립터를&amp;nbsp;적절히&amp;nbsp;닫지&amp;nbsp;않으면&amp;nbsp;리소스&amp;nbsp;누수로&amp;nbsp;이어져&amp;nbsp;시스템&amp;nbsp;전체의&amp;nbsp;안정성을&amp;nbsp;해칠&amp;nbsp;수&amp;nbsp;있으므로,&amp;nbsp;파일을&amp;nbsp;사용한&amp;nbsp;후&amp;nbsp;반드시&amp;nbsp;close()&amp;nbsp;함수를&amp;nbsp;호출하여&amp;nbsp;반환하는&amp;nbsp;것이&amp;nbsp;중요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;파일&amp;nbsp;디스크립터와&amp;nbsp;소켓&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네트워크&amp;nbsp;프로그래밍에서도&amp;nbsp;파일&amp;nbsp;디스크립터는&amp;nbsp;동일하게&amp;nbsp;사용된다.&amp;nbsp;socket()&amp;nbsp;함수로&amp;nbsp;생성된&amp;nbsp;소켓도&amp;nbsp;파일&amp;nbsp;디스크립터로&amp;nbsp;관리되며,&amp;nbsp;read(),&amp;nbsp;write()&amp;nbsp;또는&amp;nbsp;select(),&amp;nbsp;poll()&amp;nbsp;같은&amp;nbsp;함수에서&amp;nbsp;활용된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1747100434914&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int sockfd = socket(AF_INET, SOCK_STREAM, 0);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이와&amp;nbsp;같이&amp;nbsp;생성된&amp;nbsp;소켓도&amp;nbsp;일반&amp;nbsp;파일과&amp;nbsp;동일하게&amp;nbsp;정수값(파일&amp;nbsp;디스크립터)을&amp;nbsp;가진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Computer Science/OS(Operating System)</category>
      <category>file descriptor</category>
      <category>linux</category>
      <category>파일 디스크립터</category>
      <author>AlienCoder</author>
      <guid isPermaLink="true">https://aliencoder.tistory.com/225</guid>
      <comments>https://aliencoder.tistory.com/225#entry225comment</comments>
      <pubDate>Tue, 13 May 2025 10:50:51 +0900</pubDate>
    </item>
    <item>
      <title>Hidden Markov Model(HMM, 은닉 마르코프 모델)</title>
      <link>https://aliencoder.tistory.com/224</link>
      <description>&lt;script src=&quot;https://polyfill.io/v3/polyfill.min.js?features=es6&quot;&gt;&lt;/script&gt;
&lt;script src=&quot;https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js&quot;&gt;&lt;/script&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;음성정보처리를 수행할 때 &lt;b&gt;Hidden Markov Model(HMM)&lt;/b&gt;에 대해 배우게 된다. 현재는 DNN 기반의 여러 방법들이 사용되고 있지만 레거시라고 그냥 넘어가기엔 큰 영향이 있었던 방법이므로 다시 정리하려한다. HMM을 살펴보기 전 Markov Model이 무엇인지 먼저 살펴보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Markov Model&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Markov Model&lt;/b&gt;은 확률 기반 시퀀스 모델의 시작점으로 볼 수 있다. Markov Model은 시스템이 여러 상태를 가지고 있으며, 각 &lt;b&gt;상태(State)&lt;/b&gt; 사이를 이동하는 확률인 &lt;b&gt;전이 확률(Transition Probability)&lt;/b&gt;을 마르코프 성질로 정의한 확률 모델을 의미한다. 상태와 전이 확률을 다음과 같이 정의할 수 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;상태(State)&lt;/b&gt;: 시스템이 취할 수 있는 다양한 상태. &lt;br /&gt;예) 날씨의 경우 맑음, 흐림, 비, 눈 등 다양한 상태&amp;nbsp;&amp;nbsp;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;전이 확률(Transition&amp;nbsp;Probability)&lt;/b&gt;: 한 상태에서 다른 상태로 이동할 확률. &lt;br /&gt;예) 맑음에서 흐림으로 이동할 확률, 흐림에서 비로 이동할 확률 등&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 &lt;b&gt;마르코프 성질(Markov Property)&lt;/b&gt;이라는 중요한 가정을 기반으로 한다. Markov Property란 미래 상태는 현재 상태에만 의존하고, 과거 상태에는 의존하지 않는다는 성질을 의미한다. 예를 들어, 오늘 날씨가 맑다면 내일 날씨가 맑을 확률은 오늘 날씨가 맑다는 사실만으로 결정되고, 지난주 날씨가 어떻든 상관없다는 것이다. 개인적으로&amp;nbsp;이&amp;nbsp;부분에&amp;nbsp;대해&amp;nbsp;&lt;b&gt;바로&amp;nbsp;이전&amp;nbsp;상태(\(&amp;nbsp;t-1&amp;nbsp;\))가&amp;nbsp;과거의&amp;nbsp;모든&amp;nbsp;정보를&amp;nbsp;요약하고&amp;nbsp;있는&amp;nbsp;것과&amp;nbsp;같다&lt;/b&gt;는&amp;nbsp;의미로&amp;nbsp;받아들였을&amp;nbsp;때&amp;nbsp;더&amp;nbsp;와닿았다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;First-order dependency: 어떤&amp;nbsp;시점의&amp;nbsp;상태는&amp;nbsp;직전&amp;nbsp;시점의&amp;nbsp;상태에만&amp;nbsp;의존하며,&amp;nbsp;그&amp;nbsp;이전의&amp;nbsp;상태에는&amp;nbsp;독립적이다.&lt;br /&gt;$$ P(q^t = j | q^{t-1} = i, q^{t-2}, ...) = P(q^t = j | q^{t-1} = i) = a_{ij} $$&lt;/li&gt;
&lt;li&gt;Time-invariance: 시불변성은 상태&amp;nbsp;전이&amp;nbsp;확률은&amp;nbsp;시간&amp;nbsp;\(&amp;nbsp;t&amp;nbsp;\)에&amp;nbsp;의존하지&amp;nbsp;않는다.&amp;nbsp;즉,&amp;nbsp;상태&amp;nbsp;간의&amp;nbsp;전이&amp;nbsp;확률은&amp;nbsp;항상&amp;nbsp;일정하다.&lt;br /&gt;$$ P(q^t = j | q^{t-1} = i) = a_{ij}&amp;nbsp;&amp;nbsp;for all t $$&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 Markov Model의 기본 전제는 &lt;b&gt;현재&amp;nbsp;상태가&amp;nbsp;주어졌다면,&amp;nbsp;미래&amp;nbsp;상태는&amp;nbsp;과거&amp;nbsp;상태에&amp;nbsp;독립적&lt;/b&gt;이다를 상정하고 시작한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ P(X_{t+1} | X_t, X_{t-1}, ..., X_1) = P(X_{t+1} | X_t) $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이&amp;nbsp;성질&amp;nbsp;덕분에&amp;nbsp;상태&amp;nbsp;전이가&amp;nbsp;간단한&amp;nbsp;확률표&amp;nbsp;또는&amp;nbsp;행렬로&amp;nbsp;표현&amp;nbsp;가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마르코프 모델은 시간의 흐름을 따라 확률이 진화하는 모델인 &lt;b&gt;연속 마르코프 체인(Continuous-Time Markov Chain)&lt;/b&gt;과 이산 상태공간을 갖는 &lt;b&gt;이산 마르코프 체인(Discrete Markov Chain)&lt;/b&gt;이 있다. 연속 마르코프 모델은 확률 미분 방정식 기반으로 동작하여 비교적 수학적으로 난이도가 높으며, 직관적으로 이해하기 어렵기도 하다. 또한 HMM(Hidden Markov Model), MDP(Markov Decision Process) 등 실전 모델은 이산 마르코프 체인을 기반으로 확장되므로 &lt;b&gt;이산 마르코프 체인&lt;/b&gt;을&amp;nbsp;이용하여&amp;nbsp;예제를&amp;nbsp;하나&amp;nbsp;만들어보자.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전체 모델은 다음 세 가지 요소 \( (S, a, \pi) \)로 정의된다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;상태 집합: \( S = {s_1, s_2, ..., s_N} \)&lt;/li&gt;
&lt;li&gt;초기 상태: \( \pi = n \times 1 \text{ initial state probability vector } \left( \sum_i \pi_i = 1 \right) \)&lt;/li&gt;
&lt;li&gt;전이 확률 행렬: \( a = n \times n \text{ state transition probability matrix } \left( \sum_j a_{ij} = 1 \text{ for all } i \right) \)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 상태 집합 \( S \)가 고정된 구조라면 \( \text{Markov Model}\,\theta = (\pi, a) \)로 표현할 수 있다. 여기서 고정된 구조는 모델이 시작되기 전에 상태 목록을 우리가 알고 있고, 그 목록이 바뀌지 않는다는 것을 의미한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;항아리에 다음과 같이 공 3가지가 들어있다고 가정하자. &quot;RED&quot;, &quot;GREEN&quot;, &quot;BLUE&quot; 중 하나라고 하자. 각 날씨에 따라 다음 날로의 전이 확률을 다음과 같이 설정할 수 있다. 이 때, 맑은, 흐림, 비 와 같이 상태 집합은 고정되어 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;708&quot; data-origin-height=&quot;424&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/baiBET/btsNL9N6cO5/Rdbgg7bBniUIUtKkAQiSI1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/baiBET/btsNL9N6cO5/Rdbgg7bBniUIUtKkAQiSI1/img.png&quot; data-alt=&quot;Fig 1. HMM의 상태 전이 다이어그램&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/baiBET/btsNL9N6cO5/Rdbgg7bBniUIUtKkAQiSI1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbaiBET%2FbtsNL9N6cO5%2FRdbgg7bBniUIUtKkAQiSI1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;559&quot; height=&quot;335&quot; data-origin-width=&quot;708&quot; data-origin-height=&quot;424&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Fig 1. HMM의 상태 전이 다이어그램&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;예시 상태 행렬(\( \pi \))&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;\( \pi \) = \( [s_1, s_2, s_3] \) = [0.5, 0.2, 0.3] = [&lt;span style=&quot;color: #ee2323;&quot;&gt;RED&lt;/span&gt;, &lt;span style=&quot;color: #409d00;&quot;&gt;GREEN&lt;/span&gt;, &lt;span style=&quot;color: #006dd7;&quot;&gt;BLUE&lt;/span&gt;]&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;b&gt;예시 전이 행렬(a)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;\[ &lt;br /&gt;a&amp;nbsp;= &lt;br /&gt;\begin{bmatrix} &lt;br /&gt;\textcolor{red}{0.4}&amp;nbsp;&amp;amp;&amp;nbsp;\textcolor{green}{0.3}&amp;nbsp;&amp;amp;&amp;nbsp;\textcolor{blue}{0.3}&amp;nbsp;\\ &lt;br /&gt;\textcolor{red}{0.2}&amp;nbsp;&amp;amp;&amp;nbsp;\textcolor{green}{0.6}&amp;nbsp;&amp;amp;&amp;nbsp;\textcolor{blue}{0.2}&amp;nbsp;\\ &lt;br /&gt;\textcolor{red}{0.1}&amp;nbsp;&amp;amp;&amp;nbsp;\textcolor{green}{0.1}&amp;nbsp;&amp;amp;&amp;nbsp;\textcolor{blue}{0.8} &lt;br /&gt;\end{bmatrix} &lt;br /&gt;\]&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 현재가 RED일 때 다음 선택이 다시 RED일 확률이 40%임을 의미한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Hidden Markov Model(HMM)&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서&amp;nbsp;설명한&amp;nbsp;Markov&amp;nbsp;Model은&amp;nbsp;모든&amp;nbsp;상태가&amp;nbsp;관측&amp;nbsp;가능하다는&amp;nbsp;가정&amp;nbsp;하에&amp;nbsp;작동한다.&amp;nbsp;즉,&amp;nbsp;시스템의&amp;nbsp;상태&amp;nbsp;전이가&amp;nbsp;시간에&amp;nbsp;따라&amp;nbsp;확률적으로&amp;nbsp;이루어지며,&amp;nbsp;그&amp;nbsp;상태를&amp;nbsp;직접&amp;nbsp;관찰할&amp;nbsp;수&amp;nbsp;있다고&amp;nbsp;본다. &lt;br /&gt;&lt;br /&gt;그러나 현실 세계의 많은 문제에서는, 실제 상태(state)는 직접 관측할 수 없고, 대신 그 상태에서 발생한 관측 결과(observation)만을 알 수 있는 경우가 많다. 예를 들어, 사람의 감정 상태는 직접 보이지 않지만 음성, 표정, 언어 등을 통해 간접적으로 관측할 수 있다. 따라서 HMM의 가장 핵심적인 특징은 상태(state)가 직접 관측되지 않는다는 것이다. 대신, 각 상태에서 확률적으로 관측값(observation)이 발생한다. 이 때, 상태 시퀀스는 숨겨져 있으며 \( q^{1:T} = (q^1, q^2, \dots, q^T) \)로 표현된다. HMM의 Property는 다음과 같이 정리할 수 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;First-order dependency: 어떤&amp;nbsp;시점의&amp;nbsp;상태는&amp;nbsp;직전&amp;nbsp;시점의&amp;nbsp;상태에만&amp;nbsp;의존하며,&amp;nbsp;그&amp;nbsp;이전의&amp;nbsp;상태에는&amp;nbsp;독립적이다.&lt;br /&gt;$$ P(q^t = j | q^{t-1} = i, q^{t-2}, ...) = P(q^t = j | q^{t-1} = i) = a_{ij} $$&lt;/li&gt;
&lt;li&gt;Time-invariance: 시불변성은 상태&amp;nbsp;전이&amp;nbsp;확률은&amp;nbsp;시간&amp;nbsp;\(&amp;nbsp;t&amp;nbsp;\)에&amp;nbsp;의존하지&amp;nbsp;않는다.&amp;nbsp;즉,&amp;nbsp;상태&amp;nbsp;간의&amp;nbsp;전이&amp;nbsp;확률은&amp;nbsp;항상&amp;nbsp;일정하다.&lt;br /&gt;$$ P(q^t = j | q^{t-1} = i) = a_{ij}&amp;nbsp;&amp;nbsp;for all t $$&lt;/li&gt;
&lt;li&gt;주어진 \( \text{Observation sequence: } x^{1:T} = (x^1, x^2, \dots, x^T) \)에 따라 Unobservable state sequence가 도출됨.&lt;br /&gt;$$ \text{Unobservable state sequence: } q^{1:T} = (q^1, q^2, \dots, q^T) $$&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이처럼 상태는 숨겨져(hidden) 있고, 우리는 &lt;b&gt;초기 상태 확률, 상태 전이 확률, 관측 확률(Observation Probability)&lt;/b&gt;를 바탕으로 숨겨진 상태의 시퀀스를 추정해야 한다. 이러한 구조를 가진 모델이 바로 &lt;b&gt;Hidden Markov Model(HMM)&lt;/b&gt;이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음은 HMM의 요소들을 가지고 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;상태 집합: \( S = {s_1, s_2, ..., s_N} \)&lt;/li&gt;
&lt;li&gt;초기 상태: \( \pi = n \times 1 \text{ initial state probability vector } \left( \sum_i \pi_i = 1 \right) \)&lt;/li&gt;
&lt;li&gt;전이 확률 행렬: \( a = n \times n \text{ state transition probability matrix } \left( \sum_j a_{ij} = 1 \text{ for all } i \right) \)&lt;/li&gt;
&lt;li&gt;관측 확률 행렬: \( b = n \times v \text{ observation(emission) probability matrix } \left( \sum_j b_{ij} = 1 \text{ for all } i \right) \)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 상태 집합 \( S \)가 고정된 구조일 때, \( \text{HMM}\,\theta = (\pi, a, b) \)과 같이 표현될 수 있다. 위와 동일한 수치를 이용하여 예시를 다음과 같이 만들어보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;예시 상태 행렬(\( \pi \))&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;\(&amp;nbsp;\pi&amp;nbsp;\)&amp;nbsp;=&amp;nbsp;\(&amp;nbsp;[s_1,&amp;nbsp;s_2,&amp;nbsp;s_3]&amp;nbsp;\)&amp;nbsp;=&amp;nbsp;[&lt;span style=&quot;color: #ee2323;&quot;&gt;0.5&lt;/span&gt;,&lt;span style=&quot;color: #409d00;&quot;&gt;&amp;nbsp;0.2&lt;/span&gt;,&amp;nbsp;&lt;span style=&quot;color: #006dd7;&quot;&gt;0.3&lt;/span&gt;]&amp;nbsp;=&amp;nbsp;[&lt;span style=&quot;color: #ee2323;&quot;&gt;RED&lt;/span&gt;,&amp;nbsp;&lt;span style=&quot;color: #409d00;&quot;&gt;GREEN&lt;/span&gt;,&amp;nbsp;&lt;span style=&quot;color: #006dd7;&quot;&gt;BLUE&lt;/span&gt;]&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;b&gt;예시 전이 행렬(a)&lt;/b&gt;&lt;br /&gt;\[ &lt;br /&gt;a&amp;nbsp;= &lt;br /&gt;\begin{bmatrix} &lt;br /&gt;{0.4} &amp;amp; {0.3} &amp;amp; {0.3} \\ &lt;br /&gt;{0.2} &amp;amp; {0.6} &amp;amp; {0.2} \\ &lt;br /&gt;{0.1} &amp;amp; {0.1} &amp;amp; {0.8} &lt;br /&gt;\end{bmatrix} &lt;br /&gt;\]&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;예시 관측 행렬(b)&lt;/b&gt;&lt;br /&gt;\[ &lt;br /&gt;b&amp;nbsp;= &lt;br /&gt;\begin{bmatrix} &lt;br /&gt;\textcolor{red}{0.2} &amp;amp; \textcolor{green}{0.3} &amp;amp; \textcolor{blue}{0.5} \\ &lt;br /&gt;\textcolor{red}{0.3} &amp;amp; \textcolor{green}{0.4} &amp;amp; \textcolor{blue}{0.3} \\ &lt;br /&gt;\textcolor{red}{0.6} &amp;amp; \textcolor{green}{0.3} &amp;amp; \textcolor{blue}{0.1} &lt;br /&gt;\end{bmatrix} &lt;br /&gt;\]&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 때, 다음과 같은 상황이 있다고 가정하자.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;관측 시퀀스: \( x^{1:4} = [\text{red}, \text{red}, \text{blue}, \text{blue}] \) &lt;br /&gt;&amp;rarr; b 행렬: 상태가 색깔을 방출할 확률로 색깔과 직접 관련이 있음&lt;/li&gt;
&lt;li&gt;숨겨진 상태 시퀀스: \( q^{1:4} = [s_1, s_2, s_2, s_3] \)&lt;br /&gt;&amp;rarr; a행렬: 상태 간 이동 확률이므로 색깔과 무관함.&lt;/li&gt;
&lt;li&gt;공동 확률: \( P(x^{1:4}, q^{1:4} \mid \theta) \)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제를 계산하면 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;\[ &lt;br /&gt;\begin{aligned} &lt;br /&gt;P(x^{1:4},&amp;nbsp;q^{1:4}&amp;nbsp;\mid&amp;nbsp;\theta)&amp;nbsp; &lt;br /&gt;&amp;amp;=&amp;nbsp;\pi_1&amp;nbsp;\cdot&amp;nbsp;b_{11}&amp;nbsp;\cdot&amp;nbsp;a_{12}&amp;nbsp;\cdot&amp;nbsp;b_{21}&amp;nbsp;\cdot&amp;nbsp;a_{22}&amp;nbsp;\cdot&amp;nbsp;b_{23}&amp;nbsp;\cdot&amp;nbsp;a_{23}&amp;nbsp;\cdot&amp;nbsp;b_{33}&amp;nbsp;\\ &lt;br /&gt;&amp;amp;=&amp;nbsp;0.5&amp;nbsp;\times&amp;nbsp;0.2&amp;nbsp;\times&amp;nbsp;0.3&amp;nbsp;\times&amp;nbsp;0.3&amp;nbsp;\times&amp;nbsp;0.6&amp;nbsp;\times&amp;nbsp;0.3&amp;nbsp;\times&amp;nbsp;0.2&amp;nbsp;\times&amp;nbsp;0.1&amp;nbsp;\\ &lt;br /&gt;&amp;amp;=&amp;nbsp;0.0000324 &lt;br /&gt;\end{aligned} &lt;br /&gt;\]&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 예시는 관측 시퀀스와 상태 시퀀스가 주어졌을 때&amp;nbsp;&lt;b&gt;Joint Likelihood&lt;/b&gt;를 계산하는 방식이며, 전체 \( P(x∣\theta) \)는 모든 가능한 상태 시퀀스에 대해 위 값을 합산해야 하므로 일반적으로 Forward 알고리즘을 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Likelihood는 관측된 데이터를 바탕으로, 어떤 모델이 그 데이터를 설명하는 데 가장 적합한지를 판단하기 위해 사용한다. HMM은 이 최대 Likelihood를 찾아 나가는 방향으로 진행된다. 이 과정은 Forward, Backward, Baum-Welch Algorithm을 사용하여 최종 결과까지 도출한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt; Forward Algorithm&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Forward Algorithm&lt;/b&gt;은 시간 \( t \)까지의 부분 관측 시퀀스를 고려하여, 상태 \( s_j \)에 있을 누적 확률을 재귀적으로 계산한다. 즉, 시간&amp;nbsp;\(&amp;nbsp;t&amp;nbsp;\)까지의&amp;nbsp;관측&amp;nbsp;시퀀스&amp;nbsp;\(&amp;nbsp;x^1,&amp;nbsp;x^2,&amp;nbsp;...,&amp;nbsp;x^t&amp;nbsp;\)를&amp;nbsp;생성하고,&amp;nbsp;그&amp;nbsp;시점에&amp;nbsp;상태&amp;nbsp;\(j&amp;nbsp;\)에&amp;nbsp;있을&amp;nbsp;확률이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1561&quot; data-origin-height=&quot;651&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/8IhvJ/btsNLDB4cMp/S7BEUIYxJhD2Q9JqMA3dz0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/8IhvJ/btsNLDB4cMp/S7BEUIYxJhD2Q9JqMA3dz0/img.png&quot; data-alt=&quot;Fig 2. Forward Algorithm&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/8IhvJ/btsNLDB4cMp/S7BEUIYxJhD2Q9JqMA3dz0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F8IhvJ%2FbtsNLDB4cMp%2FS7BEUIYxJhD2Q9JqMA3dz0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;661&quot; height=&quot;276&quot; data-origin-width=&quot;1561&quot; data-origin-height=&quot;651&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Fig 2. Forward Algorithm&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ \alpha_t(j) \equiv P(x^1, x^2, \cdots, x^t, q^t = j \mid \theta) = \sum_{q^{1:t-1}} P(x^1, x^2, \cdots, x^t, q^1, q^2, \cdots, q^{t-1}, q^t = j \mid \theta) $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Backward Algorithm&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Backward Algorithm&lt;/b&gt;은 Forward와 반대로, 현재 상태에서 남은 관측 시퀀스를 생성할 확률을 계산한다. 또한 Baum-Welch 알고리즘에서 사용된다. 즉, 상태&amp;nbsp;\(&amp;nbsp;i&amp;nbsp;\)에서&amp;nbsp;시작하여,&amp;nbsp;시간&amp;nbsp;\(&amp;nbsp;t+1&amp;nbsp;\)부터&amp;nbsp;\(&amp;nbsp;T&amp;nbsp;\)까지의&amp;nbsp;&amp;nbsp;관측값을&amp;nbsp;생성할&amp;nbsp;모든&amp;nbsp;상태&amp;nbsp;경로에&amp;nbsp;대한&amp;nbsp;총합이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1589&quot; data-origin-height=&quot;665&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ckBuFP/btsNLCwnMnC/lYE6gLnWyjgiqU2WsPBvD1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ckBuFP/btsNLCwnMnC/lYE6gLnWyjgiqU2WsPBvD1/img.png&quot; data-alt=&quot;Fig 3. Backward Algorithm&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ckBuFP/btsNLCwnMnC/lYE6gLnWyjgiqU2WsPBvD1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FckBuFP%2FbtsNLCwnMnC%2FlYE6gLnWyjgiqU2WsPBvD1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;645&quot; height=&quot;270&quot; data-origin-width=&quot;1589&quot; data-origin-height=&quot;665&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Fig 3. Backward Algorithm&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ \beta_t(i) \equiv P(x^{t+1}, x^{t+2}, \cdots, x^T \mid q^t = i, \theta) = \sum_{q^{t+1:T}} P(x^{t+1}, \cdots, x^T, q^{t+1}, \cdots, q^T \mid q^t = i, \theta)&amp;nbsp; $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Baum-Welch Algorithm&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Baum-Welch Algorithm&lt;/b&gt;은&amp;nbsp;Maximum&amp;nbsp;Likelihood&amp;nbsp;Estimation(MLE)을&amp;nbsp;수행하는&amp;nbsp;알고리즘이다.&amp;nbsp;&lt;b&gt;Expectation-Maximization(EM)&lt;/b&gt; 알고리즘의 구조를 따른다. &lt;b&gt;Expectation Step(E-Step)&lt;/b&gt;과 &lt;b&gt;Maximization(M-Step)&lt;/b&gt;이 있는데 하나씩 알아보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Expectation&amp;nbsp;Step(E-Step)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 Expectation Step(E-Step)에선 &lt;b&gt;\( \gamma \)&lt;/b&gt;와 &lt;b&gt;\( \xi \)&lt;/b&gt;가 도출된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ Q(\hat{\theta} \mid \theta) \equiv \mathbb{E}_{q^{1:T}} \left[ \log P(x^{1:T}, q^{1:T} \mid \hat{\theta}) \mid x^{1:T}, \theta \right] $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 Q는 Baum&amp;rsquo;s   function인데 간단하게 설명하자면 숨겨진 정보를 고려해 파라미터를 개선하는 핵심 수식이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ Q(\hat{\theta} \mid \theta) = \mathbb{E}_{q^{1:T} \sim P(q^{1:T} \mid x^{1:T}, \theta)} \left[ \log P(x^{1:T}, q^{1:T} \mid \hat{\theta}) \right] $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;상태 점유 확률(\( \gamma_t(i) \))&amp;nbsp;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;\( \gamma_t(i) \)는 &lt;b&gt;시점 \( t \)에서 상태 \( i \)에 있을 사후 확률(posterior probability)&lt;/b&gt;을 도출한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1550&quot; data-origin-height=&quot;641&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bdlyUM/btsNJ2C7QOK/YGMdzdKkbJkplVgKeMKdkk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bdlyUM/btsNJ2C7QOK/YGMdzdKkbJkplVgKeMKdkk/img.png&quot; data-alt=&quot;Fig 4. Baum-Welch Algorithm 1&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bdlyUM/btsNJ2C7QOK/YGMdzdKkbJkplVgKeMKdkk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbdlyUM%2FbtsNJ2C7QOK%2FYGMdzdKkbJkplVgKeMKdkk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;676&quot; height=&quot;280&quot; data-origin-width=&quot;1550&quot; data-origin-height=&quot;641&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Fig 4. Baum-Welch Algorithm 1&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ \gamma_t(i) \equiv P(q^t = i \mid x^1, x^2, \cdots, x^T, \theta) $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ \gamma_t(i) = \frac{\alpha_t(i) \cdot \beta_t(i)}{\sum_i \alpha_t(i) \cdot \beta_t(i)} $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;상태 전이 점유 확률(\( \xi_t(i, j) \))&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;\( \xi_t(i, j) \)는 시간 \( t \)에 상태 \( i \)에 있고, &lt;b&gt;시간 \( t+1 \)에 상태 \( j \)로 전이(arc)되었을 사후 확률(posterior probability)&lt;/b&gt;을 나타낸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1488&quot; data-origin-height=&quot;617&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bx2fv5/btsNKd5trp1/8D1g3MlmW15SRsNomNb1O0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bx2fv5/btsNKd5trp1/8D1g3MlmW15SRsNomNb1O0/img.png&quot; data-alt=&quot;Fig 4. Baum-Welch Algorithm 2&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bx2fv5/btsNKd5trp1/8D1g3MlmW15SRsNomNb1O0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbx2fv5%2FbtsNKd5trp1%2F8D1g3MlmW15SRsNomNb1O0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;653&quot; height=&quot;271&quot; data-origin-width=&quot;1488&quot; data-origin-height=&quot;617&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Fig 4. Baum-Welch Algorithm 2&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ \xi_t(i, j) \equiv P(q^t = i, q^{t+1} = j \mid x^1, x^2, \cdots, x^T, \theta) $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ \xi_t(i, j) = \frac{\alpha_t(i) \cdot a_{ij} \cdot b_j(x^{t+1}) \cdot \beta_{t+1}(j)}{\sum_i \sum_j \alpha_t(i) \cdot a_{ij} \cdot b_j(x^{t+1}) \cdot \beta_{t+1}(j)} $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Maximization(M-Step)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 계산한 \( \gamma \)와 \( \xi \)를 바탕으로 파라미터를 업데이트한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ \hat{\theta} = \arg\max_{\hat{\theta}} Q(\hat{\theta} \mid \theta) $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;상태&amp;nbsp;점유&amp;nbsp;확률(\(&amp;nbsp;\gamma_t(i)&amp;nbsp;\))&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ \gamma_t(i) = \frac{\alpha_t(i) \cdot \beta_t(i)}{\sum_{k=1}^N \alpha_t(k) \cdot \beta_t(k)} $$&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;br /&gt;&lt;b&gt;상태&amp;nbsp;전이&amp;nbsp;점유&amp;nbsp;확률(\(&amp;nbsp;\xi_t(i,&amp;nbsp;j)&amp;nbsp;\)) &lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ \xi_t(i, j) = \frac{\alpha_t(i) \cdot a_{ij} \cdot b_j(x^{t+1}) \cdot \beta_{t+1}(j)}{\sum_{i=1}^N \sum_{j=1}^N \alpha_t(i) \cdot a_{ij} \cdot b_j(x^{t+1}) \cdot \beta_{t+1}(j)} $$&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;br /&gt;&lt;b&gt;초기&amp;nbsp;상태&amp;nbsp;확률&amp;nbsp;업데이트 &lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ \pi_i^{\text{new}} = \gamma_1(i) $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;전이 확률 업데이트 &lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$&amp;nbsp;a_{ij}^{\text{new}}&amp;nbsp;=&amp;nbsp;\frac{\sum_{t=1}^{T-1}&amp;nbsp;\xi_t(i,&amp;nbsp;j)}{\sum_{t=1}^{T-1}&amp;nbsp;\gamma_t(i)}&amp;nbsp;$$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;출력(관측)&amp;nbsp;확률&amp;nbsp;업데이트&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$&amp;nbsp;b_j^{\text{new}}(k)&amp;nbsp;=&amp;nbsp;\frac{\sum_{t=1}^T&amp;nbsp;\delta(x^t&amp;nbsp;=&amp;nbsp;v_k)&amp;nbsp;\cdot&amp;nbsp;\gamma_t(j)}{\sum_{t=1}^T&amp;nbsp;\gamma_t(j)}&amp;nbsp;$$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정리하자면 Baum-Welch 알고리즘은 반복적인 알고리즘으로, 현재 모델 파라미터 \( \theta \)를 기준으로 상태 점유 확률을 추정하는 E-step과 추정된&amp;nbsp;정보를&amp;nbsp;바탕으로&amp;nbsp;파라미터를&amp;nbsp;최적화하는&amp;nbsp;M-step을&amp;nbsp;번갈아&amp;nbsp;수행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;관련 포스팅&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://aliencoder.tistory.com/21&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;2021.12.29 - [Data Science/Data Analysis] - [Python] 확률(Probability)과 우도(가능도, Likelihood) 그리고 확률 밀도 함수(probability density function, PDF)&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://aliencoder.tistory.com/23&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;2022.01.10 - [Data Science/Data Analysis] - [Python] 최대 우도(가능도) 추정(Maximum Likelihood Estimation, MLE)과 우도 함수(Likelihood Function)&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;참고 자료&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;고려대학교 육동석 교수님 [AAA605] 음성정보처리 강의자료&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://incodom.kr/%EA%B8%B0%EA%B3%84%ED%95%99%EC%8A%B5/markov_model&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://incodom.kr/%EA%B8%B0%EA%B3%84%ED%95%99%EC%8A%B5/markov_model&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Markov_model&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://en.wikipedia.org/wiki/Markov_model&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://ko.wikipedia.org/wiki/%EC%9D%80%EB%8B%89_%EB%A7%88%EB%A5%B4%EC%BD%94%ED%94%84_%EB%AA%A8%ED%98%95&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://ko.wikipedia.org/wiki/%EC%9D%80%EB%8B%89_%EB%A7%88%EB%A5%B4%EC%BD%94%ED%94%84_%EB%AA%A8%ED%98%95&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>AI/Data Analysis</category>
      <category>backward algorithm</category>
      <category>baum-welch algorithm</category>
      <category>discrete markov chain</category>
      <category>forward algorithm</category>
      <category>Hidden Markov Model</category>
      <category>hmm</category>
      <category>Markov Model</category>
      <category>상태</category>
      <category>전이 확률</category>
      <category>초기 확률</category>
      <author>AlienCoder</author>
      <guid isPermaLink="true">https://aliencoder.tistory.com/224</guid>
      <comments>https://aliencoder.tistory.com/224#entry224comment</comments>
      <pubDate>Sun, 4 May 2025 17:10:40 +0900</pubDate>
    </item>
    <item>
      <title>Softmax + Cross Entropy 미분</title>
      <link>https://aliencoder.tistory.com/223</link>
      <description>&lt;script src=&quot;https://polyfill.io/v3/polyfill.min.js?features=es6&quot;&gt;&lt;/script&gt;
&lt;script src=&quot;https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js&quot;&gt;&lt;/script&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적으로 &lt;b&gt;Softmax&lt;/b&gt; 함수는 일반적으로 모델의 최종 출력층(final layer) 에서 사용된다. Softmax를 통해 출력된 값은 각 클래스에 대한 확률 분포로 해석할 수 있으며, 이를 기반으로 &lt;b&gt;Cross Entropy Loss&lt;/b&gt;를 계산하여 ground truth와 예측 결과 간의 차이를 측정한다. 이때, &lt;b&gt;Backpropagation&lt;/b&gt; 과정에서 Softmax와 Cross-Entropy에 대한 Chain Rule을 적용하여, Loss를 입력(logit)에 대해 미분함으로써 역전파를 시작한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1171&quot; data-origin-height=&quot;834&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cLHwqT/btsNAghuFo1/IHzUCDPfZBzoQLsCFGwmZ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cLHwqT/btsNAghuFo1/IHzUCDPfZBzoQLsCFGwmZ0/img.png&quot; data-alt=&quot;Fig 1. Softmax + Cross Entropy&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cLHwqT/btsNAghuFo1/IHzUCDPfZBzoQLsCFGwmZ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcLHwqT%2FbtsNAghuFo1%2FIHzUCDPfZBzoQLsCFGwmZ0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;565&quot; height=&quot;402&quot; data-origin-width=&quot;1171&quot; data-origin-height=&quot;834&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Fig 1. Softmax + Cross Entropy&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리는 &lt;b&gt;Softmax&lt;/b&gt;와 &lt;b&gt;Cross Entropy&lt;/b&gt;를 각각 따로 처리하는 대신 결합하여 하나의 연산처럼 다루는 것이 일반적이다. 이렇게 하면 수치적으로 더 안정적이며, 계산이 간편해진다. 특히, Softmax의 출력이 0에 가까운 경우 \( \log{p_i} \)를 계산할 때 발생할 수 있는 언더플로우 문제를 방지할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 최종적으로 다음과 같이 표현된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ \frac{\partial \mathcal{L}}{\partial z_i} = \tilde{y}_t^{(i)} - \hat{y}_t^{(i)} $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$&amp;nbsp;\frac{\partial&amp;nbsp;\mathcal{L}}{\partial&amp;nbsp;\mathbf{h}_t^{\langle&amp;nbsp;\text{logit}&amp;nbsp;\rangle&amp;nbsp;(i)}}&amp;nbsp;=&amp;nbsp;-\frac{1}{I}&amp;nbsp;\sum_{i=0}^{I-1}&amp;nbsp;\left(&amp;nbsp;\tilde{y}_t^{(i)}&amp;nbsp;-&amp;nbsp;\hat{y}_t^{(i)}&amp;nbsp;\right)&amp;nbsp;$$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 여기서 사용되는 Softmax와 Cross Entropy에 대해 기본적인 정보만 알고 넘어가자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Softmax&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Softmax&lt;/b&gt; 함수는 다중 클래스 분류 문제에서 각 클래스에 대한 확률 분포를 구하는 데 사용된다. 주어진 로짓 벡터 \( \mathbf{z} = (z_1, z_2, \dots, z_C) \)에 대해, Softmax는 각 요소를 다음과 같이 변환한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ \text{softmax}(z_i) = \frac{\exp(z_i)}{\sum_{j=1}^{C} \exp(z_j)} $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 \( C \)는 클래스의 수를 의미한다. Softmax&amp;nbsp;함수는&amp;nbsp;입력값을&amp;nbsp;0과&amp;nbsp;1&amp;nbsp;사이의&amp;nbsp;값으로&amp;nbsp;정규화하며,&amp;nbsp;전체&amp;nbsp;합이&amp;nbsp;1이&amp;nbsp;되도록&amp;nbsp;한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Cross&amp;nbsp;Entropy&amp;nbsp;Loss&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Cross&amp;nbsp;Entropy&amp;nbsp;Loss&lt;/b&gt;는&amp;nbsp;예측&amp;nbsp;확률&amp;nbsp;분포와&amp;nbsp;실제&amp;nbsp;정답&amp;nbsp;분포&amp;nbsp;간의&amp;nbsp;차이를&amp;nbsp;측정하는&amp;nbsp;함수이다. &lt;br /&gt;모델의 출력 확률 벡터 \( p \)와 정답 분포 \( y \)에 대해 Cross Entropy Loss는 다음과 같이 정의된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ L = - \sum^{C-1}_{i=0} y_i \log{\hat{y_i}} $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 \( y_i \) 는 정답 라벨의 one-hot 인코딩 값이며, \( \hat{y_i} \)는 Softmax를 거친 예측 확률이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;증명&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 다음 수식이 도출되는 과정을 순서대로 전개해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$&amp;nbsp;\frac{\partial&amp;nbsp;\mathcal{L}}{\partial&amp;nbsp;z_i}&amp;nbsp;=&amp;nbsp;\tilde{y}_t^{(i)}&amp;nbsp;-&amp;nbsp;\hat{y}_t^{(i)}&amp;nbsp;$$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1. Softmax&amp;nbsp;정의&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Softmax&lt;/b&gt; 함수는 다음과 같이 정의할 수 있다. &lt;br /&gt;$$ \hat{y}_t^{(i)} = \frac{e^{h_t^{(i)}}}{\sum_j e^{h_t^{(j)}}} $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서&amp;nbsp;\(&amp;nbsp;h_t^{(i)}&amp;nbsp;\)는&amp;nbsp;logit이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2. Cross Entropy Loss 정의&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Cross-Entropy Loss \(\mathcal{L}\)는 다음과 같다.&lt;br /&gt;$$ \mathcal{L} = -\sum_{i} \tilde{y}_t^{(i)} \log \hat{y}_t^{(i)} $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 \(\tilde{y}_t^{(i)}\)는 ground truth 분포를 나타낸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3. Loss를 logit \( h_t^{(i)} \)에 대해 미분&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Chain Rule을 적용하면 다음과 같다.&lt;br /&gt;\[ &lt;br /&gt;\frac{\partial&amp;nbsp;\mathcal{L}}{\partial&amp;nbsp;h_t^{(i)}}&amp;nbsp;=&amp;nbsp;\sum_k&amp;nbsp;\frac{\partial&amp;nbsp;\mathcal{L}}{\partial&amp;nbsp;\hat{y}_t^{(k)}}&amp;nbsp;\frac{\partial&amp;nbsp;\hat{y}_t^{(k)}}{\partial&amp;nbsp;h_t^{(i)}} &lt;br /&gt;\]&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저, Loss를 softmax 출력 \(\hat{y}_t^{(k)}\)에 대해 미분하면 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;\[ &lt;br /&gt;\frac{\partial&amp;nbsp;\mathcal{L}}{\partial&amp;nbsp;\hat{y}_t^{(k)}}&amp;nbsp;=&amp;nbsp;-\frac{\tilde{y}_t^{(k)}}{\hat{y}_t^{(k)}} &lt;br /&gt;\]&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Softmax를 logit에 대해 미분하면,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;\[ &lt;br /&gt;\frac{\partial&amp;nbsp;\hat{y}_t^{(k)}}{\partial&amp;nbsp;h_t^{(i)}}&amp;nbsp;= &lt;br /&gt;\begin{cases} &lt;br /&gt;\hat{y}_t^{(i)}&amp;nbsp;(1&amp;nbsp;-&amp;nbsp;\hat{y}_t^{(i)})&amp;nbsp;&amp;amp;&amp;nbsp;\text{if&amp;nbsp;}&amp;nbsp;i&amp;nbsp;=&amp;nbsp;k&amp;nbsp;\\ &lt;br /&gt;-\hat{y}_t^{(i)}&amp;nbsp;\hat{y}_t^{(k)}&amp;nbsp;&amp;amp;&amp;nbsp;\text{if&amp;nbsp;}&amp;nbsp;i&amp;nbsp;\neq&amp;nbsp;k &lt;br /&gt;\end{cases} &lt;br /&gt;\]&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;4.&amp;nbsp;Chain&amp;nbsp;Rule&amp;nbsp;적용&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;\[ &lt;br /&gt;\frac{\partial&amp;nbsp;\mathcal{L}}{\partial&amp;nbsp;h_t^{(i)}}&amp;nbsp;=&amp;nbsp;\sum_k&amp;nbsp;\left(&amp;nbsp;-\frac{\tilde{y}_t^{(k)}}{\hat{y}_t^{(k)}}&amp;nbsp;\right)&amp;nbsp;\frac{\partial&amp;nbsp;\hat{y}_t^{(k)}}{\partial&amp;nbsp;h_t^{(i)}} &lt;br /&gt;\]&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 두 부분으로 나누어 계산한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Case 1. \(k = i\) 인 경우&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;\[ &lt;br /&gt;-\frac{\tilde{y}_t^{(i)}}{\hat{y}_t^{(i)}}&amp;nbsp;\cdot&amp;nbsp;\hat{y}_t^{(i)}&amp;nbsp;(1&amp;nbsp;-&amp;nbsp;\hat{y}_t^{(i)})&amp;nbsp;=&amp;nbsp;-\tilde{y}_t^{(i)}&amp;nbsp;(1&amp;nbsp;-&amp;nbsp;\hat{y}_t^{(i)}) &lt;br /&gt;\]&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Case 2. \(k \neq i\) 인 경우&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;\[ &lt;br /&gt;\sum_{k&amp;nbsp;\neq&amp;nbsp;i}&amp;nbsp;\left(&amp;nbsp;-\frac{\tilde{y}_t^{(k)}}{\hat{y}_t^{(k)}}&amp;nbsp;\right)&amp;nbsp;\left(&amp;nbsp;-\hat{y}_t^{(i)}&amp;nbsp;\hat{y}_t^{(k)}&amp;nbsp;\right)&amp;nbsp;=&amp;nbsp;\hat{y}_t^{(i)}&amp;nbsp;\sum_{k&amp;nbsp;\neq&amp;nbsp;i}&amp;nbsp;\tilde{y}_t^{(k)} &lt;br /&gt;\]&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;5. 합치기&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;과정 4에서 도출된 두 식을 합치면 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ \frac{\partial \mathcal{L}}{\partial h_t^{(i)}} = -\tilde{y}_t^{(i)} (1 - \hat{y}_t^{(i)}) + \hat{y}_t^{(i)} \sum_{k \neq i} \tilde{y}_t^{(k)} $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 풀어서 전개하면 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;\[ &lt;br /&gt;\begin{align*} &lt;br /&gt;\frac{\partial&amp;nbsp;\mathcal{L}}{\partial&amp;nbsp;h_t^{(i)}} &lt;br /&gt;&amp;amp;=&amp;nbsp;\left(&amp;nbsp;-\tilde{y}_t^{(i)}&amp;nbsp;+&amp;nbsp;\tilde{y}_t^{(i)}&amp;nbsp;\hat{y}_t^{(i)}&amp;nbsp;\right)&amp;nbsp;+&amp;nbsp;\left(&amp;nbsp;\hat{y}_t^{(i)}&amp;nbsp;\sum_{k&amp;nbsp;\neq&amp;nbsp;i}&amp;nbsp;\tilde{y}_t^{(k)}&amp;nbsp;\right)&amp;nbsp;\\ &lt;br /&gt;&amp;amp;=&amp;nbsp;-\tilde{y}_t^{(i)}&amp;nbsp;+&amp;nbsp;\hat{y}_t^{(i)}&amp;nbsp;\left(&amp;nbsp;\tilde{y}_t^{(i)}&amp;nbsp;+&amp;nbsp;\sum_{k&amp;nbsp;\neq&amp;nbsp;i}&amp;nbsp;\tilde{y}_t^{(k)}&amp;nbsp;\right)&amp;nbsp;\\ &lt;br /&gt;&amp;amp;=&amp;nbsp;-\tilde{y}_t^{(i)}&amp;nbsp;+&amp;nbsp;\hat{y}_t^{(i)}&amp;nbsp;\times&amp;nbsp;1&amp;nbsp;\\ &lt;br /&gt;&amp;amp;=&amp;nbsp;\hat{y}_t^{(i)}&amp;nbsp;-&amp;nbsp;\tilde{y}_t^{(i)} &lt;br /&gt;\end{align*} &lt;br /&gt;\]&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이므로 다시 간단하게 정리하면 다음과 같다.&lt;br /&gt;$$&amp;nbsp;\frac{\partial&amp;nbsp;\mathcal{L}}{\partial&amp;nbsp;h_t^{(i)}}&amp;nbsp;=&amp;nbsp;\hat{y}_t^{(i)}&amp;nbsp;-&amp;nbsp;\tilde{y}_t^{(i)}&amp;nbsp;$$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고로 \( \tilde{y} \)가 &lt;b&gt;확률 분포(probability distribution)&lt;/b&gt;이기 때문에 다음이 성립한다. &lt;br /&gt;$$ \sum_{k \neq i} \tilde{y}_t^{(k)}&amp;nbsp; + \tilde{y}_t^{(i)} = 1 $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;6. 최종 결론&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러므로 Softmax와 Cross-Entropy를 함께 사용할 때, logit에 대한 Loss의 gradient는 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ \boxed{\frac{\partial \mathcal{L}}{\partial h_t^{(i)}} = \hat{y}_t^{(i)} - \tilde{y}_t^{(i)}} $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;복잡해 보이는 미분과정이지만 결과적으론 아주 명료하고 깔끔하게 정리가 된다. 이를 토대로 Backpropagation 과정에서 다음 가중치의 update도 chain rule을 이용해 전개할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;관련 포스팅&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://aliencoder.tistory.com/129&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;2024.10.27 - [Data Science/ML &amp;amp; DL] - Softmax에 대한 고찰&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://aliencoder.tistory.com/222&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;2025.04.16 - [Data Science/Data Analysis] - Cross Entropy와 KL(Kullback&amp;ndash;Leibler) Divergence&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;참고 자료&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://levelup.gitconnected.com/killer-combo-softmax-and-cross-entropy-5907442f60ba&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://levelup.gitconnected.com/killer-combo-softmax-and-cross-entropy-5907442f60ba&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>AI/ML &amp;amp; DL</category>
      <category>backpropagation</category>
      <category>Chain Rule</category>
      <category>cross entropy</category>
      <category>softmax</category>
      <author>AlienCoder</author>
      <guid isPermaLink="true">https://aliencoder.tistory.com/223</guid>
      <comments>https://aliencoder.tistory.com/223#entry223comment</comments>
      <pubDate>Sun, 27 Apr 2025 15:00:09 +0900</pubDate>
    </item>
    <item>
      <title>Cross Entropy와 KL(Kullback&amp;ndash;Leibler) Divergence</title>
      <link>https://aliencoder.tistory.com/222</link>
      <description>&lt;script src=&quot;https://polyfill.io/v3/polyfill.min.js?features=es6&quot;&gt;&lt;/script&gt;
&lt;script src=&quot;https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js&quot;&gt;&lt;/script&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Cross Entropy&lt;/b&gt;와 &lt;b&gt;KL(Kullback&amp;ndash;Leibler) Divergence&lt;/b&gt;는 머신 러닝과 딥러닝 분야뿐만 아니라 여러 분야에서 사용된다. 특히 분류 문제나 확률 분포를 다루는 모델에서 자주 등장하는 개념이다. 두 개념 모두 &lt;b&gt;확률 분포 간의 차이(Divergence)&lt;/b&gt;를 측정하는 방법이라는 공통점이 있지만 목적과 수식 구조, 해석 관점에서는 차이가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 주요 개념들에 대해 살펴보기 전에 &lt;b&gt;정보량&lt;/b&gt;과 &lt;b&gt;Entropy&lt;/b&gt;가 무엇인지 알고 넘어가자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;정보량(Information&amp;nbsp;Quantity)&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;정보량(Information&amp;nbsp;Quantity)&lt;/b&gt;이라는 개념은 다소 추상적으로 느껴지지만, 실제로는 매우 논리적인 수식으로 설명이 가능하다. 정보량을 직관적 이해해 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어, 어떤 사람이 대학교에서 수업에 지각했는데, 그 지각한 학생에 대한 정보가 아래와 같이 두 가지 방식으로 제공되었다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;A: 그 학생은 &lt;b&gt;빨간 옷&lt;/b&gt;을 입고 있어.&lt;/li&gt;
&lt;li&gt;B: 그 학생은 &lt;b&gt;옷&lt;/b&gt;을 입고 있어.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;이때, 지각한 사람을 특정하는데 더 도움이 되는 정보는 A이다. 왜냐하면 청강생 대부분은 옷을 입고 있겠지만, &lt;b&gt;빨간 옷&lt;/b&gt;으로 특정하였기 때문이다. 따라서 &lt;b&gt;&quot;빨간 옷&quot;&lt;/b&gt;이라는 정보는 더 희귀하고, 더 많은 정보량을 가진다. &lt;br /&gt;&lt;br /&gt;이처럼 희귀한 사건일수록 정보량이 크고, 흔한 사건일수록 정보량은 작다. 정보량은 사건의 확률과 &lt;b&gt;반비례 관계&lt;/b&gt;를 갖는다. 이 정보량의 수학적 정의는 어떤 사건 \( x \)가 발생할 확률을 \( P(x) \)라고 할 때, 그 사건의 정보량 \( I(x) \)는 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ I(x) = - \log{P(x)} $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 식에서 로그의 밑은 2를 사용하는 것이 일반적이며, 이 경우 단위는 bit가 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예시를 가지고 와보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수업에는 오직 학생들이 &lt;b&gt;빨간 옷&lt;/b&gt;과 &lt;b&gt;파란 옷&lt;/b&gt;만 입고 있다고 하자. 만약 지각한 학생이 &lt;b&gt;빨간 옷&lt;/b&gt;일 확률이 10%라면 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ P(red) = \frac{1}{10} $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ I(red) = -\log_2{0.1} &amp;asymp; 3.321 \, bits $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약, 빨간 옷이 아니라면, &lt;b&gt;파란 옷&lt;/b&gt;일 것이며 확률은 당연히 90%가 된다. 정보량에 대한 결과는 다음과 같을 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ P(blue) = \frac{1}{90} $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ I(blue) = -\log_2{0.9} &amp;asymp; 0.152 \, bits $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정보량은 사건의 확률과 반비례 관계이므로 더 낮은 확률로 발생하는 정보량이 더 커지는 것을 확인할 수 있다. 또한 당연히 모든 발생할 수 있는 확률들의 합은 1이고 \( 0 &amp;nbsp;\le x \le 1\)가 된다. 참고로 (\( \log1 = 0 \))이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Entropy(엔트로피)&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Entropy(엔트로피)&lt;/b&gt;는&amp;nbsp;머신러닝과&amp;nbsp;정보이론에서&amp;nbsp;&lt;b&gt;불확실성(Uncertainty)&lt;/b&gt;과&amp;nbsp;&lt;b&gt;정보량(Information&amp;nbsp;Quantity)&lt;/b&gt;을 수치적으로 측정하는 지표로 사용된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 색깔 예제를 이어가보자.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;빨, 주, 노, 초 옷을 입고 있는 10명이 있다고 가정하자. 우리는 이 정보들을 저장할 방법을 고안하는 업무를 맡았다고 가정하자. 우리는 이 정보들을 저장할 때 &lt;u&gt;&quot;빨간 옷을 입은 사람 1&quot;, &quot;주황 옷을 입은 사람 1&quot;, &quot;빨간 옷을 입은 사람 2&quot;...&lt;/u&gt; 이와 같이 저장할 수 있지만 이는 다소 비효율적이다. 만약 이 정보를 이진화해 4가지 색(빨, 주, 노, 초)을 2비트로 표현하면 10명에 대해 20비트만 필요하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 각 색의 사전 확률을 알고 있다면, 더 적은 비트로 인코딩할 수 있는 최적 방법이 존재한다. 이때 사용되는 것이 바로 Shannon의 엔트로피 개념이다. 섀넌은 특정 확률분포 \( P(X) \)를 따르는 확률 변수 \( X \)가 있을 때, 확률 변수 \( X \)의 사건 \( x \)를 \( p(x)I(x) \)의 길이로 인코딩하는 것이 기대값을 최소로 만들 수 있음을 보였다. 이때의 &lt;b&gt;확률 변수 \( X \)&lt;/b&gt;의 &lt;b&gt;기대값(Expected&amp;nbsp;Value)&lt;/b&gt;을 &lt;b&gt;엔트로피&lt;/b&gt;라고 한다. 즉,&amp;nbsp;엔트로피는&amp;nbsp;이론적으로&amp;nbsp;가능한&amp;nbsp;최소&amp;nbsp;비트&amp;nbsp;수를&amp;nbsp;정의한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;기대값(Expected&amp;nbsp;Valu&lt;/b&gt;e)이란 각 사건이 벌어졌을 때의 이득과 그 사건이 벌어질 확률을 곱한 것을 전체 사건에 대해 합한 값이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ \mathbb{E}[X] = \sum_{i}{x_i P(x_i)} $$&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;\( X \): 어떤 확률 변수(예: 주사위 눈)&lt;/li&gt;
&lt;li&gt;\( x_i \): 그&amp;nbsp;확률&amp;nbsp;변수가&amp;nbsp;가질&amp;nbsp;수&amp;nbsp;있는&amp;nbsp;i번째&amp;nbsp;구체적인&amp;nbsp;값&lt;/li&gt;
&lt;li&gt;\( P(x_i) \): 확률 변수 \( X \)가 \( x_i \) 값을 가질 확률(주사위의 경우 \( \frac{1}{6} \))&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주사위 경우에서 기대값은 결국 \( \mathbb{E}[X] = \frac{1}{6}(1+2+3+4+5+6) = 3.5 \)가 된다. 이는 1부터 6까지 고르게 분포된 값들의 평균이 3.5라는 의미이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 &lt;b&gt;정보량(\( I(x) \))&lt;/b&gt;의 &lt;b&gt;기대값(\( \mathbb{E}[X] \))&lt;/b&gt;을 계산하면 그 분포의 엔트로피가 된다. 이는 특정 확률 분포를 따르는 사건을 인코딩할 때, 이론적으로 필요한 평균 비트 수를 의미한다. 수식은 다음과 같다.&lt;br /&gt;$$&amp;nbsp;H(P)&amp;nbsp;=&amp;nbsp;\mathbb{E}_{x&amp;nbsp;\sim&amp;nbsp;P}[I(x)]&amp;nbsp;=&amp;nbsp;-&amp;nbsp;\sum_{x}&amp;nbsp;P(x)&amp;nbsp;\log&amp;nbsp;P(x)&amp;nbsp;$$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 수식에서 \( I(x) = -\log{P(x)} \)이며 이는 기대값 수식의 \( x_i \)에 대응한다. 또한 \( P(x) \)는 기대값 수식의 \( P(x_i) \)에 대응한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;빨, 주, 노, 초의 옷을 입은 사람들이 다음과 같은 비율로 존재한다고 하자. 아래는 확률 분포 \( P \)에 해당한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;\( P(color) \)
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #ee2323; text-align: left;&quot;&gt;빨간색(R): 0.4&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #ee2323; text-align: left;&quot;&gt;&lt;span style=&quot;color: #f89009; text-align: left;&quot;&gt;주황색(O): 0.3&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #ee2323; text-align: left;&quot;&gt;&lt;span style=&quot;color: #f3c000; text-align: left;&quot;&gt;노란색(Y): 0.2&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #ee2323; text-align: left;&quot;&gt;&lt;span style=&quot;color: #1b711d; text-align: left;&quot;&gt;초록색(G): 0.1&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정보량은 다음과 같이 구할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ I(R) = -\log_2(0.4) &amp;asymp; 1.3219 $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ I(O) = -\log_2(0.3) &amp;asymp; 1.7370 $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ I(Y) = -\log_2(0.2) &amp;asymp; 2.3219 $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ I(G) = -\log_2(0.1) &amp;asymp; 3.3219 $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이&amp;nbsp;확률&amp;nbsp;분포의&amp;nbsp;엔트로피는&amp;nbsp;다음과&amp;nbsp;같이&amp;nbsp;계산된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;\begin{aligned} &lt;br /&gt;H(X)&amp;nbsp;&amp;amp;=&amp;nbsp;0.4&amp;nbsp;\cdot&amp;nbsp;I(R)&amp;nbsp;+&amp;nbsp;0.3&amp;nbsp;\cdot&amp;nbsp;I(O)&amp;nbsp;+&amp;nbsp;0.2&amp;nbsp;\cdot&amp;nbsp;I(Y)&amp;nbsp;+&amp;nbsp;0.1&amp;nbsp;\cdot&amp;nbsp;I(G)&amp;nbsp;\\ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;amp;=&amp;nbsp;0.4&amp;nbsp;\cdot&amp;nbsp;1.3219&amp;nbsp;+&amp;nbsp;0.3&amp;nbsp;\cdot&amp;nbsp;1.7370&amp;nbsp;+&amp;nbsp;0.2&amp;nbsp;\cdot&amp;nbsp;2.3219&amp;nbsp;+&amp;nbsp;0.1&amp;nbsp;\cdot&amp;nbsp;3.3219&amp;nbsp;\\ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;amp;=&amp;nbsp;1.8464\&amp;nbsp;\text{bits} &lt;br /&gt;\end{aligned}&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 중요한 점은 어떤 확률 분포가 있을 때, \( H(P) \) 보다 낮은 기대값을 가질 수 없다는 점이다. 즉 머신러닝 관점에서 확률 분포 \( P \)를 알고 있다고 했을 때, \( H(P) \)는 &lt;b&gt;Global Minimum&lt;/b&gt;이 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추가적으로 엔트로피는 확률 분포의 불확실성을 측정하는 지표로, 확률 분포가 &lt;b&gt;균등할 때(Uniform&amp;nbsp;Distribution)&lt;/b&gt; &lt;b&gt;최대값&lt;/b&gt;을 가진다. 이는 어떤 사건이 발생할지 예측하기 가장 어려운 상태로, &lt;b&gt;불확실성이 가장 큰 상황을&lt;/b&gt; 의미한다. 반면, 특정 사건이 높은 확률로 반복적으로 발생하는 &lt;b&gt;편향된 분포&lt;/b&gt;에서는 불확실성이 줄어들어 &lt;b&gt;엔트로피가 낮아진다&lt;/b&gt;.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1417&quot; data-origin-height=&quot;365&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kzcve/btsNoMeI7fd/xDr4tp7T8UKdKe8RQrxLJ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kzcve/btsNoMeI7fd/xDr4tp7T8UKdKe8RQrxLJ1/img.png&quot; data-alt=&quot;Fig 1. Entropy&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kzcve/btsNoMeI7fd/xDr4tp7T8UKdKe8RQrxLJ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fkzcve%2FbtsNoMeI7fd%2FxDr4tp7T8UKdKe8RQrxLJ1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;678&quot; height=&quot;175&quot; data-origin-width=&quot;1417&quot; data-origin-height=&quot;365&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Fig 1. Entropy&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위&amp;nbsp;그래프는&amp;nbsp;확률&amp;nbsp;분포에&amp;nbsp;따른&amp;nbsp;엔트로피의&amp;nbsp;차이를&amp;nbsp;시각화한&amp;nbsp;것이다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Uniform Distribution&lt;/b&gt;: 모든 사건이 동일한 확률을 가지며, 불확실성이 최대이므로 엔트로피도 가장 큼.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Skewed Distribution&lt;/b&gt;: 한 사건의 확률이 압도적으로 높고 나머지는 낮아 불확실성이 줄어들어 엔트로피가 낮음.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Deterministic Distribution&lt;/b&gt;: 하나의 사건만 발생하며, 불확실성이 전혀 없기 때문에 엔트로피가 0.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 성질로 인해 엔트로피는 어떤 확률 변수의 정보량의 총량을 나타냄과 동시에 그 분포가 지닌 불확실성의 정도를 정량화할 수 있게 해준다. 이는 정보이론에서 중요한 개념이며, 머신러닝에서는 직접적으로 사용되지 않는 경우도 있지만, 모델의 예측 불확실성이나 정보 획득 등의 맥락에서 응용될 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Cross Entropy(교차 엔트로피)&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리는 현실 세계에서 일어나는 현상 중 실제 확률 분포 \( P \)를 모두 알고 있는 것은 쉽지 않다. 내가 당장 10초 뒤에 무슨 일이 일어날 수도 있는 것이고, 우리는 모든 법칙에 대한 이치를 알고 있지는 않기 때문이다. 이 문제는 우리가 머신 러닝에서 현실의 문제를 모델할 때도 똑같이 적용된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 우리는 일단 빨, 주, 노, 초의 옷 4가지 종류가 있으니 다음과 같이 맞는지 아닌지는 모르는 확률 분포 \( Q \)를 다음과 같이 정의해 보자.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;\( Q(color) \)
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #ee2323; text-align: left;&quot;&gt;빨간색(R): 0.25&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #ee2323; text-align: left;&quot;&gt;&lt;span style=&quot;color: #f89009; text-align: left;&quot;&gt;주황색(O): 0.25&lt;/span&gt; &lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #ee2323; text-align: left;&quot;&gt;&lt;span style=&quot;color: #f3c000; text-align: left;&quot;&gt;노란색(Y): 0.25&lt;/span&gt; &lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #ee2323; text-align: left;&quot;&gt;&lt;span style=&quot;color: #1b711d; text-align: left;&quot;&gt;초록색(G): 0.25&lt;/span&gt; &lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 사용하던 정보량 \( -\log{P(x)} \)는 우리가 임의로 만든 확률 분포 \( Q \)를 사용하여 \( -\log{Q(x)} \)가 되었다. 우리는 지금 실제 확률 분포인 \( P \)에 대한 것은 모르는 상태이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 &lt;b&gt;실제 확률 분포(\( P \), Ground&amp;nbsp;Truth)&lt;/b&gt;를 우리가 &lt;b&gt;가정한(근사한) 확률 분포 \( Q \)&lt;/b&gt;를 이용하여 예측했을 때, 그 예측의 평균적인 &lt;b&gt;정보 손실&lt;/b&gt;을 &lt;b&gt;Cross Entropy&lt;/b&gt;로 측정할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;\begin{aligned} &lt;br /&gt;H(P)&amp;nbsp;&amp;amp;=&amp;nbsp;\mathbb{E}_{x&amp;nbsp;\sim&amp;nbsp;P}[I(x)]&amp;nbsp;\\ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;amp;=&amp;nbsp;\mathbb{E}_{x&amp;nbsp;\sim&amp;nbsp;P}[-\log&amp;nbsp;P(x)]&amp;nbsp;\\ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;amp;=&amp;nbsp;-\sum_x&amp;nbsp;P(x)&amp;nbsp;\log&amp;nbsp;P(x) &lt;br /&gt;\end{aligned}&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ H(P, Q) &amp;equiv; CE(P,Q) = &amp;minus;&amp;sum;P(x) \log{Q(x)} $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;왜 \( P(x) \)와 \( \log{Q(x)} \)를 곱한 뒤 합산하면 &lt;b&gt;정보 손실&lt;/b&gt;이 될까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 곱하는 이유는 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;\( P(x) \)는 실제 사건이 일어날 확률이다. &lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;\( \log{Q(x)} \)는 예측 확률이 낮을수록 손실이 크게 된다. 따라서 이 둘을 곱하는 것은 &lt;b&gt;실제로 일어날 가능성이 있는 사건들 각각에 대해, 모델이 그 사건을 얼마나 잘 예측했는지를 고려한 정보 손실&lt;/b&gt;이 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;합산은 &lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;한 사건만 보는 게 아니라 &lt;b&gt;모든 가능한 사건에 대해 평균적으로 평가&lt;/b&gt;한다는 의미를 가진다. &lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;즉, Cross Entropy는 &lt;b&gt;&quot;정답 기준에서 모델이 예측한 정보량은 얼마나 손실이 있나?&quot;&lt;/b&gt;&amp;nbsp;수치화한 것이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;KL(Kullback&amp;ndash;Leibler)&amp;nbsp;Divergence&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;KL(Kullback&amp;ndash;Leibler) Divergence&lt;/b&gt;는 두 확률 분포 \( P \)와 \( Q \)의 차이를 수치화한 것이다. 여기서 KL은 Kullback-Leibler의 약자이고, Divergence는 &quot;차이&quot;라는 의미이다. 즉, KL&amp;nbsp;Divergence란&amp;nbsp;&quot;&lt;b&gt;얼마나&amp;nbsp;비효율적으로&amp;nbsp;예측했는가?&lt;/b&gt;&quot;를&amp;nbsp;수치화한&amp;nbsp;값이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 위에서 나온 내용들을 정리하면 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;\(&amp;nbsp;P(x)&amp;nbsp;\):&amp;nbsp;실제&amp;nbsp;정답&amp;nbsp;분포&amp;nbsp;(우리가&amp;nbsp;알거나&amp;nbsp;가정한&amp;nbsp;분포)&amp;nbsp;=&amp;nbsp;Ground&amp;nbsp;Truth &lt;br /&gt;&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;\(&amp;nbsp;Q(x)&amp;nbsp;\):&amp;nbsp;모델이&amp;nbsp;학습으로&amp;nbsp;추정한&amp;nbsp;분포&amp;nbsp;(예측&amp;nbsp;분포)&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Entropy(엔트로피): \( H(P) = -&amp;sum; P(x) \log P(x) \)&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Cross Entropy(CE, 교차 엔트로피): \( H(P, Q) = -&amp;sum; P(x) \log Q(x) \)&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 내용들을 바탕으로 KL Divergence는 다음과 같이 정의된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;\begin{aligned} &lt;br /&gt;KL(P&amp;nbsp;\parallel&amp;nbsp;Q)&amp;nbsp;&amp;amp;=&amp;nbsp;CE(P,&amp;nbsp;Q)&amp;nbsp;-&amp;nbsp;H(P)&amp;nbsp;\\ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;amp;=&amp;nbsp;-\sum_x&amp;nbsp;P(x)&amp;nbsp;\log&amp;nbsp;Q(x)&amp;nbsp;-&amp;nbsp;\left(-\sum_x&amp;nbsp;P(x)&amp;nbsp;\log&amp;nbsp;P(x)\right)&amp;nbsp;\\ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;amp;=&amp;nbsp;\sum_x&amp;nbsp;P(x)&amp;nbsp;\log&amp;nbsp;P(x)&amp;nbsp;-&amp;nbsp;\sum_x&amp;nbsp;P(x)&amp;nbsp;\log&amp;nbsp;Q(x)&amp;nbsp;\\ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;amp;=&amp;nbsp;\sum_x&amp;nbsp;P(x)&amp;nbsp;(\log&amp;nbsp;P(x)&amp;nbsp;-&amp;nbsp;\log&amp;nbsp;Q(x))&amp;nbsp;\\ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;amp;=&amp;nbsp;\sum_x&amp;nbsp;P(x)&amp;nbsp;\log&amp;nbsp;\frac{P(x)}{Q(x)} &lt;br /&gt;\end{aligned}&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 \( Q(x) \)가 \( P(x) \)의 분포를 정확하게 알아냈을 때, 다시 말해 \( Q(x) \)와 \( P(x) \)가 완전히 같아졌을 때 \( \log \frac{P(x)}{Q(x)} = \log(1) \)이 되면서 \( KL(P \parallel Q) \) 값이 0이 되고, 이것이 바로 최소가 된다. 반대로 분포가 다르면 KL 값이 커지며 이는 예측이 정답에서 멀어졌다는 의미가 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고로 멀다고 표현했지만 엄밀이 여기선 거리를 의미하는 것은 아니고 &lt;b&gt;비대칭적인 차이(discrepancy)&lt;/b&gt;를 의미한다고 봐야한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;관련 포스팅&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://aliencoder.tistory.com/153&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;2024.11.13 - [Data Science/ML &amp;amp; DL] - Cross Entropy에 대한 고찰(with Softmax)&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;참고 자료&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://angeloyeo.github.io/2020/10/27/KL_divergence.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://angeloyeo.github.io/2020/10/27/KL_divergence.html&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://lifeignite.tistory.com/44&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://lifeignite.tistory.com/44&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>AI/Data Analysis</category>
      <category>cross entropy</category>
      <category>entropy</category>
      <category>information quantity</category>
      <category>KL Divergence</category>
      <category>기대값</category>
      <category>정보량</category>
      <author>AlienCoder</author>
      <guid isPermaLink="true">https://aliencoder.tistory.com/222</guid>
      <comments>https://aliencoder.tistory.com/222#entry222comment</comments>
      <pubDate>Wed, 16 Apr 2025 19:56:41 +0900</pubDate>
    </item>
    <item>
      <title>LTI(Linear Time-Invariant) Systems</title>
      <link>https://aliencoder.tistory.com/221</link>
      <description>&lt;script src=&quot;https://polyfill.io/v3/polyfill.min.js?features=es6&quot;&gt;&lt;/script&gt;
&lt;script src=&quot;https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js&quot;&gt;&lt;/script&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;LTI(Linear Time-Invariant) Systems&lt;/b&gt;에 대해 확인하기 전 여기서 &lt;b&gt;System&lt;/b&gt;이 뭘 의미하는지 살펴보자.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;System은 신호를 처리하여 새로운 신호 또는 새로운 신호 표현을 생성하는 방식으로 동작한다. 시스템의 입력과 출력이 모두 이산시간 신호일 경우, 해당 시스템을 &lt;b&gt;이산시간 시스템(discrete-time system)&lt;/b&gt;이라고 부른다. &lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;658&quot; data-origin-height=&quot;174&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tzmzW/btsNf9wlT1p/b5K6GGM1GKrpqA3RkCR6SK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tzmzW/btsNf9wlT1p/b5K6GGM1GKrpqA3RkCR6SK/img.png&quot; data-alt=&quot;Fig 1. Discrete-Time System&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tzmzW/btsNf9wlT1p/b5K6GGM1GKrpqA3RkCR6SK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtzmzW%2FbtsNf9wlT1p%2Fb5K6GGM1GKrpqA3RkCR6SK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;620&quot; height=&quot;164&quot; data-origin-width=&quot;658&quot; data-origin-height=&quot;174&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Fig 1. Discrete-Time System&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 시스템의 특성을 가진 &lt;b&gt;LTI 시스템(Linear Time-Invariant System)&lt;/b&gt;은 이름에 나오듯&amp;nbsp;&lt;b&gt;선형(linear)성&lt;/b&gt;과 &lt;b&gt;시간 불변성(time-invariance)&lt;/b&gt;을 만족하는 시스템이다. 저 특성들에 의해 어떤 시스템을 &lt;b&gt;Predictable&lt;/b&gt;하게 해주는 것이 핵심이라 생각한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LTI에 대해 이해하기 전에 &lt;b&gt;Causality(인과성)&lt;/b&gt;와 &lt;b&gt;Stability(안정성)&lt;/b&gt;라는 사전 지식이 필요하다. &lt;b&gt;Causality&lt;/b&gt;와 &lt;b&gt;Stability&lt;/b&gt;를 간단하게 살펴보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Causality(인과성)&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LTI 시스템은 인과적일 수도 있고 비인과적일 수도 있으며, 인과성은 임펄스 응답이 \( h[n] = 0 \,\, \text{for} \,\, n &amp;lt; 0 \)인지 여부로 판별된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Causal system&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재&amp;nbsp;및&amp;nbsp;과거&amp;nbsp;입력값만을&amp;nbsp;사용하여&amp;nbsp;출력을&amp;nbsp;계산하는&amp;nbsp;시스템을&amp;nbsp;&lt;b&gt;인과적 시스템(causal system)&lt;/b&gt;이라고&amp;nbsp;한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ y[n] =  (x[n]) = x[n] - x[n-1] $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, input 과거(어딘지 모를 먼 과거)부터 현재까지의 데이터가 현재에 대한 output에 영향을 끼친다는 것이다. 일반적으로 이는 합리적이라 느껴지고 당연하게 느껴진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Non-Causal system&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;미래&amp;nbsp;입력값을&amp;nbsp;사용하는&amp;nbsp;시스템을&amp;nbsp;&lt;b&gt;비인과적 시스템(noncausal system)&lt;/b&gt;이라고&amp;nbsp;한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ y[n] =  (x[n]) = x[n+1] - x[n] $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Causal system과 반대로 현재부터 미래(어딘지 모를 먼 미래)까지의 데이터가 현재의 output에 영향을 준다. 말이 안되는 소리처럼 느껴지긴 한다. 하지만 &lt;span&gt;비인과적 시스템은 오프라인 처리(예: 녹음된 데이터 처리, 사후 분석)에서는 가능하다. 즉, 비인과적 시스템은 실시간 처리에는 부적합하지만, 모든 데이터를 미리 확보한 상황에서는 사용할 수 있다. 미리 확보한다는 것은 &lt;/span&gt;&lt;b&gt;Buffer&lt;/b&gt;와 연관이 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;비인과 시스템은 미래 입력값 필요.&lt;/li&gt;
&lt;li&gt;하지만 실시간으로 입력이 들어오는 경우, 미래 입력은 아직 존재하지 않기 때문에 바로 처리할 수 없음.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 입력 신호를 일정 시간 동안 저장(버퍼링) 한 다음, 미래 값까지 확보되었을 때 계산을 수행하여 해결 가능하다. 버퍼는 이렇게 미래 입력을 확보할 때까지 기다리기 위해 필요한 메모리 공간이다. 우리 삶에 밀접한 비인과 시스템 예시를 좀 더 설명해 보면 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;스마트폰 사진 보정(HDR 합성): &lt;/b&gt;스마트폰 카메라는 한 번의 셔터로 여러 장의 사진(밝기 다른 이미지)을 연속으로 찍은 후 합성한다. 이 때 밝은 사진과 어두운 사진(미래에 찍힌 사진)을 함께 고려하여 한 장의 선명한 이미지를 생성하는데 여기서 비인과적 연산이 발생한다. 여러 시점의 데이터를 모두 참조하여 최종 결과를 만들기 때문이다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;음성 인식(Voice Activity Detection):&lt;/b&gt; 음성 신호의 말소리 시작/끝을 탐지할 때, 미래 신호를 참고하면 더욱 정확한 탐지가 가능해진다. 이 때 오프라인 음성 데이터 처리에서는 미래 신호를 저장한 후 분석하는 과정에서 비인과적 연산이 발생한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;스포츠 하이라이트 생성: &lt;/b&gt;녹화된 스포츠 영상을 분석하여 골 장면, 득점 장면을 추출할 때를 생각해볼 수 있다. 특정 이벤트 발생 이후 몇 초 동안의 데이터를 참조하여 하이라이트를 생성하는데 이 역시 비인과적 처리이다. 미래의 상황을 보고 중요한 장면을 결정하는 것이기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Stability(안정성)&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 유한한 입력 시퀀스가 유한한 출력 시퀀스를 생성할 때, 시스템은 안정적(stable)이다. 아래는 임펄스 응답이 없는 일반 비선형 시스템 예시이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ y[n] =  (x[n]) = x[n]^2 $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ \text{※} \,\, y[n] =  (x[n]) = log(x[n]) $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LTI 시스템이 BIBO 안정성(Bounded-Input Bounded-Output Stability)을 만족하려면 임펄스 응답(\( h[n] \))의 절댓값 총합이 유한해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ \sum_{n=-\infty}^{\infty} |h[n]| &amp;lt; \infty $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;LTI(Linear&amp;nbsp;Time-Invariant)&amp;nbsp;Systems&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;LTI(Linear Time-Invariant) Systems&lt;/b&gt;이란 &lt;b&gt;선형성(linearity)&lt;/b&gt;과 &lt;b&gt;시간 불변성(time-invariance)&lt;/b&gt;을 동시에 만족하는 시스템이다. 그러므로 LTI 시스템은 &lt;b&gt;예측가능한(Predictable) 시스템&lt;/b&gt;이라는 장점을 가지게 된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 게임 속 여러 개의 캐릭터를 동시에 조작할 때, 각각의 캐릭터를 따로 조작한 결과를 더한 것과 여러 캐릭터를 한꺼번에 조작한 결과가 같다면, 이 시스템은 선형성을 가진다. 또한 어느 시점에 조작을 시작하든 결과가 일정하다면 시간 불변성도 가진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LTI 시스템의 장점을 정리해보면 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;수학적으로 분석과 설계가 매우 용이하다.&lt;/li&gt;
&lt;li&gt;임펄스 응답만 알면 시스템 전체 특성을 알 수 있다.&lt;/li&gt;
&lt;li&gt;주파수 응답 분석(푸리에 변환) 및 안정성 판별이 간단하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;선형성(Linearity)&lt;/b&gt;이란 시스템이 선형성을 가지려면, 입력 신호의 덧셈과 스칼라 곱이 출력에 대해 그대로 적용되어야 한다. 즉, 두 입력 \( x_1[n], x_2[n] \)과 상수 \( a, b \)에 대해 다음을 만족해야 한다.&lt;br /&gt;$$ T(a x_1[n] + b x_2[n]) = a T(x_1[n]) + b T(x_2[n]) $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;시간 불변성(Time-Invariance)&lt;/b&gt;이란 시스템이 시간 불변성을 가지려면, 입력 신호를 시간적으로 지연시켰을 때 출력도 동일하게 지연되어야 한다. 즉, 입력 \( x[n] \)에 대해 \( x[n-n_0] \)를 입력하면 출력은 \( y[n-n_0] \)가 되어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ T(x[n-n_0]) = y[n-n_0] $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LTI 시스템의 기본 특성은 LTI 시스템은 입력과 시스템의 임펄스 응답 \( h[n] \)의 &lt;b&gt;Convolution&lt;/b&gt;으로 출력이 계산된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ y[n] = x[n] * h[n] = &amp;sum;_{k=-&amp;infin;}^{&amp;infin;} x[k] h[n-k] $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 시간 영역에서 컨볼루션은 계산이 복잡하다. 이를 Z 변환(Z-transform) 을 사용하면 곱셈으로 단순화할 수 있다. LTI 시스템의 입력&amp;nbsp;\( x[n] \), 출력 \( y[n] \), 임펄스 응답 \( h[n] \)에 대해 다음 관계가 성립한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ Y(z) = H(z) X(z) $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, LTI 시스템은 Z 변환을 통해 매우 간단하게 분석할 수 있다. 관련 예제를 하나 풀어보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;예제&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;입력 \( x[n] \)과 출력 \( y[n] \)의 관계가 다음과 같다고 하자. 이를 Z 변환을 사용해 분석하라.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ y[n] = 0.5 y[n-1] + x[n] $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 위해 우선 시스템 함수인 \( H(z) \)를 구해야 하며, 시스템이 안정한지 판단하여야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해설은 아래와 같다.&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. Z 변환 적용&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시스템 양변에 Z 변환을 취한다. &lt;br /&gt;$$ Z\{y[n]\} = 0.5 Z\{y[n-1]\} + Z\{x[n]\} $$&lt;br /&gt;z&amp;nbsp;변환의&amp;nbsp;시간&amp;nbsp;지연&amp;nbsp;성질에&amp;nbsp;따라 &lt;br /&gt;$$ Z\{y[n-1]\} = z^{-1} Y(z) $$&lt;br /&gt;따라서&amp;nbsp;식은&amp;nbsp;다음과&amp;nbsp;같이&amp;nbsp;된다. &lt;br /&gt;$$&amp;nbsp;Y(z)&amp;nbsp;=&amp;nbsp;0.5&amp;nbsp;z^{-1}&amp;nbsp;Y(z)&amp;nbsp;+&amp;nbsp;X(z)&amp;nbsp;$$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. 시스템 함수 \( H(z) \) 구하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;출력 \( Y(z) \)를 정리한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ Y(z) (1 - 0.5 z^{-1}) = X(z) $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;양변은 \( X(z) \)로 나누어 \( H(z) \)를 구한다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ H(z) = \frac{Y(z)}{X(z)} = \frac{1}{1 - 0.5 z^{-1}} $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3. 안정성 판단&lt;/b&gt;&lt;br /&gt;안정성은&amp;nbsp;시스템&amp;nbsp;함수의&amp;nbsp;폴(pole)&amp;nbsp;위치로&amp;nbsp;판단한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;분모를 0으로 만드는 \( z \)를 찾는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ 1 - 0.5 z^{-1} = 0 $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;양변에 \( z \)를 곱하여 \( z \)를 찾는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ z - 0.5 = 0 $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$&amp;nbsp;z&amp;nbsp;=&amp;nbsp;0.5&amp;nbsp;$$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 pole이 \( |z| = 0.5 \)에 위치하며, 단위 원 \( |z| = 1 \) 안쪽에 있으므로 이 시스템은 안정적이다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;폴이&amp;nbsp;단위원&amp;nbsp;안에(\(&amp;nbsp;|z|&amp;nbsp;&amp;lt;&amp;nbsp;1&amp;nbsp;\))&amp;nbsp;있다면&amp;nbsp;신호가&amp;nbsp;시간이&amp;nbsp;지남에&amp;nbsp;따라&amp;nbsp;점점&amp;nbsp;감쇠:&amp;nbsp;안정&lt;/li&gt;
&lt;li&gt;폴이&amp;nbsp;단위원에&amp;nbsp;있으면(\(&amp;nbsp;|z|&amp;nbsp;=&amp;nbsp;1&amp;nbsp;\))&amp;nbsp;단위신호가&amp;nbsp;변하지&amp;nbsp;않고&amp;nbsp;유지:&amp;nbsp;경계적&amp;nbsp;상태(특별&amp;nbsp;관리&amp;nbsp;필요)&lt;/li&gt;
&lt;li&gt;폴이&amp;nbsp;단위원&amp;nbsp;밖에&amp;nbsp;있을&amp;nbsp;때(\(&amp;nbsp;|z|&amp;nbsp;&amp;gt;&amp;nbsp;1&amp;nbsp;\))&amp;nbsp;신호가&amp;nbsp;시간이&amp;nbsp;지남에&amp;nbsp;따라&amp;nbsp;폭주:&amp;nbsp;불안정&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;관련 포스팅&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://aliencoder.tistory.com/220&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;2025.04.10 - [Data Science/SR &amp;amp; VC] - Z Transform(Z 변환)&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;참고 자료&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://velog.io/@lighthouse97/LTI-%EC%8B%9C%EC%8A%A4%ED%85%9C%EC%9D%B4%EB%9E%80&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://velog.io/@lighthouse97/LTI-%EC%8B%9C%EC%8A%A4%ED%85%9C%EC%9D%B4%EB%9E%80&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>AI/SR &amp;amp; VC</category>
      <category>linear time-invariant systems</category>
      <category>lti systems</category>
      <author>AlienCoder</author>
      <guid isPermaLink="true">https://aliencoder.tistory.com/221</guid>
      <comments>https://aliencoder.tistory.com/221#entry221comment</comments>
      <pubDate>Fri, 11 Apr 2025 13:25:09 +0900</pubDate>
    </item>
    <item>
      <title>Z Transform(Z 변환)</title>
      <link>https://aliencoder.tistory.com/220</link>
      <description>&lt;script src=&quot;https://polyfill.io/v3/polyfill.min.js?features=es6&quot;&gt;&lt;/script&gt;
&lt;script src=&quot;https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js&quot;&gt;&lt;/script&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적으로 변환(Transform)이라는 말이 붙은 모든 것들은 기존의 풀거나 해석하기 어려웠던 형태를 보다 더 쉬운 형태로 바꿔주는 수학적 기법들이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Z Transform(Z 변환)&lt;/b&gt;은 좁게는 선형 차분 방정식(Linear Difference Equation)을 쉽게 풀 수 있게 만들어 주는 테크닉이라고 할 수도 있고, 좀 더 넓은 의미에서는 DTFT(Discrete Time Fourier Transform)의 일반화된 형태라고 할 수도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Z 변환&lt;/b&gt;에는 One-Sided Z-Transform, Two-Sided Z-Transform이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;One-Sided&amp;nbsp;Z-Transform&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;One side Z 변환은 +영역만을 사용하는 변환이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ X(z) = \sum_{n=0}^{\infty} x[n] z^{-n} $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공식을 보면 0부터 무한대까지의 영역만을 더해주는 것을 볼 수 있다. 주로 대부분의 디지털 필터와 실제 시스템 해석에서 사용되며, &lt;b&gt;인과적(Causal) 시스템&lt;/b&gt; 분석에 사용한다. 여기서 Causal이란 현재 및 과거 입력값만을 사용하여 출력을 계산하는 시스템을 인과적 시스템(Causal System)을 의미한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;디지털 신호처리에서는 주로 One sided Z 변환을 주로 사용하는데 이는 대부분의 시스템이 인과적(Causal)이기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Two-Sided&amp;nbsp;Z-Transform&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Two-Sided Z-Transform&lt;/b&gt;은 말 그대로 두 영역, +와 - 영역을 모두 변환하는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ X(z) = \sum_{n=-\infty}^{\infty} x[n] z^{-n} $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;비인과적(non-causal)&lt;/b&gt; 신호나 양방향 신호를 다룰 때 사용된다. 일반적인 이론적 분석에서 사용되며, DTFT(이산 시간 푸리에 변환)와 연결될 때 이용된다. 여기서 non-causal이란 미래 입력값을 사용하는 것을 의미한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이산 신호 \( x[n] \)에 대해 변환하게 되면 아래와 같이 표현할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ Z\left[x[n]\right] = \sum_{n=-\infty}^{\infty}x[n]z^{-n} $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 \( z \)는 복소수에 해당한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Z 변환(Z-Transform)&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Z 변환(Z-Transform)&lt;/b&gt;은 이산 신호(discrete-time signal)를 복소수 영역(complex plane)으로 변환하는 수학적 방법이다. Continuous system에서 라플라스 변환이 있다면, 이와 비슷한 역할을 해주는 것이 바로 Z 변환이다. 시간 영역(Time domain)의 신호를 주파수 영역(Frequency domain) 또는 복소수 영역(Complex domain)으로 옮겨서 분석하기 쉽게 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;490&quot; data-origin-height=&quot;392&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cPDJyw/btsNfSNihh9/5wWKZ2mtT7jM33Ctzm1IHK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cPDJyw/btsNfSNihh9/5wWKZ2mtT7jM33Ctzm1IHK/img.png&quot; data-alt=&quot;Fig 1. Z Transform&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cPDJyw/btsNfSNihh9/5wWKZ2mtT7jM33Ctzm1IHK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcPDJyw%2FbtsNfSNihh9%2F5wWKZ2mtT7jM33Ctzm1IHK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;413&quot; height=&quot;330&quot; data-origin-width=&quot;490&quot; data-origin-height=&quot;392&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Fig 1. Z Transform&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ X(z) = \sum_{n=-\infty}^{\infty} x[n] z^{-n} \quad (ROC: ∣z∣&amp;gt;∣a∣) $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원래 Z 변환은 복소수 \( z \)를 변수로 사용한다. 그런데 z에 특별히 \( z=e^{j&amp;omega;} \)를 대입하면, 이때는 복소수 크기가 1인 경우, 즉 단위원 위에서 값을 계산하는 것이다. 이 결과는 바로 이산 시간 푸리에 변환 (DTFT)가 된다. 즉, Z 변환을 단위원 위에서 본 것이 바로 주파수 영역 분석(&amp;omega;-domain)이고, \( X(e^{j&amp;omega;}) \)라고 부른다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이산시간 푸리에 변환(DTFT, Discrete-Time Fourier Transform)의 수식은 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$&amp;nbsp;X(e^{j\omega})&amp;nbsp;=&amp;nbsp;\sum_{n=-\infty}^{\infty}&amp;nbsp;x[n]&amp;nbsp;e^{-j&amp;nbsp;\omega&amp;nbsp;n}&amp;nbsp;$$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Z 변환과 DTFT는 이산 신호를 주파수 영역으로 변환하는 방법이다. 둘은 서로 밀접한 관계를 가지며, Z 변환을 특정 경로(단위원) 상에서 평가함으로써 DTFT를 얻을 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단위원이 가지는 의미를 잠시 짚고 넘어가면 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;\( z \)(=pole)이 단위원 안에(\( |z| &amp;lt; 1 \)) 있다면 신호가 시간이 지남에 따라 점점 감쇠: 안정&lt;/li&gt;
&lt;li&gt;pole이 단위원에 있으면(\( |z| = 1 \)) 단위신호가 변하지 않고 유지: 경계적 상태(특별 관리 필요)&lt;/li&gt;
&lt;li&gt;pole이 단위원 밖에 있을 때(\( |z| &amp;gt; 1 \)) 신호가 시간이 지남에 따라 폭주: 불안정&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Z 변환을 이용해 주파수 응답을 구하는 순서는 다음과 같다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;신호의 Z 변환을 구한다.&lt;/li&gt;
&lt;li&gt;주파수&amp;nbsp;응답을&amp;nbsp;얻기&amp;nbsp;위해&amp;nbsp;\(&amp;nbsp;z=e^{j&amp;omega;}&amp;nbsp;\)&amp;nbsp;를&amp;nbsp;대입한다.&lt;/li&gt;
&lt;li&gt;결과가&amp;nbsp;바로&amp;nbsp;DTFT,&amp;nbsp;즉&amp;nbsp;주파수&amp;nbsp;응답이다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주파수&amp;nbsp;응답을&amp;nbsp;얻기&amp;nbsp;위해서는&amp;nbsp;단위원&amp;nbsp;상에서&amp;nbsp;해석해야&amp;nbsp;하므로,&amp;nbsp;다음과&amp;nbsp;같이&amp;nbsp;전개할&amp;nbsp;수&amp;nbsp;있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ X(e^{j\omega}) = \frac{1}{1 - a e^{-j\omega}} $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추가적으로 이 값에 절대값을 취하면 크기(진폭)을 알 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만, Z 변환과 DTFT의 상호 변환에 대한 내용은 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;532&quot; data-start=&quot;485&quot;&gt;&lt;b&gt;Z변환 &amp;rarr; DTFT&lt;/b&gt;: 가능. (단, ROC가 단위원을 포함해야 한다.)&lt;/li&gt;
&lt;li data-end=&quot;618&quot; data-start=&quot;533&quot;&gt;&lt;b&gt;DTFT &amp;rarr; Z변환&lt;/b&gt;: 일반적으로 불가능. (DTFT는 단위원 상의 정보만 갖고 있어 전체 Z평면 상의 특성, 특히 ROC를 알 수 없다.)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 &lt;b&gt;ROC(Region&amp;nbsp;of&amp;nbsp;Convergence)&lt;/b&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;란 이 무한급수가 수렴하는 \( z \)&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;값들의 집합이다. &lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;즉, ROC는 Z변환이 유효하게 정의되는 복소평면 상의 영역을 의미한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Z 변환은 다음과 같은 특정을 가지고 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;덧셈에 대해 선형(linear): 두 신호의 합의 z-변환은 각 신호의 z-변환의 합과 같다.&lt;/li&gt;
&lt;li&gt;스칼라 배에 대해 선형(linear): 상수를 곱한 신호의 z-변환은, 그 상수만큼 결과에도 곱해진다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 예제로 한 번 알아보자&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;예제&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;신호 \( x[n] \)에 대한 Z 변환을 구하라.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ x[n] = 2^n u[n] + 3^n u[n-2] $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ u[n] =&amp;nbsp;&lt;br /&gt;\begin{cases}&lt;br /&gt;1,&amp;nbsp;&amp;amp;&amp;nbsp;n&amp;nbsp;\geq&amp;nbsp;0&amp;nbsp;\\&lt;br /&gt;0,&amp;nbsp;&amp;amp;&amp;nbsp;n&amp;nbsp;&amp;lt;&amp;nbsp;0&lt;br /&gt;\end{cases} $$&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1. 신호를 먼저 두 부분으로 분리한다.&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫 번째 항은 \( 2^n \)에 단위 계단이 곱해진 신호이며, 두 번째 항은 \( 3^n \)이지만 \( n = 2 \)부터 시작한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2. 각 항의 Z 변환 구하기&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫 번째항: \( \sum_{n=0}^{\infty} (2^n) z^{-n} = \frac{1}{1 - 2z^{-1}} \quad \text{(수렴조건: } |z| &amp;gt; 2 \text{)} \)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 번째항: \(\sum_{n=0}^{\infty} (3^n) z^{-n} = \frac{1}{1 - 3z^{-1}} \quad \text{(수렴조건: } |z| &amp;gt; 3 \text{)}&amp;nbsp;\)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수렴 조건은 등비수열의 수렴 조건을 적용한 것이다. 등비수열은 다음과 같이 생겼다. 단, 이번 등비수열은 \( |r| &amp;lt; 1 \)인 경우만 논하도록 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ \sum_{n=0}^{\infty} r^n $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 유한한 등비수열의 합이라면 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ S_n = a \frac{1 - r^n}{1 - r} $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;무한한 등비수열의 합이라면 다음과 같을 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ S = \lim_{n \to \infty} a \frac{1 - r^n}{1 - r} = \frac{a}{1 - r} $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Z 변환은 무한한 경우를 고려해야 한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 첫 째항의 공비는 \( r = 2z^{-1} = \frac{2}{z} \)이 되고 따라서 수렴 조건은 \( \left| \frac{2}{z} \right| &amp;lt; 1 \)이 된다. 따라서 \( |z| &amp;gt; 2 \)이다. 만약 \( z \)가 이 범위를 벗어나면 발산하게 될 것이기 때문이다. 두&amp;nbsp;번째&amp;nbsp;항도&amp;nbsp;같은&amp;nbsp;방식으로&amp;nbsp;풀면&amp;nbsp;된다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;3. 전체 Z 변환&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 번째 항은 시간 이동(Time Shifting) 성질에 따라 \( x[n&amp;minus;k] \)의 Z 변환은 \( z^{&amp;minus;k} X(z) \)가 되는 성질에 근거한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 Z 변환 결과를 다음과 같이 정리할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ \frac{1}{1 - 2z^{-1}} + z^{-2} \times \frac{1}{1 - 3z^{-1}} $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 결과를 더 깔끔하게 다음과 같이 정리할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ X(z) = \frac{1}{1 - 2z^{-1}} + \frac{z^{-2}}{1 - 3z^{-1}} $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 ROC(수렴 영역)에서 공통 영역을 만족해야 한다는 원칙에 의해 \( |z| &amp;gt; 3 \)이 된다. 시각화하면 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;459&quot; data-origin-height=&quot;435&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/m5MPj/btsNfKaJskx/GGiQPFYn1ltsP7Innaxae0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/m5MPj/btsNfKaJskx/GGiQPFYn1ltsP7Innaxae0/img.png&quot; data-alt=&quot;Fig 2. 예제 시각화&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/m5MPj/btsNfKaJskx/GGiQPFYn1ltsP7Innaxae0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fm5MPj%2FbtsNfKaJskx%2FGGiQPFYn1ltsP7Innaxae0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;374&quot; height=&quot;354&quot; data-origin-width=&quot;459&quot; data-origin-height=&quot;435&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Fig 2. 예제 시각화&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 x 표시는 극점을 나타내고 있으며, &lt;b&gt;회색 음영의 바깥쪽&lt;/b&gt;이 바로&amp;nbsp;&lt;b&gt;수렴 영역(ROC)&lt;/b&gt; \( |z| &amp;gt; 3 \)을 나타낸다. 중간의 노란 점선원은 단위원 \( |z| = 1 \)을 의미한다. 단위원은 DTFT 분석할 때 중요하지만 여기서는 ROC와 직접 연결되지는 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Z 변환의 주요 목적을 정리하자면 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;시스템 해석: 필터, 시스템의 거동을 간단히 분석할 수 있다.&lt;/li&gt;
&lt;li&gt;차분방정식 &amp;rarr; 대수방정식 변환: 복잡한 시간 영역 차분 방정식을 간단한 대수적 형태로 다룰 수 있다.&lt;/li&gt;
&lt;li&gt;안정성 분석: 극점(poles)과 영점(zeros)을 이용해 시스템의 안정성 여부를 쉽게 확인할 수 있다.&lt;/li&gt;
&lt;li&gt;주파수 응답 분석: \( z \)에 특정한 값을 대입하여 주파수 응답을 얻을 수 있다.&amp;nbsp; &lt;br /&gt;예: \( z=e^{j&amp;omega;} \) 이면 DTFT(Discrete-Time Fourier Transform)와 연결된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;관련 포스팅&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://aliencoder.tistory.com/219&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;2025.04.08 - [Data Science/SR &amp;amp; VC] - Discrete-Time Fourier Transform(DTFT)과 Discrete Fourier Transform(DFT)&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;참고 자료&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=4PV6ikgBShw&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.youtube.com/watch?v=4PV6ikgBShw&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://angeloyeo.github.io/2019/08/13/Z_transform.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://angeloyeo.github.io/2019/08/13/Z_transform.html&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://under-bridge.tistory.com/6&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://under-bridge.tistory.com/6&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>AI/SR &amp;amp; VC</category>
      <category>DTFT</category>
      <category>region of convergence</category>
      <category>ROC</category>
      <category>z transform</category>
      <category>z 변환</category>
      <author>AlienCoder</author>
      <guid isPermaLink="true">https://aliencoder.tistory.com/220</guid>
      <comments>https://aliencoder.tistory.com/220#entry220comment</comments>
      <pubDate>Thu, 10 Apr 2025 12:26:38 +0900</pubDate>
    </item>
    <item>
      <title>Discrete-Time Fourier Transform(DTFT)과 Discrete Fourier Transform(DFT)</title>
      <link>https://aliencoder.tistory.com/219</link>
      <description>&lt;script src=&quot;https://polyfill.io/v3/polyfill.min.js?features=es6&quot;&gt;&lt;/script&gt;
&lt;script src=&quot;https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js&quot;&gt;&lt;/script&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Discrete-Time Fourier Transform(DTFT)&lt;/b&gt;와 &lt;b&gt;Discrete Fourier Transform(DFT)&lt;/b&gt;는 모두 이산 신호의 주파수 특성을 분석하기 위한 도구로 사용된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-end=&quot;1018&quot; data-start=&quot;853&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;&lt;b&gt;항목&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;&lt;b&gt;DTFT&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;&lt;b&gt;DFT&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;914&quot; data-start=&quot;890&quot;&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;&lt;b&gt;신호 길이&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;무한&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;유한 (N개)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;946&quot; data-start=&quot;915&quot;&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;&lt;b&gt;주파수 축&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;연속적 (무한)&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;이산적 (N개)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;987&quot; data-start=&quot;947&quot;&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;&lt;b&gt;존재 이유&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;이론적 분석 (연속 주파수)&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;실제 계산 및 구현&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;1018&quot; data-start=&quot;988&quot;&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;&lt;b&gt;계산 가능성&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;불가능&lt;/td&gt;
&lt;td style=&quot;text-align: center;&quot;&gt;가능 (FFT 사용)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Discrete-Time&amp;nbsp;Fourier&amp;nbsp;Transform(DTFT)&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Discrete-Time&amp;nbsp;Fourier&amp;nbsp;Transform(DTFT)&lt;/b&gt;는 무한 길이의 이산 신호를 연속적인 주파수 변수(&amp;omega;)에 대해 변환하는 수학적 도구이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시간 이산 신호 \( x[n] \)에 대한 DTFT는 다음과 같이 정의된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ X(e^{j\omega}) = \sum_{n=-\infty}^{\infty} x[n] e^{-j \omega n} $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Inverse DTFT&lt;/b&gt;는 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ x[n] = \frac{1}{2\pi} \int_{-\pi}^{\pi} X(e^{j\omega}) e^{j \omega n} d\omega $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또는 주파수 \( f \)를 사용하여 표현할 수도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$&amp;nbsp;X(f)&amp;nbsp;=&amp;nbsp;\sum_{n=-\infty}^{\infty}&amp;nbsp;x[n]&amp;nbsp;e^{-j&amp;nbsp;2\pi&amp;nbsp;f&amp;nbsp;n}&amp;nbsp;$$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이에 대한 &lt;b&gt;Inverse DTFT&lt;/b&gt;는 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ x[n] = \int_{-1/2}^{1/2} X(f) e^{j 2\pi f n} df $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DTFT는 이산 신호의 연속적인 주파수 성분을 이론적으로 분석하기 위해 필요하다. 이는 아날로그 푸리에 변환과 대응되게 이산 신호 및 이산 시스템의 주파수 특성을 다루기 위해 존재한다. 특히 필터나 시스템의 주파수 응답을 정확히 이해하는 데 사용된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특징은 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;입력 신호가 무한 길이일 수 있다.&lt;/li&gt;
&lt;li&gt;주파수 변수는 연속적이다 (&amp;omega; &amp;isin; [&amp;ndash;&amp;pi;, &amp;pi;]).&lt;/li&gt;
&lt;li&gt;실제로는&amp;nbsp;무한&amp;nbsp;데이터를&amp;nbsp;다루기&amp;nbsp;때문에&amp;nbsp;수치적으로&amp;nbsp;계산할&amp;nbsp;수&amp;nbsp;없다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 하나 풀어보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;예제 1&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단순한 이산 신호를 고려하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ x[n] =&amp;nbsp; &lt;br /&gt;\begin{cases} &lt;br /&gt;1,&amp;nbsp;&amp;amp;&amp;nbsp;0&amp;nbsp;\leq&amp;nbsp;n&amp;nbsp;\leq&amp;nbsp;2&amp;nbsp;\\ &lt;br /&gt;0,&amp;nbsp;&amp;amp;&amp;nbsp;\text{otherwise} &lt;br /&gt;\end{cases} $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 신호는 \( n=0,1,2 \)에서 값이 1이고, 그 외에는 0이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이&amp;nbsp;신호는&amp;nbsp;\(&amp;nbsp;n=0,1,2&amp;nbsp;\)만&amp;nbsp;값이&amp;nbsp;1이므로&lt;br /&gt;$$ X(e^{j\omega}) = 1 \cdot e^{-j \omega \cdot 0} + 1 \cdot e^{-j \omega \cdot 1} + 1 \cdot e^{-j \omega \cdot 2} $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ = 1 + e^{-j \omega} + e^{-j 2\omega} $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 결과는 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ X(e^{j\omega}) = 1 + e^{-j\omega} + e^{-j2\omega} $$&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;635&quot; data-origin-height=&quot;432&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lDn7v/btsNcnA0Jvj/M2SQcvFwgV1R6vYisapwpK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lDn7v/btsNcnA0Jvj/M2SQcvFwgV1R6vYisapwpK/img.png&quot; data-alt=&quot;Fig 1. DTFT Magnitude&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lDn7v/btsNcnA0Jvj/M2SQcvFwgV1R6vYisapwpK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlDn7v%2FbtsNcnA0Jvj%2FM2SQcvFwgV1R6vYisapwpK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;466&quot; height=&quot;317&quot; data-origin-width=&quot;635&quot; data-origin-height=&quot;432&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Fig 1. DTFT Magnitude&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;가로축:&amp;nbsp;\(\omega\)&amp;nbsp;(단위:&amp;nbsp;radians/sample)이다.&lt;/li&gt;
&lt;li&gt;\(\omega\):&amp;nbsp;\(-\pi\)에서&amp;nbsp;\(\pi\)까지&amp;nbsp;연속적으로&amp;nbsp;변한다.&lt;/li&gt;
&lt;li&gt;세로축:&amp;nbsp;DTFT의&amp;nbsp;크기&amp;nbsp;\(|X(e^{j\omega})|\)이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 그래프가 바로 DTFT 결과를 보여주는 그림이다. 우리가 알고 싶은 것은 &quot;주파수 &amp;omega;마다 신호의 에너지가 얼마나 존재하는가&quot;이다. 복소수는 방향(위상)도 가지고 있다. 따라서 절대값을 취하여 복소수의 순수한 세기(Strength)만 확인이 가능하게 바꿔주어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ |a + jb| = \sqrt{a^2 + b^2} $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;그래프를 보면, \(-\pi\)에서 시작해서 \(\pi\)까지 부드럽게 연결되고 있으며, \(-\pi\)와 \(\pi\)에서 그래프가 이어지듯 반복되려고 한다. 이는 주파수 \( w \)에 대해 연속적이로 \( [-\pi, \pi] \) 사이를 연속적으로 움직일 수 있다는 것을 의미한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 이 결과는 0Hz 근처에서 크고, 고주파수로 갈수록 점점 작아진다. 따라서 지금 신호는 거의 &quot;DC 성분&quot;만 가지고 있다고 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Discrete&amp;nbsp;Fourier&amp;nbsp;Transform(DFT)&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Discrete&amp;nbsp;Fourier&amp;nbsp;Transform(DFT)&lt;/b&gt;는 유한 길이의 이산 신호를 유한 개수의 주파수 성분으로 변환하는 수학적 도구이다. &lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;길이 \( N \)인 이산 신호 \( x[n] \)에 대한 DFT는 다음과 같이 정의된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ X[k] = \sum_{n=0}^{N-1} x[n] e^{-j \frac{2\pi}{N} kn} $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Inverse DFT&lt;/b&gt; 정의는 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ x[n] = \frac{1}{N} \sum_{k=0}^{N-1} X[k] e^{j \frac{2\pi}{N} kn} $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 컴퓨터는 무한한 데이터를 다룰 수 없기 때문에, DTFT를 수치적으로 계산할 수 있는 형태로 만든 것이 DFT이다. 디지털&amp;nbsp;신호&amp;nbsp;처리,&amp;nbsp;오디오&amp;nbsp;및&amp;nbsp;영상&amp;nbsp;처리,&amp;nbsp;통신&amp;nbsp;시스템&amp;nbsp;등&amp;nbsp;다양한&amp;nbsp;분야에서&amp;nbsp;실제&amp;nbsp;주파수&amp;nbsp;분석과&amp;nbsp;변환&amp;nbsp;작업을&amp;nbsp;수행하기&amp;nbsp;위해&amp;nbsp;존재한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특징은 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;입력&amp;nbsp;신호는&amp;nbsp;유한&amp;nbsp;길이(N개)이어야&amp;nbsp;한다.&lt;/li&gt;
&lt;li&gt;주파수도 이산적이다 (N개의 고정된 주파수 포인트를 가진다).&lt;/li&gt;
&lt;li&gt;수치적으로 계산할 수 있다 (FFT 알고리즘 등 사용).&lt;/li&gt;
&lt;li&gt;DFT는 DTFT를 \( 2\pi \) 간격&amp;nbsp;안에서&amp;nbsp;이산적으로&amp;nbsp;샘플링한&amp;nbsp;것과&amp;nbsp;같다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음과 같이 DTFT를 이산적으로 샘플링하면 DFT가 된다. 즉, DTFT를 \( w = \frac{2\pi}{N}k \) 지점에서 샘플링하여 DFT를 만든다.&lt;br /&gt;$$&amp;nbsp;X[k]&amp;nbsp;=&amp;nbsp;X(e^{j\omega})&amp;nbsp;\Big|_{\omega&amp;nbsp;=&amp;nbsp;\frac{2\pi}{N}k}&amp;nbsp;$$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;예제 1&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 DTFT와 같은 신호를 사용하여 길이 \( N=3 \)로 DFT를 구하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ x[n] =&amp;nbsp; &lt;br /&gt;\begin{cases} &lt;br /&gt;1,&amp;nbsp;&amp;amp;&amp;nbsp;0&amp;nbsp;\leq&amp;nbsp;n&amp;nbsp;\leq&amp;nbsp;2&amp;nbsp;\\ &lt;br /&gt;0,&amp;nbsp;&amp;amp;&amp;nbsp;\text{otherwise} &lt;br /&gt;\end{cases}&amp;nbsp;$$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ x[0] = 1, \quad x[1] = 1, \quad x[2] = 1 $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 \( N = 3, \, k = 0,1,2 \)이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;\begin{aligned} &lt;br /&gt;\textbf{k&amp;nbsp;=&amp;nbsp;0:}&amp;nbsp;\quad&amp;nbsp;&amp;amp;&amp;nbsp;\\ &lt;br /&gt;X[0]&amp;nbsp;&amp;amp;=&amp;nbsp;1&amp;nbsp;\cdot&amp;nbsp;e^{-j\frac{2\pi}{3}&amp;nbsp;\cdot&amp;nbsp;0&amp;nbsp;\cdot&amp;nbsp;0}&amp;nbsp;+&amp;nbsp;1&amp;nbsp;\cdot&amp;nbsp;e^{-j\frac{2\pi}{3}&amp;nbsp;\cdot&amp;nbsp;0&amp;nbsp;\cdot&amp;nbsp;1}&amp;nbsp;+&amp;nbsp;1&amp;nbsp;\cdot&amp;nbsp;e^{-j\frac{2\pi}{3}&amp;nbsp;\cdot&amp;nbsp;0&amp;nbsp;\cdot&amp;nbsp;2}&amp;nbsp;\\ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;amp;=&amp;nbsp;1&amp;nbsp;+&amp;nbsp;1&amp;nbsp;+&amp;nbsp;1&amp;nbsp;=&amp;nbsp;3&amp;nbsp;\\[1.2em] &lt;br /&gt;&lt;br /&gt;\textbf{k&amp;nbsp;=&amp;nbsp;1:}&amp;nbsp;\quad&amp;nbsp;&amp;amp;&amp;nbsp;\\ &lt;br /&gt;X[1]&amp;nbsp;&amp;amp;=&amp;nbsp;1&amp;nbsp;\cdot&amp;nbsp;e^{-j\frac{2\pi}{3}&amp;nbsp;\cdot&amp;nbsp;1&amp;nbsp;\cdot&amp;nbsp;0}&amp;nbsp;+&amp;nbsp;1&amp;nbsp;\cdot&amp;nbsp;e^{-j\frac{2\pi}{3}&amp;nbsp;\cdot&amp;nbsp;1&amp;nbsp;\cdot&amp;nbsp;1}&amp;nbsp;+&amp;nbsp;1&amp;nbsp;\cdot&amp;nbsp;e^{-j\frac{2\pi}{3}&amp;nbsp;\cdot&amp;nbsp;1&amp;nbsp;\cdot&amp;nbsp;2}&amp;nbsp;\\ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;amp;=&amp;nbsp;1&amp;nbsp;+&amp;nbsp;e^{-j\frac{2\pi}{3}}&amp;nbsp;+&amp;nbsp;e^{-j\frac{4\pi}{3}}&amp;nbsp;\\ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;amp;=&amp;nbsp;1&amp;nbsp;+&amp;nbsp;\left(&amp;nbsp;-\frac{1}{2}&amp;nbsp;-&amp;nbsp;j\frac{\sqrt{3}}{2}&amp;nbsp;\right)&amp;nbsp;+&amp;nbsp;\left(&amp;nbsp;-\frac{1}{2}&amp;nbsp;+&amp;nbsp;j\frac{\sqrt{3}}{2}&amp;nbsp;\right)&amp;nbsp;\\ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;amp;=&amp;nbsp;0&amp;nbsp;\\[1.2em] &lt;br /&gt;&lt;br /&gt;\textbf{k&amp;nbsp;=&amp;nbsp;2:}&amp;nbsp;\quad&amp;nbsp;&amp;amp;&amp;nbsp;\\ &lt;br /&gt;X[2]&amp;nbsp;&amp;amp;=&amp;nbsp;1&amp;nbsp;\cdot&amp;nbsp;e^{-j\frac{2\pi}{3}&amp;nbsp;\cdot&amp;nbsp;2&amp;nbsp;\cdot&amp;nbsp;0}&amp;nbsp;+&amp;nbsp;1&amp;nbsp;\cdot&amp;nbsp;e^{-j\frac{2\pi}{3}&amp;nbsp;\cdot&amp;nbsp;2&amp;nbsp;\cdot&amp;nbsp;1}&amp;nbsp;+&amp;nbsp;1&amp;nbsp;\cdot&amp;nbsp;e^{-j\frac{2\pi}{3}&amp;nbsp;\cdot&amp;nbsp;2&amp;nbsp;\cdot&amp;nbsp;2}&amp;nbsp;\\ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;amp;=&amp;nbsp;1&amp;nbsp;+&amp;nbsp;e^{-j\frac{4\pi}{3}}&amp;nbsp;+&amp;nbsp;e^{-j\frac{8\pi}{3}}&amp;nbsp;\\ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;amp;=&amp;nbsp;0 &lt;br /&gt;\end{aligned}&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;\( ※ \text{(단, } e^{-j\frac{8\pi}{3}} = e^{-j\frac{2\pi}{3}} \text{ 주기성 이용)} \) =&amp;gt; 한바퀴 돌면 원위치이므로&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ X[0]=3, \, X[1]=0, \, X[2]=0 $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 모든 값이 0이고 DC 성분(0Hz)만 존재한다는 의미가 된다. 당연히 DTFT와 동일한 결과를 보여준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;예제 2&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;\( x[0]=1, \, x[1]=0, \, x[2]=&amp;minus;1, \, x[3]=0 \)일 때 DFT를 이용하여 변환해 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;풀이와 정답은 아래를 펼치면 나온다. 직접&amp;nbsp;풀어보기를&amp;nbsp;추천한다.&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ X[k] = &amp;sum;_{n=0}^{N-1} x[n] &amp;middot; W_N^{kn}, \quad where \quad W^{kn}_{N} = e^{-j \frac{2\pi}{N} kn} $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$&lt;br /&gt;\begin{aligned} &lt;br /&gt;k=0:&amp;nbsp;\quad&amp;nbsp;&amp;amp;&amp;nbsp;\\ &lt;br /&gt;X[0]&amp;nbsp;&amp;amp;=&amp;nbsp;x[0]&amp;nbsp;\cdot&amp;nbsp;W_4^{0}&amp;nbsp;+&amp;nbsp;x[1]&amp;nbsp;\cdot&amp;nbsp;W_4^{0}&amp;nbsp;+&amp;nbsp;x[2]&amp;nbsp;\cdot&amp;nbsp;W_4^{0}&amp;nbsp;+&amp;nbsp;x[3]&amp;nbsp;\cdot&amp;nbsp;W_4^{0}&amp;nbsp;\\ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;amp;=&amp;nbsp;1&amp;nbsp;+&amp;nbsp;0&amp;nbsp;+&amp;nbsp;(-1)&amp;nbsp;+&amp;nbsp;0&amp;nbsp;=&amp;nbsp;0&amp;nbsp;\\[1.2em] &lt;br /&gt;&lt;br /&gt;k=1:&amp;nbsp;\quad&amp;nbsp;&amp;amp;&amp;nbsp;\\ &lt;br /&gt;X[1]&amp;nbsp;&amp;amp;=&amp;nbsp;x[0]&amp;nbsp;\cdot&amp;nbsp;W_4^{0}&amp;nbsp;+&amp;nbsp;x[1]&amp;nbsp;\cdot&amp;nbsp;W_4^{1}&amp;nbsp;+&amp;nbsp;x[2]&amp;nbsp;\cdot&amp;nbsp;W_4^{2}&amp;nbsp;+&amp;nbsp;x[3]&amp;nbsp;\cdot&amp;nbsp;W_4^{3}&amp;nbsp;\\ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;amp;=&amp;nbsp;1&amp;nbsp;\cdot&amp;nbsp;1&amp;nbsp;+&amp;nbsp;0&amp;nbsp;\cdot&amp;nbsp;(-j)&amp;nbsp;+&amp;nbsp;(-1)&amp;nbsp;\cdot&amp;nbsp;(-1)&amp;nbsp;+&amp;nbsp;0&amp;nbsp;\cdot&amp;nbsp;j&amp;nbsp;\\ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;amp;=&amp;nbsp;1&amp;nbsp;+&amp;nbsp;0&amp;nbsp;+&amp;nbsp;1&amp;nbsp;+&amp;nbsp;0&amp;nbsp;=&amp;nbsp;2&amp;nbsp;\\[1.2em] &lt;br /&gt;&lt;br /&gt;k=2:&amp;nbsp;\quad&amp;nbsp;&amp;amp;&amp;nbsp;\\ &lt;br /&gt;X[2]&amp;nbsp;&amp;amp;=&amp;nbsp;x[0]&amp;nbsp;\cdot&amp;nbsp;W_4^{0}&amp;nbsp;+&amp;nbsp;x[1]&amp;nbsp;\cdot&amp;nbsp;W_4^{2}&amp;nbsp;+&amp;nbsp;x[2]&amp;nbsp;\cdot&amp;nbsp;W_4^{4}&amp;nbsp;+&amp;nbsp;x[3]&amp;nbsp;\cdot&amp;nbsp;W_4^{6}&amp;nbsp;\\ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;amp;=&amp;nbsp;1&amp;nbsp;\cdot&amp;nbsp;1&amp;nbsp;+&amp;nbsp;0&amp;nbsp;\cdot&amp;nbsp;(-1)&amp;nbsp;+&amp;nbsp;(-1)&amp;nbsp;\cdot&amp;nbsp;1&amp;nbsp;+&amp;nbsp;0&amp;nbsp;\cdot&amp;nbsp;(-1)&amp;nbsp;\\ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;amp;=&amp;nbsp;1&amp;nbsp;+&amp;nbsp;0&amp;nbsp;-&amp;nbsp;1&amp;nbsp;+&amp;nbsp;0&amp;nbsp;=&amp;nbsp;0&amp;nbsp;\\[1.2em] &lt;br /&gt;&lt;br /&gt;k=3:&amp;nbsp;\quad&amp;nbsp;&amp;amp;&amp;nbsp;\\ &lt;br /&gt;X[3]&amp;nbsp;&amp;amp;=&amp;nbsp;x[0]&amp;nbsp;\cdot&amp;nbsp;W_4^{0}&amp;nbsp;+&amp;nbsp;x[1]&amp;nbsp;\cdot&amp;nbsp;W_4^{3}&amp;nbsp;+&amp;nbsp;x[2]&amp;nbsp;\cdot&amp;nbsp;W_4^{6}&amp;nbsp;+&amp;nbsp;x[3]&amp;nbsp;\cdot&amp;nbsp;W_4^{9}&amp;nbsp;\\ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;amp;=&amp;nbsp;1&amp;nbsp;\cdot&amp;nbsp;1&amp;nbsp;+&amp;nbsp;0&amp;nbsp;\cdot&amp;nbsp;j&amp;nbsp;+&amp;nbsp;(-1)&amp;nbsp;\cdot&amp;nbsp;(-1)&amp;nbsp;+&amp;nbsp;0&amp;nbsp;\cdot&amp;nbsp;(-j)&amp;nbsp;\\ &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;amp;=&amp;nbsp;1&amp;nbsp;+&amp;nbsp;0&amp;nbsp;+&amp;nbsp;1&amp;nbsp;+&amp;nbsp;0&amp;nbsp;=&amp;nbsp;2 &lt;br /&gt;\end{aligned}&lt;br /&gt;$$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;※ 오일러 공식에 따라 복소 지수 함수 \( e^{&amp;minus;j&amp;pi;} = e^{j&amp;pi;} = &amp;minus;1 \)이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;\(&amp;nbsp;e^{j\theta}&amp;nbsp;=&amp;nbsp;\cos(\theta)&amp;nbsp;+&amp;nbsp;j\sin(\theta)&amp;nbsp;\)이고,&amp;nbsp;\(&amp;nbsp;\sin(\pi)&amp;nbsp;=&amp;nbsp;0&amp;nbsp;\)이므로&amp;nbsp;\(&amp;nbsp;j&amp;nbsp;\)의&amp;nbsp;부호는&amp;nbsp;무관하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;정답: \( X[0]=0, \, X[1]=2, \, X[2]=0, \, X[3]=2 \)&lt;/b&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번엔 주파수까지 구해보자. 주파수는 다음과 같이 구할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ \text{주파수} = \frac{N}{k} &amp;times; f_s $$&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;\( k \): 주파수 인덱스&lt;/li&gt;
&lt;li&gt;\( N \): DFT 포인트 수​&lt;/li&gt;
&lt;li&gt;\( f_s \): 샘플링 주파수 (sampling frequency)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제 2에서 \( N = 4 \)이고 \( f_s = 4Hz \)라 가정하면 \( k=1 \)일 때 \( 1Hz \), \( k=3 \)일 때 \( 3Hz \)가 된다. 이를 그래프로 표현하면 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;760&quot; data-origin-height=&quot;348&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/n5Ihe/btsNqlJ0hdj/g1SEM3VYA8SWkJIIk3ri41/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/n5Ihe/btsNqlJ0hdj/g1SEM3VYA8SWkJIIk3ri41/img.png&quot; data-alt=&quot;Fig 2. DFT&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/n5Ihe/btsNqlJ0hdj/g1SEM3VYA8SWkJIIk3ri41/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fn5Ihe%2FbtsNqlJ0hdj%2Fg1SEM3VYA8SWkJIIk3ri41%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;509&quot; height=&quot;233&quot; data-origin-width=&quot;760&quot; data-origin-height=&quot;348&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Fig 2. DFT&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 \( f_s = 8Hz \)라면 \( k=1 \)일 때 \( 2Hz \), \( k=3 \)일 때 \( 6Hz \)가 나오게 된다. 샘플링 주파수에 따라 구해진 1번과 3번 주파수 성분이 결정된다는 것을 알 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 &lt;b&gt;나이퀴스트 주파수&lt;/b&gt;에 의해 사실 최대 표현 가능 주파수는 샘플링 주파수의 절반까지이다. 만약 \( f_s = 8Hz \)라면 표현 가능한 주파수는 \( 4Hz \)까지이다. \( 4Hz \) 이상의 주파수인 6Hz는 &quot;진짜 주파수&quot;가 아니라 &lt;b&gt;Aliasing&lt;/b&gt;된 결과이지만 이 내용은 더 설명이 필요하니 다음에 다루도록 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;결론&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DTFT는 이론적 분석을 위해 존재하며, DFT는&amp;nbsp;현실적인&amp;nbsp;계산과&amp;nbsp;구현을&amp;nbsp;위해&amp;nbsp;존재한다. &lt;br /&gt;&lt;br /&gt;DTFT 없이는 시스템과 신호를 수학적으로 깊이 이해할 수 없고, DFT 없이는 실제 환경에서 주파수 변환과 분석을 수행할 수 없다. 이는 추후 FFT와도 연관이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;관련 포스팅&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://aliencoder.tistory.com/217&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;2025.04.08 - [Data Science/SR &amp;amp; VC] - 푸리에 급수(Fourier Series)와 푸리에 계수(Fourier Coefficient)&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://aliencoder.tistory.com/218&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;2025.04.08 - [Data Science/SR &amp;amp; VC] - 오일러 공식(Euler's Formula)과 푸리에 변환(Fourier Transform)&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>AI/SR &amp;amp; VC</category>
      <category>dft</category>
      <category>discrete fourier transform</category>
      <category>discrete-time fourier transform</category>
      <category>DTFT</category>
      <author>AlienCoder</author>
      <guid isPermaLink="true">https://aliencoder.tistory.com/219</guid>
      <comments>https://aliencoder.tistory.com/219#entry219comment</comments>
      <pubDate>Tue, 8 Apr 2025 17:18:46 +0900</pubDate>
    </item>
    <item>
      <title>오일러 공식(Euler's Formula)과 푸리에 변환(Fourier Transform)</title>
      <link>https://aliencoder.tistory.com/218</link>
      <description>&lt;script src=&quot;https://polyfill.io/v3/polyfill.min.js?features=es6&quot;&gt;&lt;/script&gt;
&lt;script src=&quot;https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js&quot;&gt;&lt;/script&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;푸리에 변환에 대해 정리해보려 한다. 주파수 분해를 위해 사용되는 여러 가지 공학분야에서 없어서는 안되는 수식 중 하나이다. 일반적으로 우리가 &lt;b&gt;푸리에 변환(Fourier Transform)&lt;/b&gt;이라 부르는 것은 &lt;b&gt;Continuous Fourier Transform(CFT)&lt;/b&gt;이며 이는 &lt;b&gt;Continuous-Time Fourier Transform(CTFT)&lt;/b&gt;로 불리기도 한다. 푸리에 변환 이론 자체가 처음에는 연속 신호를 다루는 데서 출발했기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;푸리에&amp;nbsp;변환을&amp;nbsp;진행하기&amp;nbsp;전&amp;nbsp;사전지식이&amp;nbsp;어느 정도&amp;nbsp;필요하다.&amp;nbsp;바로&amp;nbsp;복소수와&amp;nbsp;복소&amp;nbsp;공간&amp;nbsp;그리고&amp;nbsp;오일러&amp;nbsp;공식에&amp;nbsp;대한&amp;nbsp;이해이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;복소평면(Complex Plane)&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 복소&amp;nbsp;공간이란&amp;nbsp;복소수로&amp;nbsp;구성된&amp;nbsp;수학적&amp;nbsp;공간을&amp;nbsp;의미한다.&amp;nbsp;복소수는&amp;nbsp;실수부와&amp;nbsp;허수부를&amp;nbsp;가지는&amp;nbsp;수로,&amp;nbsp;다음과&amp;nbsp;같이&amp;nbsp;표현한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ z = x + iy $$&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;\( x \): 실수부(Real Part)&lt;/li&gt;
&lt;li&gt;\( y \): 허수부(Imaginay Part)&lt;/li&gt;
&lt;li&gt;\( i \): 허수 단위로 \( i^2 = -1 \)을 만족&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;1024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bkPgcC/btsNbRIL4T1/AcOgB5kcNZwzAstV75JEuk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bkPgcC/btsNbRIL4T1/AcOgB5kcNZwzAstV75JEuk/img.png&quot; data-alt=&quot;Fig 1. Complex Plane&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bkPgcC/btsNbRIL4T1/AcOgB5kcNZwzAstV75JEuk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbkPgcC%2FbtsNbRIL4T1%2FAcOgB5kcNZwzAstV75JEuk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;365&quot; height=&quot;365&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;1024&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Fig 1. Complex Plane&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;복소수 \( z \)는 2차원 평면에서 하나의 점으로 생각할 수 있다. 이 평면을 &lt;b&gt;복소 평면(Complex Plane)&lt;/b&gt;이라고 한다. &lt;br /&gt;$$ |z| = \sqrt{x^2 + y^2} $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 극형식(polar form)으로 나타내면 다음과 같다.&lt;br /&gt;$$ z = r (\cos \theta + i \sin \theta) $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;482&quot; data-origin-height=&quot;288&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dfoQ98/btsNctN9u1c/uxR5hrAT7EkZSLIF0efQK0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dfoQ98/btsNctN9u1c/uxR5hrAT7EkZSLIF0efQK0/img.png&quot; data-alt=&quot;Fig 2. Polar form&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dfoQ98/btsNctN9u1c/uxR5hrAT7EkZSLIF0efQK0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdfoQ98%2FbtsNctN9u1c%2FuxR5hrAT7EkZSLIF0efQK0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;420&quot; height=&quot;251&quot; data-origin-width=&quot;482&quot; data-origin-height=&quot;288&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Fig 2. Polar form&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 복소수는 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;\( z_1 = r_1 e^{i\theta_1} \)&lt;/li&gt;
&lt;li&gt;\( z_2 = r_2 e^{i\theta_2} \)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 이 두 복소수에 대해 곱은 다음과 같이 계산한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ z_1 z_2 = (r_1 r_2) e^{i(\theta_1 + \theta_2)} $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 크기는 \( r_1 &amp;times; r_2 \)와 같이 곱하고 위상은 \( \theta_1 + \theta_2 \)와 같이 더한다. 길이는 곱하고 위상은 더한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;오일러&amp;nbsp;공식(Euler's&amp;nbsp;Formula)&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오일러&amp;nbsp;공식은&amp;nbsp;복소수와&amp;nbsp;삼각함수&amp;nbsp;사이의&amp;nbsp;깊은&amp;nbsp;연결을&amp;nbsp;보여주는&amp;nbsp;식이다.&amp;nbsp;다음과&amp;nbsp;같이&amp;nbsp;표현된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ e^{i\theta} = \cos(\theta) + i\sin(\theta) $$&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1034&quot; data-origin-height=&quot;652&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cQsepW/btsNeoE5Opp/sMafikgFo2Kdonw5rHqi41/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cQsepW/btsNeoE5Opp/sMafikgFo2Kdonw5rHqi41/img.png&quot; data-alt=&quot;Fig 3. Euler's Formula&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cQsepW/btsNeoE5Opp/sMafikgFo2Kdonw5rHqi41/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcQsepW%2FbtsNeoE5Opp%2FsMafikgFo2Kdonw5rHqi41%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;617&quot; height=&quot;389&quot; data-origin-width=&quot;1034&quot; data-origin-height=&quot;652&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Fig 3. Euler's Formula&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 공식은 푸리에 급수 및 푸리에 변환과 밀접한 관련이 있다. 특히 \( e^{j\theta} \)로 형태로 복소 평면에서 표현할 수 있으며, 이는 원점을 중심으로 반지름이 1인 단위원 상을 회전하는 것을 의미한다. 여기서 \( j \)는 공학에서 일반적으로 사용하는 허수 단위이다. 즉, 복소 지수 함수 \(&amp;nbsp;e^{j\theta}&amp;nbsp;\)는&amp;nbsp;시간&amp;nbsp;또는&amp;nbsp;공간에&amp;nbsp;따라&amp;nbsp;회전하는&amp;nbsp;벡터를&amp;nbsp;나타낸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;뒤에서 설명하겠지만, 푸리에 변환은 신호를 다양한 주파수 성분으로 분해하고, 각 주파수 성분을 이러한 복소 지수 함수(단위원 회전)로 표현하는 것이다. 결국 신호는 여러 회전하는 벡터의 조합으로 해석할 수 있다. 이를&amp;nbsp;그림과&amp;nbsp;함께&amp;nbsp;살펴보면&amp;nbsp;훨씬&amp;nbsp;직관적으로&amp;nbsp;이해할&amp;nbsp;수&amp;nbsp;있을&amp;nbsp;것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;푸리에 변환(Fourier Transform)&lt;/b&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;470&quot; data-origin-height=&quot;216&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ncmrC/btsNcs3lsJq/a728tFMAoFHBrclhZH9MZ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ncmrC/btsNcs3lsJq/a728tFMAoFHBrclhZH9MZ0/img.png&quot; data-alt=&quot;Fig 4. Fourier Transform&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ncmrC/btsNcs3lsJq/a728tFMAoFHBrclhZH9MZ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FncmrC%2FbtsNcs3lsJq%2Fa728tFMAoFHBrclhZH9MZ0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;531&quot; height=&quot;244&quot; data-origin-width=&quot;470&quot; data-origin-height=&quot;216&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Fig 4. Fourier Transform&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-pm-slice=&quot;1 1 []&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-pm-slice=&quot;1 1 []&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;b&gt;푸리에 변환(Fourier Transform)&lt;/b&gt;은 &lt;b&gt;시간(domain)&lt;/b&gt;에서 주어진 신호를 &lt;b&gt;주파수(domain)&lt;/b&gt;로 변환하는 수학적 도구이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;쉽게 말하면, 복잡한 신호를 주파수 성분(사인파, 코사인파)으로 분해하는 방법이다. &lt;/span&gt;&lt;span&gt;푸리에 변환을 통해 우리는 신호가 어떤 주파수 성분으로 이루어져 있는지 분석할 수 있다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;쉽게 말해 여러 가지 색깔들의 물감을 한데 뒤섞어놓고 이를 다시 분해하는 것으로 상상할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1140&quot; data-origin-height=&quot;562&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cyyPV0/btsNcfo3cK6/gPF3FHxXcPGuc0dyr79KX0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cyyPV0/btsNcfo3cK6/gPF3FHxXcPGuc0dyr79KX0/img.png&quot; data-alt=&quot;Fig 5. Fourier Transform lecture 1&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cyyPV0/btsNcfo3cK6/gPF3FHxXcPGuc0dyr79KX0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcyyPV0%2FbtsNcfo3cK6%2FgPF3FHxXcPGuc0dyr79KX0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;618&quot; height=&quot;305&quot; data-origin-width=&quot;1140&quot; data-origin-height=&quot;562&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Fig 5. Fourier Transform lecture 1&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 그림에서 상단 그래프는 3Hz의 신호이다. 그림에 나와있듯 이 상단 그래프를 좌측 하단의 복소 평면에 나타나고 있는 원에 감는 것이다. 위 그림에선 초당 약 0.25바퀴를 감고 있는 것으로 볼 수 있다. 이 &lt;b&gt;'감는 진동수'&lt;/b&gt;를 조절하면 &lt;b&gt;'감긴 그래프'&lt;/b&gt;가 어떻게 생길지 결정된다. 매 시점 원에서 나타나는 벡터의 크기는 해당 시점의 3Hz 신호의 크기와 동일하다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 저 '감는 진동수'가 하나의 긴 철사라고 생각해 보자. 그렇다면 저 철사들은 무게 중심이 생길 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1130&quot; data-origin-height=&quot;558&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nj7om/btsNc7qkrpL/RXlgRIEYV9JV4zKAIV0Ud0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nj7om/btsNc7qkrpL/RXlgRIEYV9JV4zKAIV0Ud0/img.png&quot; data-alt=&quot;Fig 6. Fourier Transform lecture 2&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nj7om/btsNc7qkrpL/RXlgRIEYV9JV4zKAIV0Ud0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fnj7om%2FbtsNc7qkrpL%2FRXlgRIEYV9JV4zKAIV0Ud0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;607&quot; height=&quot;300&quot; data-origin-width=&quot;1130&quot; data-origin-height=&quot;558&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Fig 6. Fourier Transform lecture 2&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보통은 무게 중심은 원점을 크게 벗어나지 않을 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1863&quot; data-origin-height=&quot;940&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BYz5E/btsNcuzCFgo/14otUIRt9qyi0StmIOtiO1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BYz5E/btsNcuzCFgo/14otUIRt9qyi0StmIOtiO1/img.png&quot; data-alt=&quot;Fig 7. Fourier Transform lecture 3&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BYz5E/btsNcuzCFgo/14otUIRt9qyi0StmIOtiO1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBYz5E%2FbtsNcuzCFgo%2F14otUIRt9qyi0StmIOtiO1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;604&quot; height=&quot;305&quot; data-origin-width=&quot;1863&quot; data-origin-height=&quot;940&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Fig 7. Fourier Transform lecture 3&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 초당 3바퀴를 이 원에 감게 되어 진동수(3Hz)와 일치하게 되면 무게 중심은 한쪽으로 치우치게 되며 오른쪽 하단 &lt;b&gt;'x 좌표의 무게 중심'&lt;/b&gt; 그래프에서 확인할 수 있듯 3에서 무게 중심이 원점을 크게 벗어나게 된다. 이를 &lt;b&gt;Spike&lt;/b&gt;라고 한다. 0에서 Spike가 존재하는 이유는 해당 설명에서 주기 파형이 0 이상인 양수 범위이기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1131&quot; data-origin-height=&quot;555&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qY0f5/btsNboM5aJd/icZopvXujPjWfQereSrZ9K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qY0f5/btsNboM5aJd/icZopvXujPjWfQereSrZ9K/img.png&quot; data-alt=&quot;Fig 8. Fourier Transform lecture 4&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qY0f5/btsNboM5aJd/icZopvXujPjWfQereSrZ9K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqY0f5%2FbtsNboM5aJd%2FicZopvXujPjWfQereSrZ9K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;634&quot; height=&quot;311&quot; data-origin-width=&quot;1131&quot; data-origin-height=&quot;555&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Fig 8. Fourier Transform lecture 4&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;잠깐 부가적으로 설명하자면 만약 해당 주기 함수가 -1~1 사이로 진동하게 된다면 0에서의 Spike는 사라질 것이다. 참고로 이 'x 좌표의 무게 중심' 그래프는 실수부만 표현하고 있는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1130&quot; data-origin-height=&quot;558&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HrUvB/btsNcnm9tuf/BcA4K1wn8BsNj0mrRm3J50/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HrUvB/btsNcnm9tuf/BcA4K1wn8BsNj0mrRm3J50/img.png&quot; data-alt=&quot;Fig 9. Fourier Transform lecture 5&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HrUvB/btsNcnm9tuf/BcA4K1wn8BsNj0mrRm3J50/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHrUvB%2FbtsNcnm9tuf%2FBcA4K1wn8BsNj0mrRm3J50%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;606&quot; height=&quot;305&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1130&quot; data-origin-height=&quot;558&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Fig 9. Fourier Transform lecture 5&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다시 돌아와 위 그림은 2Hz와 3Hz 두 개의 주파수를 합성한 뒤 다시 감긴 그래프를 그린 결과이다. 이 역시 특정 주파수와 감는 진동수가 일치할 때 무게 중심이 치우치게 되어 Spike가 생기는 것을 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기까지 설명한 것이 푸리에 변환에 대한 기본적인 아이디어이다. 내가 참고한 영상에선 이를 '거의 푸리에 변환'이라 칭한다. 아직 푸리에 변환에 사용되는 좀 더 복잡한 내용들에 대한 설명은 포함되어있지 않기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 위에서 언급하였던 &lt;b&gt;복소 평면&lt;/b&gt;과 &lt;b&gt;오일러 공식&lt;/b&gt;이 사용된다. &lt;b&gt;오일러 공식&lt;/b&gt;은 '&lt;b&gt;원점을 중심으로 반지름이 1인 단위원'&lt;/b&gt;이라고 하였다. 위 설명들에 사용되었던 무게 중심을 잡기 위해 사용된 원을 오일러&amp;nbsp;공식을 사용하여 표현하면 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ e^{-2\pi i ft} $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고로 \( e^{i2\pi} = e^{i4\pi} = e^{i6\pi} = 1 \)과 같이 각도가 \( 2\pi (360^\circ) \)의 배수이면 항상 1이 된다. 여기서 말하는 1은 복소평면 상의 점 (1,0)에 해당하는 &lt;b&gt;좌표&lt;/b&gt;를 의미한다(\( 1 = 1 + 0i \)).&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;직교 좌표계에서 x축에 대한 각을 측정할 때 반시계 방향에 +를 이용하고 시계 방향으로 회전할 땐 -를 사용한다. 이 감는 방향을 시계 방향으로 설정하였기에 오일러 공식에서의 지수에 -를 사용하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1142&quot; data-origin-height=&quot;582&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bVzXK0/btsNc4HcZH2/NFXZtoVFa2Ajg7zg5EnkN1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bVzXK0/btsNc4HcZH2/NFXZtoVFa2Ajg7zg5EnkN1/img.png&quot; data-alt=&quot;Fig 10. Fourier Transform lecture 6&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bVzXK0/btsNc4HcZH2/NFXZtoVFa2Ajg7zg5EnkN1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbVzXK0%2FbtsNc4HcZH2%2FNFXZtoVFa2Ajg7zg5EnkN1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;630&quot; height=&quot;321&quot; data-origin-width=&quot;1142&quot; data-origin-height=&quot;582&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Fig 10. Fourier Transform lecture 6&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 \( g(t) \)는 주파수 신호가 되고 이를 위에서 말한 오일러 방정식에 곱하게 되면 원을 감으며 \( g(t) \)의 크기에 대응하는 시점에 같은 크기를 가지는 그래프가 그려질 것이다. 이를 이용해 감긴 그래프의 무게 중심 위치를 추적만 할 수 있다면 어떤 주파수가 섞여있는지 찾을 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1137&quot; data-origin-height=&quot;578&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cURTM6/btsNc4AxrlJ/S9W8vRT7eEVgDeuzHl6ZLk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cURTM6/btsNc4AxrlJ/S9W8vRT7eEVgDeuzHl6ZLk/img.png&quot; data-alt=&quot;Fig 11. Fourier Transform lecture 7&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cURTM6/btsNc4AxrlJ/S9W8vRT7eEVgDeuzHl6ZLk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcURTM6%2FbtsNc4AxrlJ%2FS9W8vRT7eEVgDeuzHl6ZLk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;622&quot; height=&quot;316&quot; data-origin-width=&quot;1137&quot; data-origin-height=&quot;578&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Fig 11. Fourier Transform lecture 7&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 위해 수많은 시간에 대응하는 값을 고르고, 감긴 그래프에서 어느 점인지 확인한 뒤 점의 개수로 나누면 된다. 이 근사는 점의 개수가 늘어날수록 정확해지므로 \( [t_2, t_1] \)의 구간에서 적분하여 모두 더한 뒤 해당 구간의 길이로 나누게 되면 위와 같은 수식이 된다. 이는 감긴 그래프의 무게 중심을 가리키게 된다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1145&quot; data-origin-height=&quot;578&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/L6QDr/btsNdUDY9aJ/gpnFIZIXlZvcDCafXpnk4K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/L6QDr/btsNdUDY9aJ/gpnFIZIXlZvcDCafXpnk4K/img.png&quot; data-alt=&quot;Fig 12. Fourier Transform lecture 8&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/L6QDr/btsNdUDY9aJ/gpnFIZIXlZvcDCafXpnk4K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FL6QDr%2FbtsNdUDY9aJ%2FgpnFIZIXlZvcDCafXpnk4K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;602&quot; height=&quot;304&quot; data-origin-width=&quot;1145&quot; data-origin-height=&quot;578&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Fig 12. Fourier Transform lecture 8&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 무게 중심을 가리키는 수식에서 해당 구간의 길이로 나누던 부분을 제거하면, 물리적으로 어떤 진동수가 오래 지속될수록(많이 감길수록) 그 Spike 되는 진동수에서 푸리에 변환 값이 커지게 만들 수 있다. 이는 해당 주파수 성분이 신호 안에 뚜렷하게 존재한다는 것을 의미한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 시간을 변수로 하는 \( g(t) \)에서 주파수를 변수로 하는 함수인 \( \hat{g}(f) \)로 바꾸면 &lt;b&gt;퓨리에 변환(Fourier&amp;nbsp;Transform)&lt;/b&gt;이 정의된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ \hat{g}(f) = \int_{-t_1}^{t_2} g(t) e^{-j 2\pi f t} \, dt $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 푸리에 변환은 유한한 시간 구간에 대해 적분하고 있다. 하지만 푸리에 변환은 이 구간을 음의 무한대에서 양의 무한대로 두기도 한다. 이를 &lt;b&gt;Continuous Fourier Transform(CFT),&lt;/b&gt; &lt;b&gt;Continuous-Time Fourier Transform(CTFT)&lt;/b&gt;라고 하며, 다음과 같은 수식으로 정의된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ X(f) = \int_{-\infty}^{\infty} x(t) e^{-j 2\pi f t} \, dt $$&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-pm-slice=&quot;4 3 []&quot; data-spread=&quot;false&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;\( x(t) \): 시간에 따라 변하는 연속 신호. 위 설명에서 \( g(t) \)에 해당&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;\( X(f) \): 주파수에&lt;/span&gt;&lt;span&gt; 대한 신호의 주파수 스펙트럼. 위 설명에서 \( \hat{g}(f) \)에 해당&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;\( j \): 허수 단위&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;역 푸리에 변환(Inverse Fourier Transform)&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로 &lt;b&gt;역&amp;nbsp;푸리에&amp;nbsp;변환(Inverse Fourier Transform)&lt;/b&gt;에 대해 간단히 설명하려 한다. 역 푸리에 변환은 주파수 영역에 표현된 신호를 다시 시간 영역 신호로 복원하기 위해 수행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ \text{Inverse Fourier Transform} = x(t) = \frac{1}{2\pi} \int_{-\infty}^{\infty} X(\omega) e^{j\omega t} \, d\omega $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ \text{Fourier&amp;nbsp;Transform} = X(\omega) = \int_{-\infty}^{\infty} x(t) e^{-j\omega t} \, dt $$&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;\( X(&amp;omega;) \): 주파수 영역 스펙트럼&lt;/li&gt;
&lt;li&gt;\( x(t) \): 시간 영역 신호&lt;/li&gt;
&lt;li&gt;\( \frac{1}{2\pi} \): 정규화 상수&lt;/li&gt;
&lt;li&gt;\( e^{-j&amp;omega;t} \): 복소 지수 함수 (역방향)&lt;/li&gt;
&lt;li&gt;\( dt \): 시간 변수에 대한 적분&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;푸리에 변환 결과는 주기적(회전 성분)을 가지고 있으므로 역 푸리에 변환 시 \( 2\pi \)로 나누어 스케일을 맞추어야 한다. 연속 푸리에 변환은 적분 구간이 무한대이기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;관련 포스팅&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://aliencoder.tistory.com/217&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;2025.04.08 - [Data Science/SR &amp;amp; VC] - 푸리에 급수(Fourier Series)와 푸리에 계수(Fourier Coefficient)&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;참고 자료&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Fourier_transform&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://en.wikipedia.org/wiki/Fourier_transform&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://darkpgmr.tistory.com/171&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://darkpgmr.tistory.com/171&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://gaussian37.github.io/math-calculus-euler_formula/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://gaussian37.github.io/math-calculus-euler_formula/&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=Mc9PHZ3H36M&amp;amp;t=408s&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.youtube.com/watch?v=Mc9PHZ3H36M&amp;amp;t=408s&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>AI/SR &amp;amp; VC</category>
      <category>CFT</category>
      <category>continuous fourier transform</category>
      <category>continuous-time fourier transform</category>
      <category>CTFT</category>
      <category>fourier transform</category>
      <category>Inverse Fourier Transform</category>
      <category>역 푸리에 변환</category>
      <category>푸리에 변환</category>
      <author>AlienCoder</author>
      <guid isPermaLink="true">https://aliencoder.tistory.com/218</guid>
      <comments>https://aliencoder.tistory.com/218#entry218comment</comments>
      <pubDate>Tue, 8 Apr 2025 16:27:41 +0900</pubDate>
    </item>
    <item>
      <title>푸리에 급수(Fourier Series)와 푸리에 계수(Fourier Coefficient)</title>
      <link>https://aliencoder.tistory.com/217</link>
      <description>&lt;script src=&quot;https://polyfill.io/v3/polyfill.min.js?features=es6&quot;&gt;&lt;/script&gt;
&lt;script src=&quot;https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js&quot;&gt;&lt;/script&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;신호처리를 처음 접했던 이후 언젠가 푸리에 변환에 대한 제대로 된 이해와 정리가 필요하다 느꼈다. 푸리에 변환에 대해 공부하기 전 사전 지식으로 &lt;b&gt;푸리에 급수(Fourier Series)&lt;/b&gt;와 &lt;b&gt;푸리에 계수(Fourier Coefficient)&lt;/b&gt;에 대해 먼저 정리하려 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;푸리에 급수와 푸리에 계수에 대한 설명 이전에 이해를 돕기 위해 &lt;b&gt;Sinusoids(정현파)&lt;/b&gt;와 &lt;b&gt;Periodic Waveforms(주기&amp;nbsp;신호)&lt;/b&gt;에 대해서 간단히 설명하려 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 &lt;b&gt;Sinusoids&lt;/b&gt;이다. 이는 시간에 따라 변하는 신호를 설명하는 기본적인 수학적 표현이다. 즉, 코사인파(cosine wave) 또는 사인파(sine wave)를 수식으로 나타낸 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ x(t) = A \cos(2\pi f t + \phi) = A \cos(\omega t + \phi) $$&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;  : amplitude&lt;/li&gt;
&lt;li&gt;  : cycle frequency or just frequency (Hz)&lt;/li&gt;
&lt;li&gt;  : phase-shift&lt;/li&gt;
&lt;li&gt;1/  : period (length of one cycle in time,  )&lt;/li&gt;
&lt;li&gt;  : radian frequency (rad/sec)&lt;/li&gt;
&lt;li&gt; /2  : cycle frequency or just frequency (=  )&lt;/li&gt;
&lt;li&gt;2 /  : period (length of one cycle in time,  )&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Periodic Waveforms&lt;/b&gt;는 sinusoids의 합으로써 합성할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ x(t) = A_0 + \sum_{k=1}^{\infty} A_k \cos \left( 2\pi k f_0 t + \phi_k \right) $$&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;\( f_0 \): fundamental frequency&lt;/li&gt;
&lt;li&gt;\( kf_0 \) : harmonic of \( f_0 \)(harmonic frequency)&lt;/li&gt;
&lt;li&gt;\( 1/f_0 \) : fundamental period (\( T_0 \))&lt;/li&gt;
&lt;li&gt;  : phase-shift&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 Fundamental Frequency는 현재 합성된 periodic waveforms에서 가장 낮은 주파수이다. harmonic은 우리말로 고조파라 부르는데 이는 \( f_0 \)의 정수배(k)로 표현된다. \( f_0 \)는 신호 전체가 정확히 반복되는 최소 단위로 사용되기 때문에 Fundamental이 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고로 \( A_0 \)는 DC값 또는 평균 진폭을 표현하는데 만약 0이 아니라면 좌표평면상 어딘가로 치우쳐있다는 말이 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;679&quot; data-origin-height=&quot;332&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/chliNz/btsNbbgH9d9/TLYD5dJ8uHrZ4IJEu1LB8k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/chliNz/btsNbbgH9d9/TLYD5dJ8uHrZ4IJEu1LB8k/img.png&quot; data-alt=&quot;Fig 1. Periodic Waveforms&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/chliNz/btsNbbgH9d9/TLYD5dJ8uHrZ4IJEu1LB8k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FchliNz%2FbtsNbbgH9d9%2FTLYD5dJ8uHrZ4IJEu1LB8k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;548&quot; height=&quot;268&quot; data-origin-width=&quot;679&quot; data-origin-height=&quot;332&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Fig 1. Periodic Waveforms&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 \( f_0 \)와 고조파의 조합으로 복잡한 주기파형을 만들어낼 수 있고 이는 &lt;b&gt;푸리에 급수(Fourier&amp;nbsp;Series)&lt;/b&gt;로 표현할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;푸리에 급수(Fourier Series)&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;푸리에&amp;nbsp;급수(Fourier&amp;nbsp;Series)&lt;/b&gt;는&amp;nbsp;sin파와&amp;nbsp;cos파를&amp;nbsp;이용하여&amp;nbsp;모든&amp;nbsp;주기&amp;nbsp;함수에&amp;nbsp;대해&amp;nbsp;표현할&amp;nbsp;수&amp;nbsp;있을까라는&amp;nbsp;물음에서&amp;nbsp;시작되었다.&amp;nbsp;즉,&amp;nbsp;세상에&amp;nbsp;존재하는&amp;nbsp;주기&amp;nbsp;함수를&amp;nbsp;삼각함수의&amp;nbsp;합으로&amp;nbsp;표현하는&amp;nbsp;방법이다.&amp;nbsp;이&amp;nbsp;푸리에&amp;nbsp;급수는&amp;nbsp;신호&amp;nbsp;처리,&amp;nbsp;진동&amp;nbsp;해석,&amp;nbsp;이미지&amp;nbsp;압축&amp;nbsp;등&amp;nbsp;다양한&amp;nbsp;분야에서&amp;nbsp;필수적인&amp;nbsp;역할을&amp;nbsp;한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 주기파형 수식을 그대로 가지고 온다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ x(t) = A_0 + \sum_{k=1}^{\infty} A_k \cos \left( k w_0 t + \phi_k \right) $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;코사인 덧셈 공식&lt;/b&gt;을 이용하여 아래와 같이 변환할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ x(t) = A_0 + \sum_{k=1}^{\infty} (A_k \cos \phi_k \cos(k \omega_0 t) - A_k \sin \phi_k \sin(k \omega_0 t)) $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;\( A_0 \)는 \( a_0 \)로 치환하고, \( A_k \cos \phi_k \)는 \( a_k \), \( A_k \sin \phi_k \)는 \( -b_k \)로 치환하면 다음과 같이 표기할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ x(t) = a_0 + \sum_{k=1}^{\infty} (a_k \cos(k \omega_0 t) + b_k \sin(k \omega_0 t)) $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 어떤 함수 \( f(x) \)가 주기 \( 2\pi \)를 가진다면, 다음과 같이 표기할 수 있다. \( w_0 = \frac{2\pi}{T} = \frac{2\pi}{2\pi} = 1 \)이고, 따라서 \( kw_0t \)가 그냥 \( kx \)가 된다. 좀 더 직관적으로 보기 위해 관례적으로 \( k \)는 \( n \)으로 바꾸어 표현하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ f(x) = a_0 + \sum_{n=1}^{\infty} \left( a_n \cos(nx) + b_n \sin(nx) \right) $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 \( a_0, a_n, b_n \)는 다음에 설명할 푸리에 계수이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;푸리에&amp;nbsp;계수(Fourier&amp;nbsp;Coefficient)&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;푸리에 계수(Fourier Coefficient)&lt;/b&gt;는 각각 cos과 sin이 주어진 신호에서 각각 얼마나 기여하는지 수치화한 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ a_0 = \frac{1}{2\pi} \int_{-\pi}^{\pi} f(x) \, dx $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ a_n = \frac{1}{\pi} \int_{-\pi}^{\pi} f(x) \cos(nx) \, dx $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ b_n = \frac{1}{\pi} \int_{-\pi}^{\pi} f(x) \sin(nx) \, dx $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;\( a_n \)은 코사인 계수에 해당하고, \( b_n \)은 사인 계수에 해당한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;푸리에 계수에 대한 수식 증명은 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 푸리에 급수 양변에&amp;nbsp;&amp;nbsp;\( \cos(mx) \)를 곱하고 \( [-\pi, \pi] \) 구간에서 적분한다.&lt;br /&gt;$$ \int_{-\pi}^{\pi} f(x) \cos(mx) \, dx = \int_{-\pi}^{\pi} \left( a_0 + \sum_{n=1}^{\infty} (a_n \cos(nx) + b_n \sin(nx)) \right) \cos(mx) \, dx $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 후 정규 직교성(orthonormal)을 이용하여 다음이 성립한다.&lt;br /&gt;$$&amp;nbsp;\int_{-\pi}^{\pi}&amp;nbsp;\cos(nx)&amp;nbsp;\cos(mx)&amp;nbsp;\,&amp;nbsp;dx&amp;nbsp;=&amp;nbsp;\begin{cases} &lt;br /&gt;\pi&amp;nbsp;&amp;amp;&amp;nbsp;\text{if&amp;nbsp;}&amp;nbsp;n&amp;nbsp;=&amp;nbsp;m&amp;nbsp;\\ &lt;br /&gt;0&amp;nbsp;&amp;amp;&amp;nbsp;\text{if&amp;nbsp;}&amp;nbsp;n&amp;nbsp;\neq&amp;nbsp;m &lt;br /&gt;\end{cases} $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ \int_{-\pi}^{\pi} \sin(nx) \cos(mx) \, dx = 0 $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 \( a_m \)만 남게 되어 다음과 같고,&lt;br /&gt;$$ \pi a_m = \int_{-\pi}^{\pi} f(x) \cos(mx) \, dx $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과적 다음이 성립하게 된다.&lt;br /&gt;$$&amp;nbsp;a_m&amp;nbsp;=&amp;nbsp;\frac{1}{\pi}&amp;nbsp;\int_{-\pi}^{\pi}&amp;nbsp;f(x)&amp;nbsp;\cos(mx)&amp;nbsp;\,&amp;nbsp;dx&amp;nbsp;$$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동일한 방법으로 \( b_n \)에 대해서도 증명할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;푸리에&amp;nbsp;계수(Fourier&amp;nbsp;Coefficient)&amp;nbsp;예제&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제로 사각파(Rectangular Wave)의 푸리에 급수 전개를 풀어보자. 여기서 사각파(Rectangular Wave)는 다음과 같은 주기 함수이다. 그림은 Fig 1을 참조하면 된다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;주기: \( 2\pi \)&lt;/li&gt;
&lt;li&gt;구간별 정의: \( f(x)&amp;nbsp;=&amp;nbsp;\begin{cases} &lt;br /&gt;1,&amp;nbsp;&amp;amp;&amp;nbsp;0&amp;nbsp;&amp;lt;&amp;nbsp;x&amp;nbsp;&amp;lt;&amp;nbsp;\pi&amp;nbsp;\\ &lt;br /&gt;-1,&amp;nbsp;&amp;amp;&amp;nbsp;-\pi&amp;nbsp;&amp;lt;&amp;nbsp;x&amp;nbsp;&amp;lt;&amp;nbsp;0 &lt;br /&gt;\end{cases} \)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 홀함수이므로, 코사인 항(\( a_n \))은 모두 0이다. 사인 항(\( b_n \))만 존재한다. 푸리에 계수 \( b_n \)은 다음과 같이 계산한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ b_n = \frac{1}{\pi} \int_{-\pi}^{\pi} f(x) \sin(nx) \, dx $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수 \( f(x) \)의 정의를 이용하여 적분을 두 구간으로 나누면 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ b_n = \frac{1}{\pi} \left( \int_{0}^{\pi} (1) \sin(nx) \, dx + \int_{-\pi}^{0} (-1) \sin(nx) \, dx \right) $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ b_n = \frac{2}{\pi} \int_{0}^{\pi} \sin(nx) \, dx $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 적분하면 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ b_n = \frac{2}{\pi} \left( \frac{1 - (-1)^n}{n} \right) $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 \( n \)이 홀수일 때 \( b_n = \frac{4}{n\pi} \)이고, \( n \)이 짝수면 \( b_n = 0 \)이다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결론적으로,&amp;nbsp;사각파의&amp;nbsp;푸리에&amp;nbsp;급수&amp;nbsp;전개는&amp;nbsp;다음과&amp;nbsp;같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ f(x) = \frac{4}{\pi} \left( \sin(x) + \frac{1}{3} \sin(3x) + \frac{1}{5} \sin(5x) + \frac{1}{7} \sin(7x) + \cdots \right) $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요약하면, 사각파는 홀수 고조파 성분만 가지며, 각 고조파의 진폭은 주파수에 반비례한다. 다만 고조파를 무한히 추가하지 않으면 완벽하게 직각 모양은 나오지 않고 &quot;깁스&amp;nbsp;현상(Gibbs&amp;nbsp;phenomenon)&quot;이라는&amp;nbsp;오버슈트가&amp;nbsp;생긴다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>AI/SR &amp;amp; VC</category>
      <category>fourier coefficient</category>
      <category>Fourier Series</category>
      <author>AlienCoder</author>
      <guid isPermaLink="true">https://aliencoder.tistory.com/217</guid>
      <comments>https://aliencoder.tistory.com/217#entry217comment</comments>
      <pubDate>Tue, 8 Apr 2025 11:12:23 +0900</pubDate>
    </item>
    <item>
      <title>[논문 리뷰] Attention Is All You Need</title>
      <link>https://aliencoder.tistory.com/216</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;script src=&quot;https://polyfill.io/v3/polyfill.min.js?features=es6&quot; data-mce-fragment=&quot;1&quot;&gt;&lt;/script&gt;
&lt;script src=&quot;https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js&quot; data-mce-fragment=&quot;1&quot;&gt;&lt;/script&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;논문 링크: &lt;a href=&quot;https://arxiv.org/abs/1706.03762&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://arxiv.org/abs/1706.03762&lt;/a&gt;&lt;/b&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1743822684395&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Attention Is All You Need&quot; data-og-description=&quot;The dominant sequence transduction models are based on complex recurrent or convolutional neural networks in an encoder-decoder configuration. The best performing models also connect the encoder and decoder through an attention mechanism. We propose a new &quot; data-og-host=&quot;arxiv.org&quot; data-og-source-url=&quot;https://arxiv.org/abs/1706.03762&quot; data-og-url=&quot;https://arxiv.org/abs/1706.03762v7&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/xKf4f/hyYBaI70me/bgXTI5tCJmsMMjb8y0mJX1/img.png?width=1200&amp;amp;height=700&amp;amp;face=0_0_1200_700,https://scrap.kakaocdn.net/dn/bItfwg/hyYCh8PuH8/sj47UNmLYbohKFKewb7Ut1/img.png?width=1000&amp;amp;height=1000&amp;amp;face=0_0_1000_1000&quot;&gt;&lt;a href=&quot;https://arxiv.org/abs/1706.03762&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://arxiv.org/abs/1706.03762&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/xKf4f/hyYBaI70me/bgXTI5tCJmsMMjb8y0mJX1/img.png?width=1200&amp;amp;height=700&amp;amp;face=0_0_1200_700,https://scrap.kakaocdn.net/dn/bItfwg/hyYCh8PuH8/sj47UNmLYbohKFKewb7Ut1/img.png?width=1000&amp;amp;height=1000&amp;amp;face=0_0_1000_1000');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Attention Is All You Need&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;The dominant sequence transduction models are based on complex recurrent or convolutional neural networks in an encoder-decoder configuration. The best performing models also connect the encoder and decoder through an attention mechanism. We propose a new&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;arxiv.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-pm-slice=&quot;1 1 []&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span&gt;1. 서론&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span&gt;1.1 논문 선정 이유&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&quot;Attention Is All You Need&quot;는 Transformer 모델을 제안한 기념비적인 논문으로, 자연어 처리 분야에 혁신을 일으킨 연구이다. 기존의 순환 신경망(RNN) 기반 모델들과 달리, 순차적 처리를 제거하고 전체 시퀀스를 한 번에 처리할 수 있는 구조를 도입하여 학습 효율성과 병렬성을 획기적으로 향상시켰다. 이 논문은 BERT, GPT 등 이후 등장한 대규모 언어 모델들의 기반이 되었기에, NLP 연구자에게 필수적으로 이해해야 할 논문이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span&gt;2. 논문 요약&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span&gt;2.1 논문 제목 및 저자 정보&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;b&gt;제목&lt;/b&gt;&lt;/span&gt;&lt;span&gt;: Attention Is All You Need&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;b&gt;저자&lt;/b&gt;&lt;/span&gt;&lt;span&gt;: Ashish Vaswani, Noam Shazeer, Niki Parmar, Jakob Uszkoreit, Llion Jones, Aidan N. Gomez, Lukasz Kaiser, Illia Polosukhin&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;b&gt;출처&lt;/b&gt;&lt;/span&gt;&lt;span&gt;: NeurIPS 2017&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span&gt;2.2 연구 배경 및 목적&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;기존의 Seq2Seq 모델은 RNN 또는 LSTM 기반으로 구성되어 있어, 병렬화가 어렵고 긴 문장을 처리할 때 정보 손실이 발생하는 문제가 있었다. 이 논문은 이러한 제약을 해결하기 위해 전적으로 Attention 메커니즘 중 self-attention에 기반한 Transformer 모델을 제안하였다. 이는 병렬 처리에 최적화되어 학습 속도를 대폭 향상시키며, 문맥 정보의 장기 의존성 문제를 효과적으로 해결할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span&gt;3. 핵심 내용 분석&lt;/span&gt;&lt;/b&gt;&lt;b&gt;&lt;span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span&gt;3.1 방법론&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;Transformer는 Encoder와 Decoder로 구성된다. 각 부분은 self-attention과 feed-forward 신경망으로 구성되며, 전체 구조에서 recurrence가 전혀 없다.&lt;/span&gt;&lt;b&gt;&lt;span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;568&quot; data-origin-height=&quot;746&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bejKuo/btsNa4GYkA2/E8Y2B6k6J7agMk9gdQ9uxk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bejKuo/btsNa4GYkA2/E8Y2B6k6J7agMk9gdQ9uxk/img.png&quot; data-alt=&quot;Fig 1. Transformer Architecture&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bejKuo/btsNa4GYkA2/E8Y2B6k6J7agMk9gdQ9uxk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbejKuo%2FbtsNa4GYkA2%2FE8Y2B6k6J7agMk9gdQ9uxk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;440&quot; height=&quot;578&quot; data-origin-width=&quot;568&quot; data-origin-height=&quot;746&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Fig 1. Transformer Architecture&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-pm-slice=&quot;1 3 []&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span&gt;3.1.1 Encoder 및 Decoder 구조&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&lt;span&gt;(a) Encoder&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-spread=&quot;false&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;Encoder는 동일한 구조의 레이어 6개로 구성&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;각 레이어는 다음의 두 서브 레이어로 구성&lt;/span&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-spread=&quot;false&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span&gt;&lt;b&gt;Multi-head Self-Attention&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;b&gt;Position-wise Feed-Forward Network&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span&gt;각 서브 레이어에는 잔차 연결(residual connection)이 적용되며, 그 뒤에 Layer Normalization이 적용&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;\( LayerNorm(x + Sublayer(x)) \)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;모든 서브 레이어는 출력 차원을 동일하게 유지하며, 모델 차원은 &lt;/span&gt;&lt;span&gt;d_model = 512&lt;/span&gt;&lt;span&gt;로 고정&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&lt;span&gt;(b) Decoder&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-spread=&quot;false&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;Decoder도 Encoder와 마찬가지로 6개의 동일한 구조의 레이어로 구성&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Decoder의 각 레이어는 다음의 세 서브 레이어로 구성&lt;/span&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-spread=&quot;false&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span&gt;&lt;b&gt;Masked Multi-head Self-Attention&lt;/b&gt;&lt;/span&gt;&lt;span&gt; (미래 정보를 가리지 않도록 마스킹 적용)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;b&gt;Multi-head Attention (Encoder의 출력과 Attention 수행)&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;b&gt;Position-wise Feed-Forward Network&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span&gt;각 서브 레이어 역시 residual connection과 layer normalization이 적용&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;출력 예측 시 position i는 i보다 작은 위치의 정보에만 접근 가능하도록 마스킹 처리&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span&gt;3.1.2 Attention 메커니즘&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;942&quot; data-origin-height=&quot;525&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tWZui/btsM92jhZhn/6XvWJJKBj3dKyGSutFvDk1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tWZui/btsM92jhZhn/6XvWJJKBj3dKyGSutFvDk1/img.png&quot; data-alt=&quot;Fig 2. (left) Scaled Dot-Product Attention. (right) Multi-Head Attention consists of several attention layers running in parallel.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tWZui/btsM92jhZhn/6XvWJJKBj3dKyGSutFvDk1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtWZui%2FbtsM92jhZhn%2F6XvWJJKBj3dKyGSutFvDk1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;655&quot; height=&quot;365&quot; data-origin-width=&quot;942&quot; data-origin-height=&quot;525&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Fig 2. (left) Scaled Dot-Product Attention. (right) Multi-Head Attention consists of several attention layers running in parallel.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&lt;span&gt;(a) Scaled Dot-Product Attention&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;$$&amp;nbsp;\text{Attention}(Q,&amp;nbsp;K,&amp;nbsp;V)&amp;nbsp;=&amp;nbsp;\text{softmax}\left(\frac{QK^T}{\sqrt{d_k}}\right)V&amp;nbsp;$$&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-spread=&quot;false&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;입력: 쿼리(Q), 키(K), 값(V)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;각 쿼리와 모든 키의 내적을 계산하고, 이를 키 차원의 제곱근(\( \sqrt{d_k} \))으로 나눈 뒤 softmax를 적용하여 가중치를 산출&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;출력: 각 값에 가중치를 곱한 값의 합&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;&lt;b&gt;(b) Self-Attention Mechanism&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;Self-Attention은&amp;nbsp;입력&amp;nbsp;문장의&amp;nbsp;각&amp;nbsp;단어가&amp;nbsp;문장의&amp;nbsp;다른&amp;nbsp;단어들과&amp;nbsp;어떤&amp;nbsp;관계를&amp;nbsp;가지는지를&amp;nbsp;파악하는&amp;nbsp;방식이다.&amp;nbsp;입력&amp;nbsp;벡터는&amp;nbsp;Query,&amp;nbsp;Key,&amp;nbsp;Value로&amp;nbsp;변환되며,&amp;nbsp;이들의&amp;nbsp;내적을&amp;nbsp;통해&amp;nbsp;각&amp;nbsp;단어&amp;nbsp;간의&amp;nbsp;유사도를&amp;nbsp;계산하고,&amp;nbsp;이를&amp;nbsp;바탕으로&amp;nbsp;가중&amp;nbsp;평균을&amp;nbsp;수행한다. &lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&lt;span&gt;(c) Scaled Dot-Product Attention&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;이&amp;nbsp;방식은&amp;nbsp;입력&amp;nbsp;길이에&amp;nbsp;관계없이&amp;nbsp;병렬&amp;nbsp;처리가&amp;nbsp;가능하며,&amp;nbsp;긴&amp;nbsp;거리의&amp;nbsp;의존&amp;nbsp;관계도&amp;nbsp;효과적으로&amp;nbsp;학습할&amp;nbsp;수&amp;nbsp;있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&lt;span&gt;(d) Multi-Head Attention&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ \text{MultiHead}(Q, K, V) = \text{Concat}(\text{head}_1, ..., \text{head}_h)W^O $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ \text{where} \text{head}_i = \text{Attention}(QW^Q_i, KW^K_i, VW^V_i) $$&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-spread=&quot;true&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;Attention을 단일 Head로 수행하는 대신, 여러 Head로 병렬 수행함으로써 다양한 표현 공간에서 정보를 추출&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;각각의 Head는 쿼리, 키, 값을 서로 다른 선형 변환을 거쳐 Attention을 수행한 후, 그 결과를 Concatenate하여 다시 선형 변환함&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;이 논문에서는 h = 8개의 Head를 사용하며, 각 Head의 차원은 64로 설정되어 총 모델 차원 512와 일치&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-pm-slice=&quot;1 3 []&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span&gt;3.1.3 Position-wise Feed-Forward Networks&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-spread=&quot;false&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;각 Encoder 및 Decoder 레이어 내에는 어텐션 외에도 포지션별 Fully Connected Feed-Forward(완전 연결 피드포워드) 네트워크가 포함됨&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;입력의 각 위치에 대해 동일한 방식으로 두 개의 선형 변환과 그 사이에 ReLU 활성 함수를 적용&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;$$&amp;nbsp;\text{FFN}(x)&amp;nbsp;=&amp;nbsp;\max(0,&amp;nbsp;xW_1&amp;nbsp;+&amp;nbsp;b_1)W_2&amp;nbsp;+&amp;nbsp;b_2&amp;nbsp;$$&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;입력 및 출력 차원은 \( d_{model} = 512 \), 내부 차원은 \( d_{ff} = 2048 \)이다. 이 연산은 1x1 컨볼루션으로도 해석 가능하다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;사실 이 Feed-Forward 부분에서 \( d_{ff} \) 부분이 왜 2048인지에 대해 의문이 생겼었다. 우선 위 수식에 따르면 FF는 2번의 linear transform이 발생하는 것으로 볼 수 있다. 또한 max는 ReLU와 같은 비선형 활성화 함수가 적용되는 부분이다. 즉 \( Linear_2(ReLU(Linear_1(x))) \)이 되고 통상적으로 차원은 2배씩 증가한다고 가정한다면, 입력 및 출력 차원을 512라 가정하였을 때 \( d_{ff} = 2048 \)이 된다고 해석하면 될 것 같다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-pm-slice=&quot;1 3 []&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span&gt;3.1.4 Embeddings and Softmax&lt;/span&gt;&lt;/b&gt;&lt;b&gt;&lt;span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-spread=&quot;false&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;입력 및 출력 토큰은 \( &lt;/span&gt;&lt;span&gt;d_{model} \)&lt;/span&gt;&lt;span&gt;차원의 임베딩 벡터로 변환됨&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;디코더 출력은 선형 변환 및 소프트맥스를 거쳐 다음 토큰의 확률 분포로 변환됨&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;임베딩 레이어와 출력층의 선형 변환에 동일한 가중치 행렬을 공유함&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;임베딩 시에는 해당 가중치에 \( \sqrt{d_{model}} \)&lt;/span&gt;&lt;span&gt;을 곱하여 스케일 조정함&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1068&quot; data-origin-height=&quot;232&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cFk006/btsNa8WTGy3/vBb3E4TIKestvkUbRfeLZk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cFk006/btsNa8WTGy3/vBb3E4TIKestvkUbRfeLZk/img.png&quot; data-alt=&quot;Table 1: Maximum path lengths, per-layer complexity and minimum number of sequential operations for different layer types. n is the sequence length, d is the representation dimension, k is the kernel size of convolutions and r the size of the neighborhood in restricted self-attention.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cFk006/btsNa8WTGy3/vBb3E4TIKestvkUbRfeLZk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcFk006%2FbtsNa8WTGy3%2FvBb3E4TIKestvkUbRfeLZk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;704&quot; height=&quot;153&quot; data-origin-width=&quot;1068&quot; data-origin-height=&quot;232&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Table 1: Maximum path lengths, per-layer complexity and minimum number of sequential operations for different layer types. n is the sequence length, d is the representation dimension, k is the kernel size of convolutions and r the size of the neighborhood in restricted self-attention.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Table 1에서 확인할 수 있듯 Self-Attention의 시간복잡도는 \( O(n^2d) \)와 같다. 만약 입력 시퀀스의 크기가 \( n \)이고 각 토큰의 임베딩 차원이 \( d \)일 때, self-attention의 연산량은 \( n*n \) 크기의 attention matrix 계산과 이에 따른 weighted sum 계산이 포함되어 &amp;nbsp;\( O(n^2d) \)가 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 시퀀스 길이가 길어질수록 계산량이 급격히 증가함을 의미한다. 하지만 병렬처리가 가능하기에 이 부분은 일정 부분은 개선될 수 있다. 물론 GPU 의존적이기에 돈은 많아야한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span&gt;3.1.5 Positional Encoding&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;Transformer는 순차 구조(RNN 등)를 사용하지 않기 때문에, 단어의 순서를 인식할 수 없다. 따라서 이를 보완하기 위해 위치 정보를 인코딩한 벡터(Positional Encoding)를 입력 임베딩에 더하는 방식을 사용하였다. 해당 인코딩은 모델의 임베딩 차원(\ d_{model} \)과 동일한 차원을 가진다.&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;$$ PE(pos, 2i) = \sin\left(\frac{pos}{10000^{\frac{2i}{d_{model}}}}\right) $$&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;$$ PE(pos, 2i+1) = \cos\left(\frac{pos}{10000^{\frac{2i}{d_{model}}}}\right) $$&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot; data-spread=&quot;false&quot;&gt;
&lt;li&gt;&lt;span&gt;어텐션 메커니즘은 입력 순서를 고려하지 않기 때문에, 각 단어의 위치 정보를 인코딩하여 입력에 추가&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;사인 및 코사인 함수를 사용하여 고정된 주기적 위치 인코딩을 생성&lt;/li&gt;
&lt;li&gt;각 차원은 고유 주기를 가지며, 위치 간 상대적인 차이를 인코딩할 수 있음&lt;/li&gt;
&lt;li&gt;이는 모델이 순서를 명시적으로 학습하지 않아도 위치 정보를 인식할 수 있도록 설계됨&lt;/li&gt;
&lt;li&gt;학습된 positional embedding과 비교했을 때 성능 차이는 크지 않았으며, 일반화 능력(긴 시퀀스 처리)에 유리할 것으로 기대되어 사인/코사인 방식 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span&gt;3.1.6 전체 구조&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-spread=&quot;false&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;&lt;b&gt;Encoder&lt;/b&gt;&lt;/span&gt;&lt;span&gt;: 입력 문장에 대해 self-attention &amp;rarr; feed-forward &amp;rarr; normalization &amp;rarr; residual connection을 반복.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;b&gt;Decoder&lt;/b&gt;&lt;/span&gt;&lt;span&gt;: 이전 단어들을 기반으로 masked self-attention 수행 후, encoder의 출력과 cross-attention을 통해 번역을 생성.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span&gt;3.2 실험 결과&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;991&quot; data-origin-height=&quot;427&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HgW23/btsNaLHDYme/SSCo8hATIO28yMhy7eYjcK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HgW23/btsNaLHDYme/SSCo8hATIO28yMhy7eYjcK/img.png&quot; data-alt=&quot;Table 2: The Transformer achieves better BLEU scores than previous state-of-the-art models on the English-to-German and English-to-French newstest2014 tests at a fraction of the training cost.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HgW23/btsNaLHDYme/SSCo8hATIO28yMhy7eYjcK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHgW23%2FbtsNaLHDYme%2FSSCo8hATIO28yMhy7eYjcK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;691&quot; height=&quot;298&quot; data-origin-width=&quot;991&quot; data-origin-height=&quot;427&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Table 2: The Transformer achieves better BLEU scores than previous state-of-the-art models on the English-to-German and English-to-French newstest2014 tests at a fraction of the training cost.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;Transformer는 WMT 2014 영어-독어, 영어-프랑스어 번역 데이터셋에서 SOTA를 달성하였으며, 특히 학습 속도 측면에서 기존 모델 대비 훨씬 빠르게 수렴하였다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-spread=&quot;false&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;BLEU 점수: 기존의 RNN 기반 모델을 능가함&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;연산 효율: 병렬 처리 덕분에 훈련 시간 단축&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 Transformer 모델의 다양한 구성 요소를 바꿨을 때 성능이 어떻게 변화하는지를 실험한 결과를 설명하는 표이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1135&quot; data-origin-height=&quot;732&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cArvNX/btsM9DKZ9Vb/phXSIzcOQESIN7Kqs8i6Vk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cArvNX/btsM9DKZ9Vb/phXSIzcOQESIN7Kqs8i6Vk/img.png&quot; data-alt=&quot;Table 3: Variations on the Transformer architecture. Unlisted values are identical to those of the base model. All metrics are on the English-to-German translation development set, newstest2013. Listed perplexities are per-wordpiece, according to our byte-pair encoding, and should not be compared to per-word perplexities.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cArvNX/btsM9DKZ9Vb/phXSIzcOQESIN7Kqs8i6Vk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcArvNX%2FbtsM9DKZ9Vb%2FphXSIzcOQESIN7Kqs8i6Vk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;668&quot; height=&quot;431&quot; data-origin-width=&quot;1135&quot; data-origin-height=&quot;732&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Table 3: Variations on the Transformer architecture. Unlisted values are identical to those of the base model. All metrics are on the English-to-German translation development set, newstest2013. Listed perplexities are per-wordpiece, according to our byte-pair encoding, and should not be compared to per-word perplexities.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Attention Head 수 및 차원 변화: 헤드 수가 너무 적거나 많으면 BLEU 점수가 떨어짐. &lt;br /&gt;&amp;rarr; 적절한 헤드 수가 중요하다는 점을 시사한다.&lt;/li&gt;
&lt;li&gt;Attention key 차원 \( d_k \) 변화: \( d_k\)감소시키면 성능이 떨어짐&lt;br /&gt;&amp;rarr; &quot;유사도 계산이 단순한 dot-product만으로는 부족할 수 있다&quot;는 점을 시사한다.&lt;/li&gt;
&lt;li&gt;모델&amp;nbsp;크기&amp;nbsp;변화&amp;nbsp;(d_model,&amp;nbsp;dff):&amp;nbsp;d_model과&amp;nbsp;dff를&amp;nbsp;늘리거나&amp;nbsp;줄인&amp;nbsp;실험에선&amp;nbsp;모델이&amp;nbsp;클수록&amp;nbsp;BLEU&amp;nbsp;성능이&amp;nbsp;향상&amp;nbsp;(예:&amp;nbsp;d_model=1024,&amp;nbsp;dff=4096일&amp;nbsp;때&amp;nbsp;BLEU가&amp;nbsp;가장&amp;nbsp;높음).&lt;/li&gt;
&lt;li&gt;Dropout 확률 변화: Pdrop 값을 0.0 또는 0.2로 바꿔서 실험 &lt;br /&gt;&amp;rarr; Dropout이 과적합을 방지하는 데 유리함.&lt;/li&gt;
&lt;li&gt;Positional Encoding 방식 변경: 기존의 sinusoidal encoding을 learned positional embedding으로 대체. &lt;br /&gt;&amp;rarr; 성능 차이는 거의 없므로 두 방식 모두 유효&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span&gt;3.3 결론&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&quot;Attention Is All You Need&quot;는 기존 시퀀스 모델의 한계를 극복하고, 이후 언어 모델 연구의 패러다임을 바꾼 중요한 연구이다. self-attention이라는 간단하면서도 강력한 구조가 어떻게 복잡한 언어 구조를 효과적으로 학습할 수 있는지를 보여준다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span&gt;4. 장점과 한계&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span&gt;4.1 연구의 장점&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-spread=&quot;false&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;RNN/CNN 없이도 시퀀스 데이터를 효과적으로 처리함&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;병렬화 가능성이 높아 학습 및 추론 속도가 빠름&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;다양한 NLP 태스크에 확장 가능&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span&gt;4.2 연구의 한계와 개선 가능성&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-spread=&quot;false&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;입력 길이가 길어질수록 계산량이 증가함 (\( O(n^2) \) 시간 복잡도)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;구조가 단순하긴 하나 대용량 학습이 요구됨&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span&gt;5. 관련 연구 및 응용&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span&gt;5.1 관련 연구 비교&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-spread=&quot;false&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;&lt;b&gt;RNN/LSTM&lt;/b&gt;&lt;/span&gt;&lt;span&gt;: 순차적인 정보 전달 방식으로 긴 의존성 학습에 한계가 있었음&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;b&gt;BERT, GPT&lt;/b&gt;&lt;/span&gt;&lt;span&gt;: Transformer 기반으로 사전 학습과 전이 학습을 활용함&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span&gt;5.2 실질적 응용 가능성&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-spread=&quot;false&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;기계 번역, 문서 요약, 질의응답, 텍스트 생성 등 거의 모든 NLP 분야에 적용 가능&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;이후 GPT 시리즈, BERT, T5 등 핵심 구조로 발전함&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span&gt;결론&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&quot;Attention Is All You Need&quot;는 NLP의 패러다임 전환을 이끈 핵심 논문으로, 간결한 구조와 강력한 성능을 바탕으로 현대의 거의 모든 대형 언어 모델의 기반이 되었다. 이 논문은 단지 하나의 모델 제안에 그치지 않고, 딥러닝 전체 구조 설계에 큰 영향을 미친 역사적인 연구라고 할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Study/Literature Review</category>
      <category>attention</category>
      <category>mha</category>
      <category>mhsa</category>
      <category>multi head attention</category>
      <category>multi head self attention</category>
      <category>self attention</category>
      <category>TRANSFORMER</category>
      <author>AlienCoder</author>
      <guid isPermaLink="true">https://aliencoder.tistory.com/216</guid>
      <comments>https://aliencoder.tistory.com/216#entry216comment</comments>
      <pubDate>Sat, 5 Apr 2025 15:11:31 +0900</pubDate>
    </item>
    <item>
      <title>[논문 리뷰] DeepSeek-R1: Incentivizing Reasoning Capability in LLMs via Reinforcement Learning</title>
      <link>https://aliencoder.tistory.com/215</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;script src=&quot;https://polyfill.io/v3/polyfill.min.js?features=es6&quot; data-mce-fragment=&quot;1&quot;&gt;&lt;/script&gt;
&lt;script src=&quot;https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js&quot; data-mce-fragment=&quot;1&quot;&gt;&lt;/script&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;논문 링크: &lt;a href=&quot;https://arxiv.org/abs/2501.12948&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://arxiv.org/abs/2501.12948&lt;/a&gt;&lt;/b&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1743438304938&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;DeepSeek-R1: Incentivizing Reasoning Capability in LLMs via Reinforcement Learning&quot; data-og-description=&quot;We introduce our first-generation reasoning models, DeepSeek-R1-Zero and DeepSeek-R1. DeepSeek-R1-Zero, a model trained via large-scale reinforcement learning (RL) without supervised fine-tuning (SFT) as a preliminary step, demonstrates remarkable reasonin&quot; data-og-host=&quot;arxiv.org&quot; data-og-source-url=&quot;https://arxiv.org/abs/2501.12948&quot; data-og-url=&quot;https://arxiv.org/abs/2501.12948v1&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bh8wWl/hyYyJkCoKT/xR8F8KYlR91SuAHOt9Xw81/img.png?width=1200&amp;amp;height=700&amp;amp;face=0_0_1200_700,https://scrap.kakaocdn.net/dn/c20BxA/hyYxKLndME/4pmH5jtSQ1wsH3mfFgTk0K/img.png?width=1000&amp;amp;height=1000&amp;amp;face=0_0_1000_1000&quot;&gt;&lt;a href=&quot;https://arxiv.org/abs/2501.12948&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://arxiv.org/abs/2501.12948&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bh8wWl/hyYyJkCoKT/xR8F8KYlR91SuAHOt9Xw81/img.png?width=1200&amp;amp;height=700&amp;amp;face=0_0_1200_700,https://scrap.kakaocdn.net/dn/c20BxA/hyYxKLndME/4pmH5jtSQ1wsH3mfFgTk0K/img.png?width=1000&amp;amp;height=1000&amp;amp;face=0_0_1000_1000');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;DeepSeek-R1: Incentivizing Reasoning Capability in LLMs via Reinforcement Learning&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;We introduce our first-generation reasoning models, DeepSeek-R1-Zero and DeepSeek-R1. DeepSeek-R1-Zero, a model trained via large-scale reinforcement learning (RL) without supervised fine-tuning (SFT) as a preliminary step, demonstrates remarkable reasonin&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;arxiv.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;576&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BGHUZ/btsM5kSgAcK/ckhLt9ada54PV6muVQh9s1/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BGHUZ/btsM5kSgAcK/ckhLt9ada54PV6muVQh9s1/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BGHUZ/btsM5kSgAcK/ckhLt9ada54PV6muVQh9s1/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBGHUZ%2FbtsM5kSgAcK%2FckhLt9ada54PV6muVQh9s1%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;645&quot; height=&quot;363&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;576&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-pm-slice=&quot;1 1 []&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span&gt;1. 서론&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span&gt;1.1 논문 선정 이유&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;Deepseek R1은 최신 대규모 언어 모델로서, 효율적인 학습 방식과 확장성을 강조한 연구이다. 최근 AI 모델의 크기가 기하급수적으로 증가함에 따라, 연산 비용 절감과 모델 효율성 개선이 중요한 연구 분야로 떠오르고 있다. 본 논문은 이러한 문제를 해결하기 위해 새로운 아키텍처와 최적화 기법을 제안하며, 최신 LLM 연구 동향을 파악하는 데 중요한 의미를 가진다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span&gt;2. 논문 요약&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span&gt;2.1 논문 제목 및 저자 정보&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;b&gt;제목&lt;/b&gt;&lt;/span&gt;&lt;span&gt;: Deepseek R1: Efficient and Scalable Language Model&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;b&gt;저자&lt;/b&gt;&lt;/span&gt;&lt;span&gt;: DeepSeek-AI&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;b&gt;출처&lt;/b&gt;&lt;/span&gt;&lt;span&gt;: DeepSeek-AI, 22 Jan 2025&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span&gt;2.2 연구 배경 및 목적&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;대규모 언어 모델(LLM)은 자연어 처리에서 혁신적인 성과를 보이고 있지만, 높은 연산 비용과 메모리 사용량으로 인해 실제 응용에서 제약이 많다. Deepseek R1은 이러한 문제를 해결하기 위해 효율적인 학습 전략과 모델 구조를 설계하여 성능을 유지하면서도 연산 비용을 줄이는 것을 목표로 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span&gt;3. 핵심 내용 분석&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span&gt;3.1 방법론&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span&gt;(a) DeepSeek-R1-Zero: 강화학습 기반 모델(GPRO)&lt;/span&gt;&lt;/b&gt;&lt;span&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;DeepSeek-R1은 강화 학습(RL)을 활용하여 지도 학습 데이터 없이 LLM의 추론 능력을 발전시키는 방안을 탐구한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존&amp;nbsp;강화&amp;nbsp;학습&amp;nbsp;방식은&amp;nbsp;평가&amp;nbsp;모델로&amp;nbsp;Agent의&amp;nbsp;행동을&amp;nbsp;평가하여&amp;nbsp;피드백을&amp;nbsp;제공하는&amp;nbsp;critic&amp;nbsp;모델과,&amp;nbsp;이를&amp;nbsp;기반으로&amp;nbsp;최적의&amp;nbsp;행동&amp;nbsp;정책을&amp;nbsp;학습하는&amp;nbsp;policy&amp;nbsp;모델로&amp;nbsp;구성된다.&amp;nbsp;하지만&amp;nbsp;이&amp;nbsp;방식은&amp;nbsp;두&amp;nbsp;개의&amp;nbsp;모델을&amp;nbsp;동시에&amp;nbsp;학습해야&amp;nbsp;하므로&amp;nbsp;연산&amp;nbsp;비용이&amp;nbsp;크고,&amp;nbsp;학습이&amp;nbsp;불안정해질&amp;nbsp;수&amp;nbsp;있다. &lt;br /&gt;&lt;br /&gt;&lt;b&gt;Critic&amp;nbsp;모델&lt;/b&gt;은&amp;nbsp;가치&amp;nbsp;함수(value&amp;nbsp;function)를&amp;nbsp;학습하고,&amp;nbsp;&lt;b&gt;Policy&amp;nbsp;모델&lt;/b&gt;은&amp;nbsp;행동(policy)을&amp;nbsp;학습해야&amp;nbsp;한다.&amp;nbsp;따라서&amp;nbsp;두&amp;nbsp;개의&amp;nbsp;신경망을&amp;nbsp;동시에&amp;nbsp;최적화해야&amp;nbsp;하므로&amp;nbsp;메모리&amp;nbsp;사용량&amp;nbsp;증가&amp;nbsp;및&amp;nbsp;계산량&amp;nbsp;증가로&amp;nbsp;인해&amp;nbsp;학습&amp;nbsp;비용이&amp;nbsp;크다. &lt;br /&gt;&lt;br /&gt;또한 기존 방식엔 학습 불안정성 문제가 있다. Critic 모델이 잘못된 가치 함수를 학습하면, Policy 모델이 이를 잘못된 방향으로 최적화할 가능성이 있다. Critic 모델 자체가 추정치를 기반으로 학습되므로 추론 오차가 누적될 가능성이 있으며, 이로 인해 학습이 불안정해질 수 있다. 특히, Critic 모델이 충분히 수렴하지 않은 상태에서 Policy 모델이 업데이트되면 비효율적인 탐색(exploration) 및 수렴 속도 저하가 발생할 수 있다. &lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 이를 개선하기 위해 해당 논문에선 &lt;span style=&quot;letter-spacing: 0px;&quot;&gt;Group Relative Policy Optimization(GRPO) 기법을 사용하며, 이는 기존의 비판자(critic) 모델을 제거하고 그룹 점수 기반으로 기준을 설정하는 방식이다.&amp;nbsp;&lt;/span&gt;&lt;span&gt;이 방법은 강화 학습의 비용을 절감하면서도 성능을 유지할 수 있도록 설계되었다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;GPRO는 기존 정책 모델 \( \pi_{\theta_{old}} \)에서 여러 개의 출력을 샘플링한 후, 새로운 정책 모델 \( \pi_{\theta} \)를 최적화하는 방식이다. 이를 위해 정책 업데이트 시 아래와 같은 목적 함수를 최대화한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;$$ J_{\text{GRPO}}(\theta) = \mathbb{E} \left[ \frac{1}{G} \sum_{i=1}^{G} \min \left( \frac{\pi_{\theta}(o_i | q)}{\pi_{\theta_{\text{old}}}(o_i | q)} A_i, \text{clip} \left( \frac{\pi_{\theta}(o_i | q)}{\pi_{\theta_{\text{old}}}(o_i | q)}, 1 - \epsilon, 1 + \epsilon \right) A_i \right) \right] - \beta D_{\text{KL}} (\pi_{\theta} || \pi_{\text{ref}}) $$&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 \( A_i \)는 각&amp;nbsp;샘플의&amp;nbsp;이점(advantage)이며,&amp;nbsp;그룹&amp;nbsp;내&amp;nbsp;보상의&amp;nbsp;평균과&amp;nbsp;표준편차를&amp;nbsp;이용하여&amp;nbsp;정규화된다.&lt;span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;$$ A_i = \frac{r_i - \text{mean}(\{r_1, r_2, \dots, r_G\})}{\text{std}(\{r_1, r_2, \dots, r_G\})} $$&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;또한 KL 발산 항인 \( D_{KL}(\pi_{\theta} || \pi_{ref}) \)를 추가하여 새로운 정책이 기존 참조 정책과 과도하게 차이나지 않도록 규제한다. 이를 통해 모델의 학습 안정성을 높이고, 강화 학습의 수렴 속도를 개선할 수 있도록 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;761&quot; data-origin-height=&quot;131&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yZHOl/btsM5yVZqE9/Xn7EzckGUCnsdUhsSKqAS0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yZHOl/btsM5yVZqE9/Xn7EzckGUCnsdUhsSKqAS0/img.png&quot; data-alt=&quot;Table 1. Template for DeepSeek-R1-Zero. prompt will be replaced with the specific reasoning question during training.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yZHOl/btsM5yVZqE9/Xn7EzckGUCnsdUhsSKqAS0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FyZHOl%2FbtsM5yVZqE9%2FXn7EzckGUCnsdUhsSKqAS0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;633&quot; height=&quot;109&quot; data-origin-width=&quot;761&quot; data-origin-height=&quot;131&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Table 1. Template for DeepSeek-R1-Zero. prompt will be replaced with the specific reasoning question during training.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적으로&amp;nbsp;강화&amp;nbsp;학습에서&amp;nbsp;보상(reward)&amp;nbsp;은&amp;nbsp;모델이&amp;nbsp;최적화할&amp;nbsp;방향을&amp;nbsp;결정하는&amp;nbsp;핵심&amp;nbsp;요소이다.&amp;nbsp;DeepSeek-R1-Zero는&amp;nbsp;강화&amp;nbsp;학습&amp;nbsp;과정에서&amp;nbsp;신경망&amp;nbsp;기반&amp;nbsp;보상&amp;nbsp;모델&amp;nbsp;대신&amp;nbsp;규칙&amp;nbsp;기반&amp;nbsp;보상&amp;nbsp;시스템을&amp;nbsp;채택하여&amp;nbsp;안정성과&amp;nbsp;효율성을&amp;nbsp;확보한다. &lt;br /&gt;&lt;br /&gt;&lt;b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;정확도 보상(Accuracy Rewards)&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;모델이 객관적으로 정답을 평가할 수 있는 기준을 따르도록 강제한다.&lt;/li&gt;
&lt;li&gt;예를 들어, 수학 문제에서는 답을 정해진 형식(예: □ 기호 안에 답 기입)으로 출력하도록 하여, 프로그램이 자동으로 평가할 수 있도록 한다.&lt;/li&gt;
&lt;li&gt;LeetCode 문제에서는 컴파일러를 활용하여 모델의 답변이 테스트 케이스를 통과하는지를 검증한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;형식 보상(Format Rewards)&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;모델의&amp;nbsp;사고&amp;nbsp;과정을&amp;nbsp;&amp;lt;think&amp;gt;&amp;nbsp;및&amp;nbsp;&amp;lt;/think&amp;gt;&amp;nbsp;태그&amp;nbsp;사이에&amp;nbsp;명시하도록&amp;nbsp;유도하여,&amp;nbsp;논리적인&amp;nbsp;사고&amp;nbsp;단계를&amp;nbsp;구조화한다.&lt;/li&gt;
&lt;li&gt;이는 모델이 중간 사고 과정을 명확하게 표현하도록 하며, 사용자가 모델의 추론 과정을 쉽게 분석할 수 있도록 돕는다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;신경망 기반 보상 모델을 사용하지 않는 이유&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;보상 해킹(reward hacking) 문제: 모델이 보상을 극대화하기 위해 예상치 못한 방식으로 행동할 가능성이 있다. 예를 들어, 신경망 보상 모델이 특정한 문구를 선호하면, 모델이 의미 없이 그 문구를 반복하는 방식으로 보상을 높일 수도 있다.&lt;/li&gt;
&lt;li&gt;추가 학습 비용 문제: 신경망 보상 모델은 훈련 후에도 보상 모델 자체를 재훈련해야 할 수 있으며, 이는 학습 자원과 시간이 추가로 소요되는 단점이 있다.&lt;/li&gt;
&lt;li&gt;훈련 과정 복잡성 증가: 신경망 보상 모델을 사용할 경우, 정책 모델(policy model)과 함께 보상 모델까지 최적화해야 하므로 전체 학습 과정이 더 복잡해진다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서, DeepSeek-R1-Zero는 보다 단순하고 효율적인 규칙 기반 보상 시스템을 채택하여 강화 학습을 최적화한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span&gt;(b) Aha Moment&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;DeepSeek-R1-Zero의&amp;nbsp;훈련&amp;nbsp;과정에서&amp;nbsp;&quot;Aha&amp;nbsp;Moment&quot;라는&amp;nbsp;흥미로운&amp;nbsp;현상이&amp;nbsp;관찰되었다.&amp;nbsp;이는&amp;nbsp;모델이&amp;nbsp;자신의&amp;nbsp;초기&amp;nbsp;접근&amp;nbsp;방식을&amp;nbsp;재평가하고,&amp;nbsp;문제&amp;nbsp;해결을&amp;nbsp;위해&amp;nbsp;더&amp;nbsp;많은&amp;nbsp;사고&amp;nbsp;시간을&amp;nbsp;할당하는&amp;nbsp;단계에서&amp;nbsp;발생한다.&amp;nbsp;이&amp;nbsp;현상은&amp;nbsp;모델의&amp;nbsp;추론&amp;nbsp;능력이&amp;nbsp;향상되고&amp;nbsp;있음을&amp;nbsp;보여주며,&amp;nbsp;강화&amp;nbsp;학습이&amp;nbsp;예상치&amp;nbsp;못한&amp;nbsp;정교한&amp;nbsp;결과를&amp;nbsp;도출할&amp;nbsp;수&amp;nbsp;있음을&amp;nbsp;시사한다. &lt;br /&gt;&lt;br /&gt;연구자들에게도&amp;nbsp;이는&amp;nbsp;중요한&amp;nbsp;순간이었다.&amp;nbsp;강화&amp;nbsp;학습은&amp;nbsp;직접적인&amp;nbsp;문제&amp;nbsp;해결&amp;nbsp;방법을&amp;nbsp;가르치는&amp;nbsp;것이&amp;nbsp;아니라,&amp;nbsp;적절한&amp;nbsp;보상&amp;nbsp;신호를&amp;nbsp;제공함으로써&amp;nbsp;모델이&amp;nbsp;스스로&amp;nbsp;고급&amp;nbsp;전략을&amp;nbsp;개발하도록&amp;nbsp;유도한다.&amp;nbsp;이러한&amp;nbsp;&quot;Aha&amp;nbsp;Moment&quot;는&amp;nbsp;인공지능&amp;nbsp;시스템이&amp;nbsp;더&amp;nbsp;자율적이고&amp;nbsp;적응적인&amp;nbsp;모델로&amp;nbsp;발전할&amp;nbsp;가능성을&amp;nbsp;보여주는&amp;nbsp;중요한&amp;nbsp;사례로&amp;nbsp;평가된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;692&quot; data-origin-height=&quot;393&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bmmutr/btsM6ATKxt7/oh2Ziit0gNPqCMghgfGCnK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bmmutr/btsM6ATKxt7/oh2Ziit0gNPqCMghgfGCnK/img.png&quot; data-alt=&quot;Table 3. An interesting &amp;amp;ldquo;aha moment&amp;amp;rdquo; of an intermediate version of DeepSeek-R1-Zero. The model learns to rethink using an anthropomorphic tone. This is also an aha moment for us, allowing us to witness the power and beauty of reinforcement learning.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bmmutr/btsM6ATKxt7/oh2Ziit0gNPqCMghgfGCnK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbmmutr%2FbtsM6ATKxt7%2Foh2Ziit0gNPqCMghgfGCnK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;632&quot; height=&quot;359&quot; data-origin-width=&quot;692&quot; data-origin-height=&quot;393&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Table 3. An interesting &amp;ldquo;aha moment&amp;rdquo; of an intermediate version of DeepSeek-R1-Zero. The model learns to rethink using an anthropomorphic tone. This is also an aha moment for us, allowing us to witness the power and beauty of reinforcement learning.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span&gt;(c) DeepSeek-R1: Reinforcement Learning with Cold Start&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DeepSeek-R1-Zero의 성공을 바탕으로, 연구진은 두 가지 질문을 제기했다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;소량의 고품질 데이터(cold start data)를 활용하면 추론 성능을 더 향상시키거나 학습 수렴 속도를 가속화할 수 있는가?&lt;/li&gt;
&lt;li&gt;더 사용자 친화적인 모델을 만들려면 어떻게 해야 하는가? 즉, 명확하고 일관된 Chain of Thought(CoT)을 생성하면서도 일반적인 문제 해결 능력이 뛰어난 모델을 학습할 수 있는가에 대한 의문이다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;이를 해결하기 위해 DeepSeek-R1 훈련 파이프라인을 설계했으며, 그 첫 단계가 &lt;b&gt;Cold Start&lt;/b&gt; 전략이다. Cold Start란 초기 학습 단계에서 모델이 충분한 경험이나 데이터 없이 불안정한 상태에서 시작하는 문제를 의미한다. DeepSeek-R1에서는 이를 해결하기 위해 소량의 긴 고품질의 &lt;b&gt;Chain of Thought(CoT)&lt;/b&gt; 데이터를 수집하여 모델을 미세 조정(fine-tuning) 하였다. 이를 통해 훈련 안정성을 높이고 학습 속도를 가속화할 수 있었다. 즉,&amp;nbsp;&lt;b&gt;Cold Start 전략&lt;/b&gt;은 기존 DeepSeek-R1-Zero와 달리, 강화 학습 초기에 모델이 불안정해지는 문제를 방지하기 위함이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터 수집 방법은 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Few-shot prompting: 긴 CoT 예제를 제공하며 모델이 이를 따르게 유도&lt;/li&gt;
&lt;li&gt;직접 프롬프트를 활용하여 상세한 답변 생성. reflection(자기반성) 및 검증 포함&lt;/li&gt;
&lt;li&gt;DeepSeek-R1-Zero의 출력을 가독성 높은 형식으로 변환&lt;/li&gt;
&lt;li&gt;사람이 후처리(post-processing)를 통해 결과를 개선&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;이렇게 수집한 수천 개의 cold-start 데이터를 이용해 DeepSeek-V3-Base를 미세 조정하여 RL의 출발점으로 활용한다. Cold Start 데이터의 장점은 크게 두 가지이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;가독성(Readability) 개선&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;DeepSeek-R1-Zero는 출력 내용이 읽기 어려운 경우가 많다. (예: 여러 언어가 섞이거나, 마크다운 형식이 부족하여 가독성이 떨어짐)&lt;/li&gt;
&lt;li&gt;DeepSeek-R1에서는 가독성 높은 형식을 설계하여, 응답의 끝에 요약을 추가하고, 비효율적인 출력을 필터링한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1743646195666&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;|special_token|&amp;lt;reasoning_process&amp;gt;|special_token|&amp;lt;summary&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&amp;lt;reasoning_process&amp;gt;: 해당 질문에 대한 Chain of Thought(CoT)&lt;/li&gt;
&lt;li&gt;&amp;lt;summary&amp;gt;: 최종 추론 결과를 요약&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;성능(Potential) 향상&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Cold-start 데이터를 신중하게 설계하여 모델이 더 논리적이고 체계적인 사고 과정을 따를 수 있도록 유도한다.&lt;/li&gt;
&lt;li&gt;DeepSeek-R1-Zero&amp;nbsp;대비&amp;nbsp;성능이&amp;nbsp;개선됨을&amp;nbsp;관찰했으며,&amp;nbsp;점진적인(iterative)&amp;nbsp;학습이&amp;nbsp;추론&amp;nbsp;모델을&amp;nbsp;훈련하는&amp;nbsp;더&amp;nbsp;효과적인&amp;nbsp;방법이라고&amp;nbsp;판단했다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;(d) Distillation: Empower Small Models with Reasoning Capability&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Distillation(지식 증류)&lt;/b&gt;는 큰 모델의 학습 결과를 작은 모델에 전달하여, 더 효율적인 모델을 만드는 과정이다. DeepSeek-R1의&amp;nbsp;추론&amp;nbsp;능력을&amp;nbsp;Qwen&amp;nbsp;및&amp;nbsp;Llama&amp;nbsp;같은&amp;nbsp;소형&amp;nbsp;모델에서도&amp;nbsp;구현하기&amp;nbsp;위해&amp;nbsp;80만&amp;nbsp;개의&amp;nbsp;학습&amp;nbsp;데이터를&amp;nbsp;활용해&amp;nbsp;미세&amp;nbsp;조정(SFT)&amp;nbsp;을&amp;nbsp;수행했다. &lt;br /&gt;&lt;br /&gt;이 연구에서 강화 학습(RL)을 적용하지 않고도 SFT만으로 작은 모델의 추론 능력을 크게 향상시킬 수 있음을 확인했다. 즉, DeepSeek-R1이 생성한 고품질 학습 데이터만으로도 효과적인 추론 모델을 만들 수 있다는 점을 보여주었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span&gt;3.2 실험 결과&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;772&quot; data-origin-height=&quot;459&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/7FBdL/btsM5o8aYWB/URl1ehGCqAg39LiCnsqCS0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/7FBdL/btsM5o8aYWB/URl1ehGCqAg39LiCnsqCS0/img.png&quot; data-alt=&quot;Figure 1. Benchmark performance of DeepSeek-R1.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/7FBdL/btsM5o8aYWB/URl1ehGCqAg39LiCnsqCS0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F7FBdL%2FbtsM5o8aYWB%2FURl1ehGCqAg39LiCnsqCS0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;673&quot; height=&quot;400&quot; data-origin-width=&quot;772&quot; data-origin-height=&quot;459&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Figure 1. Benchmark performance of DeepSeek-R1.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;AIME 2024: AIME (American Invitational Mathematics Examination)는 미국 수학 경시대회의 2단계 시험이다.&lt;/li&gt;
&lt;li&gt;Codeforces: Codeforces는 프로그래밍 대회 플랫폼으로, 알고리즘 문제 풀이 성능을 평가하는 데 사용된다.&lt;/li&gt;
&lt;li&gt;GPQA Diamond: GPQA (General-Purpose Question Answering)는 다양한 분야의 질문에 대한 정답 생성 능력을 평가하는 벤치마크이다. Diamond 난이도는 가장 어려운 질문을 포함하며, Pass@1은 첫 번째 답변에서 정답을 맞힌 비율을 의미한다.&lt;/li&gt;
&lt;li&gt;MATH-500: MATH 벤치마크는 수학 문제 해결 능력을 평가하는 데이터셋이며, 500개 샘플을 포함한 고급 문제 세트이다. Pass@1은 첫 번째 시도로 정답을 맞힌 비율을 나타낸다.&lt;/li&gt;
&lt;li&gt;MMLU: MMLU (Massive Multitask Language Understanding)는 다양한 학문 분야에서 모델의 이해도를 측정하는 벤치마크이다. 역사, 과학, 법학 등 57개 분야의 문제를 포함하며, Pass@1은 첫 번째 시도로 정답을 맞힌 비율을 의미한다.&lt;/li&gt;
&lt;li&gt;SWE-bench Verified: SWE-bench (Software Engineering Benchmark)는 실제 소프트웨어 엔지니어링 문제 해결 능력을 평가하는 벤치마크이다. Verified는&amp;nbsp;코드&amp;nbsp;수정&amp;nbsp;또는&amp;nbsp;버그&amp;nbsp;수정이&amp;nbsp;실제로&amp;nbsp;성공했는지&amp;nbsp;검증된&amp;nbsp;비율을&amp;nbsp;나타낸다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;DeepSeek-R1은 다양한 벤치마크에서 우수한 성능을 보이며, 추론(reasoning), 지식(knowledge), 기타 일반적 능력에서 강점을 나타낸다. 추론 성능에서도 OpenAI-o1-1217보다 다소 우수한 성능을 보였으며, Knowledge 부문에서도 DeepSeek-R1은 강력한 성능을 보여주고 있다. 특히 교육 및 사실 검증 문제에서 우수한 결과를 보여준다. 뿐만 아니라 시험 문제뿐만 아니라 창의적 글쓰기, 문서 편집, 요약 등 다양한 실생활 작업에서도 강력한 성능을 보이고 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span&gt;3.3 결론&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;Deepseek R1은 효율적인 학습 및 추론을 가능하게 하는 새로운 접근 방식을 제시하며, 실제 응용에서 비용 절감과 성능 향상을 동시에 실현할 가능성을 보여준다. 그러나 여전히 특정 태스크에서 미세 조정이 필요하며, 대규모 데이터 학습에서의 최적화가 추가적으로 요구된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span&gt;4. 장점과 한계&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span&gt;4.1 연구의 장점&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-spread=&quot;false&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;&lt;b&gt;연산 비용 절감&lt;/b&gt;&lt;/span&gt;&lt;span&gt;: 고성능을 유지하면서도 효율적인 연산을 수행할 수 있음.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;b&gt;모듈형 구조&lt;/b&gt;&lt;/span&gt;&lt;span&gt;: 다양한 태스크에 맞게 모델을 쉽게 조정 가능함.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;b&gt;적응형 추론&lt;/b&gt;&lt;/span&gt;&lt;span&gt;: 실시간 응용에서의 속도를 개선함.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 연산 비용 절감 부문이 최근 주식시장에도 크게 작용한 요소였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span&gt;4.2 연구의 한계와 개선 가능성&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-spread=&quot;false&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;일반적인&amp;nbsp;능력&lt;/b&gt;:&amp;nbsp;DeepSeek-R1은&amp;nbsp;함수&amp;nbsp;호출,&amp;nbsp;멀티턴&amp;nbsp;대화,&amp;nbsp;복잡한&amp;nbsp;역할&amp;nbsp;수행,&amp;nbsp;JSON&amp;nbsp;출력&amp;nbsp;등의&amp;nbsp;task에서&amp;nbsp;DeepSeek-V3보다&amp;nbsp;성능이&amp;nbsp;떨어진다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;언어&amp;nbsp;혼합&amp;nbsp;문제&lt;/b&gt;:&amp;nbsp;DeepSeek-R1은&amp;nbsp;현재&amp;nbsp;중국어와&amp;nbsp;영어에&amp;nbsp;최적화되어&amp;nbsp;있어,&amp;nbsp;다른&amp;nbsp;언어의&amp;nbsp;질문을&amp;nbsp;처리할&amp;nbsp;때&amp;nbsp;언어&amp;nbsp;혼합&amp;nbsp;문제가&amp;nbsp;발생할&amp;nbsp;수&amp;nbsp;있다.&amp;nbsp;즉,&amp;nbsp;영어와&amp;nbsp;중국어가&amp;nbsp;아닌&amp;nbsp;언어로&amp;nbsp;질문해도&amp;nbsp;DeepSeek-R1이&amp;nbsp;영어로&amp;nbsp;추론&amp;nbsp;및&amp;nbsp;응답할&amp;nbsp;가능성이&amp;nbsp;있다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;프롬프트&amp;nbsp;엔지니어링&lt;/b&gt;: DeepSeek-R1은 프롬프트에 민감하게 반응하는 경향이 있다. 특히, few-shot 프롬프트를 사용할 경우 성능이 저하된다. 따라서, 문제를 직접 설명하고 출력 형식을 지정하는 zero-shot 설정을 활용하는 것이 최적의 결과를 얻는 방법이다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;소프트웨어 엔지니어링&lt;/b&gt;: 소프트웨어 엔지니어링 벤치마크에서 DeepSeek-V3 에 비해 큰 개선을 보여주지 못했으며, 향후 버전에서는 소프트웨어 엔지니어링 데이터에 대한 reject sampling을 구현하거나 RL 과정 중 비동기 평가를 통합하여 효율성을 개선 필요하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span&gt;5. 관련 연구 및 응용&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span&gt;5.1 관련 연구 비교&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-spread=&quot;false&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;&lt;b&gt;GPT-4&lt;/b&gt;&lt;/span&gt;&lt;span&gt;: 초거대 모델로 높은 성능을 보이지만, 연산 비용이 매우 높음.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;b&gt;Llama 2&lt;/b&gt;&lt;/span&gt;&lt;span&gt;: 오픈소스 기반 모델로 Deepseek R1과 유사한 목표를 가짐.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;b&gt;Mistral 7B&lt;/b&gt;&lt;/span&gt;&lt;span&gt;: 효율적인 LLM 구조를 갖추었으며, Deepseek R1과 비교 대상이 될 수 있음.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span&gt;5.2 실질적 응용 가능성&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-spread=&quot;false&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;&lt;b&gt;AI 챗봇 및 가상 비서&lt;/b&gt;&lt;/span&gt;&lt;span&gt;: 연산 비용을 줄이면서도 높은 성능을 제공하는 모델로서 활용 가능함.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;b&gt;기업용 자동화 시스템&lt;/b&gt;&lt;/span&gt;&lt;span&gt;: 효율적인 언어 이해 및 생성 기능을 통해 고객 서비스, 문서 요약 등에 활용 가능함.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;b&gt;실시간 번역 및 질의응답 시스템&lt;/b&gt;&lt;/span&gt;&lt;span&gt;: 적응형 추론을 통해 빠르고 정확한 응답을 제공할 수 있음.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span&gt;결론&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;Deepseek R1은 대규모 언어 모델의 효율성을 극대화하는 혁신적인 연구로, 연산 비용 절감과 성능 유지를 동시에 달성하는 접근 방식을 제안하였다. 향후 연구에서는 장기 문맥 유지 및 특정 태스크에 대한 최적화가 주요 과제가 될 것이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span&gt;Appendix(추가&amp;nbsp;참고&amp;nbsp;문헌&amp;nbsp;등)&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;a href=&quot;https://modulabs.co.kr/blog/deepseek-r1-introduction&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://modulabs.co.kr/blog/deepseek-r1-introduction&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;a href=&quot;https://community.heartcount.io/ko/deepseek-r1-review/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://community.heartcount.io/ko/deepseek-r1-review/&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://kimjy99.github.io/%EB%85%BC%EB%AC%B8%EB%A6%AC%EB%B7%B0/deepseek-r1/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://kimjy99.github.io/%EB%85%BC%EB%AC%B8%EB%A6%AC%EB%B7%B0/deepseek-r1/&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Study/Literature Review</category>
      <category>deepseek</category>
      <category>deepseek-r1</category>
      <author>AlienCoder</author>
      <guid isPermaLink="true">https://aliencoder.tistory.com/215</guid>
      <comments>https://aliencoder.tistory.com/215#entry215comment</comments>
      <pubDate>Thu, 3 Apr 2025 11:30:10 +0900</pubDate>
    </item>
    <item>
      <title>[논문 리뷰] AudioGPT: Understanding and Generating Speech, Music, Sound, and Talking Head</title>
      <link>https://aliencoder.tistory.com/214</link>
      <description>&lt;script src=&quot;https://polyfill.io/v3/polyfill.min.js?features=es6&quot;&gt;&lt;/script&gt;
&lt;script src=&quot;https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js&quot;&gt;&lt;/script&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;논문 링크: &lt;a href=&quot;https://arxiv.org/abs/2304.12995&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://arxiv.org/abs/2304.12995&lt;/a&gt;&lt;/b&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1743242746093&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;AudioGPT: Understanding and Generating Speech, Music, Sound, and Talking Head&quot; data-og-description=&quot;Large language models (LLMs) have exhibited remarkable capabilities across a variety of domains and tasks, challenging our understanding of learning and cognition. Despite the recent success, current LLMs are not capable of processing complex audio informa&quot; data-og-host=&quot;arxiv.org&quot; data-og-source-url=&quot;https://arxiv.org/abs/2304.12995&quot; data-og-url=&quot;https://arxiv.org/abs/2304.12995v1&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bdFM0T/hyYxKqq5uB/8pJe3ZiPTtvWZryRpdg7MK/img.png?width=1200&amp;amp;height=700&amp;amp;face=0_0_1200_700,https://scrap.kakaocdn.net/dn/saCpe/hyYxJdXsrM/nDNXkVlM9GvHmRidsHKPwk/img.png?width=1000&amp;amp;height=1000&amp;amp;face=0_0_1000_1000&quot;&gt;&lt;a href=&quot;https://arxiv.org/abs/2304.12995&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://arxiv.org/abs/2304.12995&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bdFM0T/hyYxKqq5uB/8pJe3ZiPTtvWZryRpdg7MK/img.png?width=1200&amp;amp;height=700&amp;amp;face=0_0_1200_700,https://scrap.kakaocdn.net/dn/saCpe/hyYxJdXsrM/nDNXkVlM9GvHmRidsHKPwk/img.png?width=1000&amp;amp;height=1000&amp;amp;face=0_0_1000_1000');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;AudioGPT: Understanding and Generating Speech, Music, Sound, and Talking Head&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Large language models (LLMs) have exhibited remarkable capabilities across a variety of domains and tasks, challenging our understanding of learning and cognition. Despite the recent success, current LLMs are not capable of processing complex audio informa&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;arxiv.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-pm-slice=&quot;1 1 []&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span&gt;1. 서론&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span&gt;1.1 논문 선정 이유&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;AudioGPT는 음성, 음악, 소리 및 가상 얼굴 생성(Talking Head)과 같은 다양한 오디오 관련 작업을 수행할 수 있는 다능력 모델이다. 최근 멀티모달 AI 모델이 급격히 발전하면서, 텍스트뿐만 아니라 음성과 영상까지 다룰 수 있는 AI의 중요성이 강조되고 있다. 본 논문은 오디오 생성 및 이해에서 최신 기술을 통합한 사례로서, 그 기여도와 한계를 분석하고자 선정하였다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span&gt;2. 논문 요약&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span&gt;2.1 논문 제목 및 저자 정보&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;b&gt;제목&lt;/b&gt;&lt;/span&gt;&lt;span&gt;: AudioGPT: Understanding and Generating Speech, Music, Sound, and Talking Head&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;b&gt;저자&lt;/b&gt;&lt;/span&gt;&lt;span&gt;: Rongjie Huang, Mingze Li, Dongchao Yang, Jiatong Shi, Xuankai Chang, Zhenhui Ye, Yuning Wu, Zhiqing Hong, Jiawei Huang, Jinglin Liu, Yi Ren, Zhou Zhao, Shinji Watanabe&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&lt;b&gt;출처&lt;/b&gt;&lt;/span&gt;&lt;span&gt;: Zhejiang University | Peking University | Carnegie Mellon University | Remin University of China, 25&amp;nbsp;Apr&amp;nbsp;2023&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span&gt;2.2 연구 배경 및 목적&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;음성 및 오디오 생성 기술은 다양한 응용 분야에서 활용되고 있으며, 최근 GPT 기반 모델들이 자연어 처리에서 보여준 성과를 오디오 분야로 확장하려는 시도가 활발하다. 본 연구는 오디오 기반 멀티모달 AI 시스템의 한계를 극복하고, 보다 정교한 오디오 생성 및 이해 모델을 개발하는 것을 목표로 한다. 이를 통해, 보다 자연스러운 음성 합성, 음악 생성, 소리 인식 및 가상 얼굴 애니메이션을 가능하게 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span&gt;3. 핵심 내용 분석&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span&gt;3.1 방법론&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;893&quot; data-origin-height=&quot;378&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cb6sI9/btsM3Ukvpb7/l9UgtEIHYH1Zb7qDVv9Mr0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cb6sI9/btsM3Ukvpb7/l9UgtEIHYH1Zb7qDVv9Mr0/img.png&quot; data-alt=&quot;Fig. 1. AudioGPT Architecture&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cb6sI9/btsM3Ukvpb7/l9UgtEIHYH1Zb7qDVv9Mr0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcb6sI9%2FbtsM3Ukvpb7%2Fl9UgtEIHYH1Zb7qDVv9Mr0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;671&quot; height=&quot;284&quot; data-origin-width=&quot;893&quot; data-origin-height=&quot;378&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Fig. 1. AudioGPT Architecture&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;A high-level overview of AudioGPT. AudioGPT can be divided into four stages, including modality transformation, task analysis, model assignment, and response generation. It equips ChatGPT with audio foundation models to handle complex audio tasks and is connected with a modality transformation interface to enable spoken dialogue. We design principles to evaluate multi-modal&amp;nbsp;LLMs&amp;nbsp;in&amp;nbsp;terms&amp;nbsp;of&amp;nbsp;consistency,&amp;nbsp;capability,&amp;nbsp;and&amp;nbsp;robustness.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;AudioGPT는 GPT-4와 같은 대규모 언어 모델(LLM)의 능력을 활용하여, 오디오 데이터를 처리할 수 있도록 설계되었다. 복잡한 오디오 작업을 처리하기 위해 ChatGPT에 오디오 기반 모델을 갖추고 있으며, 음성 대화가 가능하도록 모달리티 변환 인터페이스와 연결되어 있다. Consistency,&amp;nbsp;Capability,&amp;nbsp;Robustness&amp;nbsp;측면에서&amp;nbsp;멀티모달&amp;nbsp;LLM을&amp;nbsp;평가하기&amp;nbsp;위한&amp;nbsp;원칙을&amp;nbsp;설계한다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Consistency&lt;/b&gt;:&amp;nbsp;LLM이&amp;nbsp;사용자의&amp;nbsp;의도를&amp;nbsp;제대로&amp;nbsp;이해하고&amp;nbsp;인간의&amp;nbsp;인지&amp;nbsp;및&amp;nbsp;문제&amp;nbsp;해결과&amp;nbsp;밀접하게&amp;nbsp;일치하는&amp;nbsp;오디오&amp;nbsp;foundation&amp;nbsp;model을&amp;nbsp;할당하는지&amp;nbsp;여부를&amp;nbsp;평가한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Capabilitity&lt;/b&gt;:&amp;nbsp;복잡한&amp;nbsp;오디오&amp;nbsp;task를&amp;nbsp;처리하고,&amp;nbsp;zero-shot&amp;nbsp;방식으로&amp;nbsp;음성,&amp;nbsp;음악,&amp;nbsp;소리,&amp;nbsp;talking&amp;nbsp;head를&amp;nbsp;이해하고&amp;nbsp;생성하는&amp;nbsp;오디오&amp;nbsp;foundation&amp;nbsp;model의&amp;nbsp;성능을&amp;nbsp;측정한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Robustness&lt;/b&gt;:&amp;nbsp;special&amp;nbsp;case들을&amp;nbsp;다루는&amp;nbsp;LLM의&amp;nbsp;능력을&amp;nbsp;측정한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-pm-slice=&quot;1 3 []&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span&gt;3.1.1 시스템 구조&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;AudioGPT는 다음과 같은 주요 모듈로 구성된다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;843&quot; data-origin-height=&quot;85&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Utft3/btsM3lJEVaB/0HlubjjnCh7JCnhaPYKk1K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Utft3/btsM3lJEVaB/0HlubjjnCh7JCnhaPYKk1K/img.png&quot; data-alt=&quot;Fig 2. AudioGPT&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Utft3/btsM3lJEVaB/0HlubjjnCh7JCnhaPYKk1K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FUtft3%2FbtsM3lJEVaB%2F0HlubjjnCh7JCnhaPYKk1K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;467&quot; height=&quot;47&quot; data-origin-width=&quot;843&quot; data-origin-height=&quot;85&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Fig 2. AudioGPT&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-spread=&quot;false&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;&lt;b&gt;T(Modality Transformer)&lt;/b&gt;&lt;/span&gt;&lt;span&gt;: 입력 데이터를 통합된 형식으로 변환.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;b&gt;L(Dialogue Engine, LLM)&lt;/b&gt;&lt;/span&gt;&lt;span&gt;: 사용자 질의 및 문맥을 이해하고 적절한 응답을 생성.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;b&gt;M(Prompt Manager)&lt;/b&gt;&lt;/span&gt;&lt;span&gt;: 적절한 프롬프트를 구성하여 모델을 제어.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;b&gt;H(Task Handler)&lt;/b&gt;&lt;/span&gt;&lt;span&gt;: 입력 데이터를 분석하고 적절한 태스크를 할당.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;b&gt;\( {P_i} \)(Audio Foundation Models)&lt;/b&gt;&lt;/span&gt;&lt;span&gt;: 다양한 오디오 태스크를 수행하는 모델 세트.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span&gt;3.1.2 처리 과정&lt;/span&gt;&lt;/b&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-spread=&quot;false&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span&gt;&lt;b&gt;모달리티 변환&lt;/b&gt;&lt;/span&gt;&lt;span&gt;: 입력 데이터의 형식을 일관된 모달리티로 변환.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;b&gt;태스크 분석&lt;/b&gt;&lt;/span&gt;&lt;span&gt;: 입력 데이터와 문맥을 기반으로 적절한 태스크를 결정.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;b&gt;모델 할당&lt;/b&gt;&lt;/span&gt;&lt;span&gt;: 태스크를 수행할 오디오 모델을 선택하고 실행.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;b&gt;응답 생성&lt;/b&gt;&lt;/span&gt;&lt;span&gt;: 오디오, 텍스트, 비디오 등의 최종 출력을 생성.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span&gt;3.1.3 실험&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;논문에선 GPT 모델의 gpt-3.5-turbo를 LLM으로 사용하고 LangChain으로 LLM을 안내한다. 오디오 foundation model의 배포에는 허깅페이스에 유연한 NVIDIA T4 GPU만 필요하다. Greedy search를 사용하여 출력을 생성하기 위해 0의 temperature를 사용하고 생성을 위한 최대 토큰 수를 2048로 설정한다.&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;아래는 AudioGPT 실행 결과이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;531&quot; data-origin-height=&quot;682&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/btu6OP/btsM4IRmuWB/NZRcmmb6Jhq94CknpQDcck/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/btu6OP/btsM4IRmuWB/NZRcmmb6Jhq94CknpQDcck/img.png&quot; data-alt=&quot;Figure 3: Qualitative analysis on multiple rounds of dialogue between humans and AudioGPT.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/btu6OP/btsM4IRmuWB/NZRcmmb6Jhq94CknpQDcck/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbtu6OP%2FbtsM4IRmuWB%2FNZRcmmb6Jhq94CknpQDcck%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;474&quot; height=&quot;609&quot; data-origin-width=&quot;531&quot; data-origin-height=&quot;682&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Figure 3: Qualitative analysis on multiple rounds of dialogue between humans and AudioGPT.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;position: absolute; text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;469&quot; data-origin-height=&quot;676&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/4Zwav/btsM100MLJM/QHdkAzqJCvL6FuH1V5B0I1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/4Zwav/btsM100MLJM/QHdkAzqJCvL6FuH1V5B0I1/img.png&quot; data-alt=&quot;Figure 4: Qualitative analysis on simple tasks.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/4Zwav/btsM100MLJM/QHdkAzqJCvL6FuH1V5B0I1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F4Zwav%2FbtsM100MLJM%2FQHdkAzqJCvL6FuH1V5B0I1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;393&quot; height=&quot;566&quot; data-origin-width=&quot;469&quot; data-origin-height=&quot;676&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Figure 4: Qualitative analysis on simple tasks.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;479&quot; data-origin-height=&quot;674&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/L7D6v/btsM3jE2FU5/oJztqcnjpsxOh21K9o9JXK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/L7D6v/btsM3jE2FU5/oJztqcnjpsxOh21K9o9JXK/img.png&quot; data-alt=&quot;Figure 5: Qualitative analysis on simple tasks.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/L7D6v/btsM3jE2FU5/oJztqcnjpsxOh21K9o9JXK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FL7D6v%2FbtsM3jE2FU5%2FoJztqcnjpsxOh21K9o9JXK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;436&quot; height=&quot;613&quot; data-origin-width=&quot;479&quot; data-origin-height=&quot;674&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Figure 5: Qualitative analysis on simple tasks.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span&gt;3.2 실험 결과&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;복잡한 오디오 정보를 처리하는 작업 수행자로서, 오디오 기반 모델은 복잡한 다운스트림 작업을 처리하는 데 중요한 영향을 미친다. 이 논문에선 음성, 음악, 소리 및 가상 얼굴(Talking Head)의 이해 및 생성과 관련된 평가 지표(evaluation metrics)와 다운스트림 데이터셋을 다음 Table 3에서 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;843&quot; data-origin-height=&quot;483&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bh0AuJ/btsM19iEDUJ/50Rz50YHN3BhozC2vFD2TK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bh0AuJ/btsM19iEDUJ/50Rz50YHN3BhozC2vFD2TK/img.png&quot; data-alt=&quot;Table 3. Evaluating audio foundation models in AudioGPT&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bh0AuJ/btsM19iEDUJ/50Rz50YHN3BhozC2vFD2TK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbh0AuJ%2FbtsM19iEDUJ%2F50Rz50YHN3BhozC2vFD2TK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;539&quot; height=&quot;309&quot; data-origin-width=&quot;843&quot; data-origin-height=&quot;483&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Table 3. Evaluating audio foundation models in AudioGPT&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;Table 3에서 확인할 수 있는 다양한 실험을 통해 AudioGPT의 성능을 다음과 같이 평가하였다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-spread=&quot;false&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;&lt;b&gt;음성 합성(Speech Synthesis)&lt;/b&gt;&lt;/span&gt;&lt;span&gt;: 기존 TTS 모델보다 자연스러운 발화를 생성함.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;b&gt;음악 생성(Music Generation)&lt;/b&gt;&lt;/span&gt;&lt;span&gt;: 다양한 장르와 스타일을 학습하여 새로운 음악을 창작할 수 있음.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;b&gt;환경 소리 분석(Sound Understanding)&lt;/b&gt;&lt;/span&gt;&lt;span&gt;: 특정 소리를 인식하고 이를 텍스트로 변환하는 능력이 향상됨.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;b&gt;가상 얼굴 애니메이션(Talking Head Animation)&lt;/b&gt;&lt;/span&gt;&lt;span&gt;: 음성 데이터를 기반으로 보다 자연스러운 얼굴 움직임을 생성함.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span&gt;3.3 결론&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주요 특징은 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;멀티모달&amp;nbsp;데이터&amp;nbsp;처리:&amp;nbsp;텍스트,&amp;nbsp;음성,&amp;nbsp;음악,&amp;nbsp;환경&amp;nbsp;소리&amp;nbsp;등을&amp;nbsp;통합적으로&amp;nbsp;이해하고&amp;nbsp;생성할&amp;nbsp;수&amp;nbsp;있음.&lt;/li&gt;
&lt;li&gt;프롬프트&amp;nbsp;기반&amp;nbsp;생성:&amp;nbsp;사용자가&amp;nbsp;입력한&amp;nbsp;텍스트&amp;nbsp;프롬프트에&amp;nbsp;따라&amp;nbsp;특정&amp;nbsp;유형의&amp;nbsp;오디오를&amp;nbsp;생성함.&lt;/li&gt;
&lt;li&gt;지속적&amp;nbsp;학습&amp;nbsp;및&amp;nbsp;조정:&amp;nbsp;사용자의&amp;nbsp;피드백을&amp;nbsp;반영하여&amp;nbsp;생성&amp;nbsp;품질을&amp;nbsp;지속적으로&amp;nbsp;개선함.&lt;/li&gt;
&lt;li&gt;다양한&amp;nbsp;애플리케이션&amp;nbsp;적용&amp;nbsp;가능성:&amp;nbsp;음성&amp;nbsp;비서,&amp;nbsp;음악&amp;nbsp;생성,&amp;nbsp;게임&amp;nbsp;및&amp;nbsp;영화&amp;nbsp;사운드&amp;nbsp;디자인,&amp;nbsp;가상&amp;nbsp;캐릭터&amp;nbsp;음성&amp;nbsp;합성&amp;nbsp;등.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;AudioGPT는 오디오 생성 및 이해의 새로운 가능성을 제시하며, 다양한 응용 분야에서 활용될 수 있는 잠재력을 보유하고 있다. 그러나 모델의 크기와 학습 데이터의 한계, 실시간 처리 성능 등의 문제를 해결해야 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span&gt;4. 장점과 한계&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span&gt;4.1 연구의 장점&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-spread=&quot;false&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;&lt;b&gt;멀티모달 AI의 확장성&lt;/b&gt;&lt;/span&gt;&lt;span&gt;: 텍스트뿐만 아니라 오디오까지 처리할 수 있는 AI 모델의 가능성을 확장함.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;b&gt;고품질 오디오 생성&lt;/b&gt;&lt;/span&gt;&lt;span&gt;: 다양한 오디오 유형을 높은 품질로 생성 가능.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;b&gt;응용 범위의 다양성&lt;/b&gt;&lt;/span&gt;&lt;span&gt;: 음성 비서, 음악 생성, 가상 인간 인터페이스 등 다양한 산업에서 활용 가능.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span&gt;4.2 연구의 한계와 개선 가능성&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-spread=&quot;false&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;&lt;b&gt;실시간 처리 성능 부족&lt;/b&gt;&lt;/span&gt;&lt;span&gt;: 높은 연산량으로 인해 실시간 응용이 어려울 수 있음.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;b&gt;일관성 문제&lt;/b&gt;&lt;/span&gt;&lt;span&gt;: 긴 문맥을 유지하는 오디오 생성 시 일관성이 떨어질 가능성이 있음.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;b&gt;데이터 편향성&lt;/b&gt;&lt;/span&gt;&lt;span&gt;: 학습 데이터의 편향으로 인해 특정 언어나 스타일에 최적화될 가능성이 있음.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span&gt;5. 관련 연구 및 응용&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span&gt;5.1 관련 연구 비교&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-spread=&quot;false&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;&lt;b&gt;VALL-E&lt;/b&gt;&lt;/span&gt;&lt;span&gt;: AudioGPT와 유사한 방식으로 음성을 생성하지만, 보다 압축된 모델 구조를 채택함.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;b&gt;Jukebox (OpenAI)&lt;/b&gt;&lt;/span&gt;&lt;span&gt;: 음악 생성에 특화된 모델로, AudioGPT보다 특정 도메인에 강점이 있음.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;b&gt;Tacotron &amp;amp; WaveNet&lt;/b&gt;&lt;/span&gt;&lt;span&gt;: 기존 음성 합성 모델과 비교했을 때, AudioGPT는 보다 유연한 프롬프트 기반 접근 방식을 제공함.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span&gt;5.2 실질적 응용 가능성&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-spread=&quot;false&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;&lt;b&gt;음성 비서(AI Assistants)&lt;/b&gt;&lt;/span&gt;&lt;span&gt;: 더 자연스럽고 문맥을 이해하는 AI 음성 비서 개발에 활용 가능.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;b&gt;음악 창작(Music Composition)&lt;/b&gt;&lt;/span&gt;&lt;span&gt;: AI 작곡가로서의 역할 수행 가능.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;b&gt;게임 및 미디어(Game &amp;amp; Media)&lt;/b&gt;&lt;/span&gt;&lt;span&gt;: 게임 내 환경 사운드, 영화 사운드트랙 및 효과음 자동 생성 가능.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;b&gt;가상 인간 인터페이스(Virtual Human Interface)&lt;/b&gt;&lt;/span&gt;&lt;span&gt;: 메타버스, 가상 아바타 등에 활용 가능.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span&gt;결론&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;AudioGPT는 오디오 AI 분야에서 중요한 발전을 이루었으며, 다목적 오디오 생성 및 이해 모델로서의 가능성을 보여준다. 향후 연구에서는 실시간 처리 성능 개선, 학습 데이터의 다변화, 사용자 맞춤형 오디오 생성 기능 등이 주요 과제가 될 것이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Appendix(&lt;/b&gt;&lt;b&gt;추가 참고 문헌 등)&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://kimjy99.github.io/%EB%85%BC%EB%AC%B8%EB%A6%AC%EB%B7%B0/audiogpt/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://kimjy99.github.io/%EB%85%BC%EB%AC%B8%EB%A6%AC%EB%B7%B0/audiogpt/&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://whynn.tistory.com/8&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://whynn.tistory.com/8&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://kimjy99.github.io/%EB%85%BC%EB%AC%B8%EB%A6%AC%EB%B7%B0/audiogpt/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://kimjy99.github.io/%EB%85%BC%EB%AC%B8%EB%A6%AC%EB%B7%B0/audiogpt/&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Study/Literature Review</category>
      <category>audiogpt</category>
      <author>AlienCoder</author>
      <guid isPermaLink="true">https://aliencoder.tistory.com/214</guid>
      <comments>https://aliencoder.tistory.com/214#entry214comment</comments>
      <pubDate>Tue, 1 Apr 2025 01:19:51 +0900</pubDate>
    </item>
    <item>
      <title>[논문 리뷰] FastSpeech 2: Fast and High-Quality End-to-End Text to Speech</title>
      <link>https://aliencoder.tistory.com/213</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;논문 링크: &lt;a href=&quot;https://arxiv.org/abs/2006.04558&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://arxiv.org/abs/2006.04558&lt;/a&gt;&lt;/b&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1743237193228&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;FastSpeech 2: Fast and High-Quality End-to-End Text to Speech&quot; data-og-description=&quot;Non-autoregressive text to speech (TTS) models such as FastSpeech can synthesize speech significantly faster than previous autoregressive models with comparable quality. The training of FastSpeech model relies on an autoregressive teacher model for duratio&quot; data-og-host=&quot;arxiv.org&quot; data-og-source-url=&quot;https://arxiv.org/abs/2006.04558&quot; data-og-url=&quot;https://arxiv.org/abs/2006.04558v8&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/mk4Cx/hyYxG9oMJH/dVI5TRi8dSg2c5ZJkcU5C1/img.png?width=1200&amp;amp;height=700&amp;amp;face=0_0_1200_700,https://scrap.kakaocdn.net/dn/EfvLw/hyYxFbA8TS/LDQrXgSMWwoYfizNS5VGzK/img.png?width=1000&amp;amp;height=1000&amp;amp;face=0_0_1000_1000&quot;&gt;&lt;a href=&quot;https://arxiv.org/abs/2006.04558&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://arxiv.org/abs/2006.04558&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/mk4Cx/hyYxG9oMJH/dVI5TRi8dSg2c5ZJkcU5C1/img.png?width=1200&amp;amp;height=700&amp;amp;face=0_0_1200_700,https://scrap.kakaocdn.net/dn/EfvLw/hyYxFbA8TS/LDQrXgSMWwoYfizNS5VGzK/img.png?width=1000&amp;amp;height=1000&amp;amp;face=0_0_1000_1000');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;FastSpeech 2: Fast and High-Quality End-to-End Text to Speech&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Non-autoregressive text to speech (TTS) models such as FastSpeech can synthesize speech significantly faster than previous autoregressive models with comparable quality. The training of FastSpeech model relies on an autoregressive teacher model for duratio&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;arxiv.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-pm-slice=&quot;1 1 []&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span&gt;1. 서론&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span&gt;1.1 논문 선정 이유&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;FastSpeech 2는 기존 FastSpeech의 한계를 극복하고, 더욱 높은 음질과 자연스러운 Prosody(억양)를 제공하기 위해 개발된 &lt;b&gt;TTS(Text-to-Speech) 모델&lt;/b&gt;이다. 본 논문 리뷰에서는 FastSpeech 2의 핵심 아이디어와 방법론을 분석하고, 실험 결과 및 연구의 장단점을 살펴보고자 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span&gt;2. 논문 요약&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span&gt;2.1 논문 제목 및 저자 정보&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-spread=&quot;false&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;&lt;b&gt;제목&lt;/b&gt;&lt;/span&gt;&lt;span&gt;: FastSpeech 2: Fast and High-Quality End-to-End Text to Speech&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;b&gt;저자&lt;/b&gt;&lt;/span&gt;&lt;span&gt;: Yi Ren, Chenxu Hu, Xu Tan, Tao Qin, Sheng Zhao, Zhou Zhao, Tie-Yan Liu&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;b&gt;출처&lt;/b&gt;&lt;/span&gt;&lt;span&gt;: ICLR 2021&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span&gt;2.2 연구 배경 및 목적&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;FastSpeech 1은 Non-Autoregressive 방식을 도입하여 속도를 개선했지만, Prosody(억양 및 강세) 조절이 제한적이고, Tacotron 2의 Attention 정보를 사용한 길이 예측 방식이 최적이 아니라는 문제가 있었다. 뿐만 아니라 FastSpeech 1은 세부적인 표현 능력이 비교적 부족하였고, 품질 최적화가 쉽지 않았다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;FastSpeech 2는 이를 해결하기 위해 &lt;/span&gt;&lt;span&gt;&lt;b&gt;Pitch(기본 주파수), Energy(음성 에너지) 예측 모듈을 추가&lt;/b&gt;&lt;/span&gt;&lt;span&gt;하고, &lt;/span&gt;&lt;span&gt;&lt;b&gt;데이터 기반 Duration 모델링을 도입&lt;/b&gt;&lt;/span&gt;&lt;span&gt;하여 더욱 자연스러운 음성을 합성하는 것을 목표로 한다.&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span&gt;3. 핵심 내용 분석&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span&gt;3.1 방법론&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;FastSpeech 2는 FastSpeech의 한계를 극복하기 위해 다음과 같은 주요 개선점을 도입하였다.&lt;/span&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-spread=&quot;true&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span&gt;&lt;b&gt;Pitch(기본 주파수) 및 Energy 예측 모듈 추가&lt;/b&gt;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-spread=&quot;false&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;음성의 억양과 강세를 보다 세밀하게 조절하기 위해 Pitch(F0)와 Energy를 예측하는 모듈을 추가하였다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;이를 통해 감정 표현 및 자연스러운 Prosody가 가능해졌다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Pitch는 예측이 어려운 중요한 요소로, Continuous Wavelet Transform을 활용하여 이를 정확하게 예측할 수 있도록 개선하였다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;b&gt;데이터 기반 Duration 모델링&lt;/b&gt;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-spread=&quot;false&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;FastSpeech 1은 Tacotron 2의 Attention 정보를 사용하여 길이를 예측했지만, FastSpeech 2는 음성 데이터에서 직접 Duration 정보를 추출하여 더욱 정교한 길이 예측을 수행한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;이를 통해 Autoregressive 모델 대비 자연스러운 음성 길이를 유지할 수 있다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;b&gt;End-to-End 학습 가능&lt;/b&gt;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-spread=&quot;false&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;FastSpeech 2는 Mel-Spectrogram을 생성하는 것뿐만 아니라, 음성을 직접 출력하는 &lt;b&gt;Vocoder&lt;/b&gt;를 통합하여 End-to-End 학습이 가능하다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;이를 통해 TTS 시스템의 구현이 더욱 간결해지고 효율성이 향상되었다.&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;b&gt;Teacher-Student&amp;nbsp;Distillation&lt;/b&gt; &lt;br /&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot; data-spread=&quot;false&quot;&gt;
&lt;li&gt;&lt;span&gt;Teacher-Student Distillation에서는 데이터 단순화로 인한 정보 손실을 방지하기 위해, teacher의 출력 대신 ground-truth target으로 FastSpeech 2를 직접 학습시켜 &lt;/span&gt;&lt;span&gt;더 정확한 학습이 가능하고, 정보 손실을 최소화할 수 있다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt; FastSpeech&amp;nbsp;2s&amp;nbsp;제시&lt;/b&gt;&lt;br /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot; data-spread=&quot;false&quot;&gt;
&lt;li&gt;음성 합성을 단순화하기 위해, mel-spectrogram을 중간 출력으로 사용하지 않고, text에서 직접 음성을 생성하는 FastSpeech 2s 모델을 제시하였다.&lt;/li&gt;
&lt;li&gt;이 모델은 멜 스펙트로그램을 중간 출력 없이 바로 음성을 생성함으로써 음성 합성의 복잡성을 줄이고 효율성을 높였다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;FastSpeech&amp;nbsp;2의&amp;nbsp;모델&amp;nbsp;구조를&amp;nbsp;구성하는&amp;nbsp;주요&amp;nbsp;모듈은&amp;nbsp;다음과&amp;nbsp;같다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1386&quot; data-origin-height=&quot;698&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/biMCHu/btsM04uXJu8/H0FfkTshD3SXCvygq9AlXK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/biMCHu/btsM04uXJu8/H0FfkTshD3SXCvygq9AlXK/img.png&quot; data-alt=&quot;Fig. 1. FastSpeech 2 Architecture&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/biMCHu/btsM04uXJu8/H0FfkTshD3SXCvygq9AlXK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbiMCHu%2FbtsM04uXJu8%2FH0FfkTshD3SXCvygq9AlXK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;728&quot; height=&quot;367&quot; data-origin-width=&quot;1386&quot; data-origin-height=&quot;698&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Fig. 1. FastSpeech 2 Architecture&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;The&amp;nbsp;overall&amp;nbsp;architecture&amp;nbsp;for&amp;nbsp;FastSpeech&amp;nbsp;2&amp;nbsp;and&amp;nbsp;2s.&amp;nbsp;LR&amp;nbsp;in&amp;nbsp;subfigure&amp;nbsp;(b)&amp;nbsp;denotes&amp;nbsp;the&amp;nbsp;length&amp;nbsp;regulator&amp;nbsp;proposed&amp;nbsp;in&amp;nbsp;FastSpeech.&amp;nbsp;LN&amp;nbsp;in&amp;nbsp;subfigure&amp;nbsp;(c)&amp;nbsp;denotes&amp;nbsp;layer&amp;nbsp;normalization.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;&lt;b&gt;(a)&amp;nbsp;FastSpeech&amp;nbsp;2&lt;/b&gt; &lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;FastSpeech 2는 FastSpeech 1의 한계를 개선한 비자동회귀(Non-Autoregressive) 음성 합성 모델이다.&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;FastSpeech 1과 동일한 Feed-Forward Transformer (FFT) 기반 구조를 사용하지만, 추가적인 Prosody 정보를 학습하여 더욱 자연스러운 음성을 생성한다.&lt;/li&gt;
&lt;li&gt;Variance Adaptor를 도입하여 Pitch, Energy, Duration을 조정할 수 있도록 개선하였다.&lt;/li&gt;
&lt;li&gt;Attention&amp;nbsp;기반&amp;nbsp;길이&amp;nbsp;예측&amp;nbsp;방식&amp;nbsp;대신&amp;nbsp;데이터&amp;nbsp;기반&amp;nbsp;Duration&amp;nbsp;모델링을&amp;nbsp;사용하여&amp;nbsp;발화&amp;nbsp;길이를&amp;nbsp;더&amp;nbsp;정확하게&amp;nbsp;예측한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;&lt;b&gt;(b)&amp;nbsp;Variance&amp;nbsp;Adaptor&lt;/b&gt; &lt;br /&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;Variance Adaptor는 발화의 Prosody(억양, 강세 등)를 조절하는 모듈이다.&amp;nbsp;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Pitch Predictor: 기본 주파수(F0)를 예측하여 억양(intonation)을 조절한다. 예) 어린아이 목소리, 어른 목소리 등&lt;/li&gt;
&lt;li&gt;Energy Predictor: 음성 에너지를 조절하여 발화 강도를 조정한다. 예) 소리의 강약(볼륨)&lt;/li&gt;
&lt;li&gt;Duration Predictor: 음소의 지속 시간을 예측하여 발화 속도를 조절한다. 예) 말의 속도&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;br /&gt;참고로 여기서 Pitch Predictor와 Energy Predictor는 FastSpeech 1에 없었던 새로 추가된 모듈이다. &lt;/span&gt;&lt;span&gt;이 세 모듈을 통해 FastSpeech 2는 더 자연스럽고 다양한 스타일의 음성을 합성할 수 있다. &lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;&lt;b&gt;(c)&amp;nbsp;Duration/Pitch/Energy&amp;nbsp;Predictor&lt;/b&gt; &lt;br /&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;FastSpeech 2는 각각의 Prosody 요소를 별도로 예측하는 3개의 예측기를 포함한다.&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Duration Predictor: 음소별 지속 시간을 예측하여 Length Regulator에 입력한다.&lt;/li&gt;
&lt;li&gt;Pitch Predictor: 음소별로 기본 주파수(F0)를 예측하여 억양을 조절한다.&lt;/li&gt;
&lt;li&gt;Energy&amp;nbsp;Predictor:&amp;nbsp;음성의&amp;nbsp;에너지를&amp;nbsp;조절하여&amp;nbsp;감정&amp;nbsp;표현과&amp;nbsp;발화&amp;nbsp;강도를&amp;nbsp;조정한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;br /&gt;이&amp;nbsp;세&amp;nbsp;가지&amp;nbsp;요소를&amp;nbsp;활용하여&amp;nbsp;FastSpeech&amp;nbsp;2는&amp;nbsp;보다&amp;nbsp;자연스럽고&amp;nbsp;감정&amp;nbsp;표현이&amp;nbsp;풍부한&amp;nbsp;음성을&amp;nbsp;생성할&amp;nbsp;수&amp;nbsp;있다. &lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span&gt;(d)&amp;nbsp;Waveform&amp;nbsp;Decoder&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Waveform Decoder는 합성된 음향 특성을 실제 음성 신호(Waveform)로 변환하는 역할을 한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;FastSpeech 2는 별도의 신경망 기반 보코더(Neural Vocoder)를 사용하여 Waveform을 생성한다.&lt;/li&gt;
&lt;li&gt;Tacotron 2와 마찬가지로 WaveGlow, Parallel WaveGAN, HiFi-GAN 등의 보코더와 결합하여 음성을 합성한다.&lt;/li&gt;
&lt;li&gt;Waveform&amp;nbsp;Decoder의&amp;nbsp;성능에&amp;nbsp;따라&amp;nbsp;최종&amp;nbsp;음질이&amp;nbsp;결정되며,&amp;nbsp;최신&amp;nbsp;신경망&amp;nbsp;보코더를&amp;nbsp;활용하면&amp;nbsp;더&amp;nbsp;자연스러운&amp;nbsp;음성을&amp;nbsp;생성할&amp;nbsp;수&amp;nbsp;있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;br /&gt;FastSpeech&amp;nbsp;2는&amp;nbsp;이러한&amp;nbsp;개선을&amp;nbsp;통해&amp;nbsp;FastSpeech&amp;nbsp;1보다&amp;nbsp;Prosody&amp;nbsp;조절이&amp;nbsp;자유롭고,&amp;nbsp;더&amp;nbsp;자연스러운&amp;nbsp;음성을&amp;nbsp;합성할&amp;nbsp;수&amp;nbsp;있도록&amp;nbsp;최적화되었다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span&gt;3.2 실험 결과&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1316&quot; data-origin-height=&quot;452&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yuEt5/btsM1kKCkAx/k3r5GIG5y17Q77jDktcX7K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yuEt5/btsM1kKCkAx/k3r5GIG5y17Q77jDktcX7K/img.png&quot; data-alt=&quot;Table 1: Audio quality comparison.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yuEt5/btsM1kKCkAx/k3r5GIG5y17Q77jDktcX7K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FyuEt5%2FbtsM1kKCkAx%2Fk3r5GIG5y17Q77jDktcX7K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;676&quot; height=&quot;232&quot; data-origin-width=&quot;1316&quot; data-origin-height=&quot;452&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Table 1: Audio quality comparison.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1339&quot; data-origin-height=&quot;242&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/G4z6I/btsM0tnYsUs/JKr7Lj3XdbrpqKgNFB5YHk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/G4z6I/btsM0tnYsUs/JKr7Lj3XdbrpqKgNFB5YHk/img.png&quot; data-alt=&quot;Table 2: The comparison of training time and inference latency in waveform synthesis. The training time of FastSpeech includes teacher and student training. RTF denotes the real-time factor, that is the time (in seconds) required for the system to synthesize one second waveform. The training and inference latency tests are conducted on a server with 36 Intel Xeon CPUs, 256GB memory, 1 NVIDIA V100 GPU and batch size of 48 for training and 1 for inference. Besides, we do not include the time of GPU memory garbage collection and transferring input and output data between the CPU and the GPU. The speedup in waveform synthesis for FastSpeech is larger than that reported in Ren et al. (2019) since we use Parallel WaveGAN as the vocoder which is much faster than WaveGlow.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/G4z6I/btsM0tnYsUs/JKr7Lj3XdbrpqKgNFB5YHk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FG4z6I%2FbtsM0tnYsUs%2FJKr7Lj3XdbrpqKgNFB5YHk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;682&quot; height=&quot;123&quot; data-origin-width=&quot;1339&quot; data-origin-height=&quot;242&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Table 2: The comparison of training time and inference latency in waveform synthesis. The training time of FastSpeech includes teacher and student training. RTF denotes the real-time factor, that is the time (in seconds) required for the system to synthesize one second waveform. The training and inference latency tests are conducted on a server with 36 Intel Xeon CPUs, 256GB memory, 1 NVIDIA V100 GPU and batch size of 48 for training and 1 for inference. Besides, we do not include the time of GPU memory garbage collection and transferring input and output data between the CPU and the GPU. The speedup in waveform synthesis for FastSpeech is larger than that reported in Ren et al. (2019) since we use Parallel WaveGAN as the vocoder which is much faster than WaveGlow.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FastSpeech 2는 FastSpeech와 비교하여 3.12배의 학습 속도 향상을 보이며, Transformer TTS와 비교하였을 때도 FastSpeech 2, 2s는 각각 47.8, 51.8배의 오디오 생성 속도 향상을 보이고 있다. FastSpeech 2s는 end-to-end 방식을 사용하기 때문에 FastSpeech 2보다 빠르다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;777&quot; data-origin-height=&quot;374&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/T4guA/btsM1jrnJzW/9jkAmfOeTRi1j17ZxILv8K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/T4guA/btsM1jrnJzW/9jkAmfOeTRi1j17ZxILv8K/img.png&quot; data-alt=&quot;Table 3: Standard deviation (&amp;amp;sigma;), skewness (&amp;amp;gamma;), kurtosis (K) and average DTW distances (DTW) of pitch in ground-truth and synthesized audio.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/T4guA/btsM1jrnJzW/9jkAmfOeTRi1j17ZxILv8K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FT4guA%2FbtsM1jrnJzW%2F9jkAmfOeTRi1j17ZxILv8K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;580&quot; height=&quot;279&quot; data-origin-width=&quot;777&quot; data-origin-height=&quot;374&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Table 3: Standard deviation (&amp;sigma;), skewness (&amp;gamma;), kurtosis (K) and average DTW distances (DTW) of pitch in ground-truth and synthesized audio.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FastSpeech 2와 2s에서 생성된 오디오의 Pitch(기본 주파수)와 ground-truth pitch(실제 정답 pitch) 사이의 평균 DTW 거리(Dynamic Time Warping distance)가 더 작다는 것은, 모델이 생성한 Pitch가 실제 정답에 더 가까운 값임을 의미한다. 즉, FastSpeech 2와 2s는 Pitch 예측이 더 정확하고, 그 차이가 적다는 것을 보여준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1246&quot; data-origin-height=&quot;319&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cWXpRh/btsM1kqfHCL/Q7ZuRxS0uVEkVQSkrTMtu0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cWXpRh/btsM1kqfHCL/Q7ZuRxS0uVEkVQSkrTMtu0/img.png&quot; data-alt=&quot;Table 6: CMOS comparison in the ablation studies&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cWXpRh/btsM1kqfHCL/Q7ZuRxS0uVEkVQSkrTMtu0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcWXpRh%2FbtsM1kqfHCL%2FQ7ZuRxS0uVEkVQSkrTMtu0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;629&quot; height=&quot;161&quot; data-origin-width=&quot;1246&quot; data-origin-height=&quot;319&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Table 6: CMOS comparison in the ablation studies&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Table 6은 Pitch와 Energy Input에 대한 ablation 테스트 결과이다. 여기서 FastSpeech 2, 2s에서 각각 Energy를 제거하는 경우와 Pitch를 제거하는 경우 -0.245, -1.130의 CMOS 저하가 발생하였다는 결과를 보여주고 있다. Pitch와 Energy를 모두 제거하는 경우도 현저한 품질 저하가 발생한다는 점을 시사하며 Pitch와 Energy가 중요한 요소라는 점을 환기하고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 실험 결과들에 대해 정리하자면 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-spread=&quot;false&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;FastSpeech 2는 FastSpeech 1과 비교하여 MOS(Mean Opinion Score) 평가에서 음질이 향상되었음을 확인하였다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Tacotron 2보다 inference 속도가 훨씬 빠르면서도, 유사한 수준의 음질을 제공한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Pitch 및 Energy 예측을 도입함으로써, 보다 다양한 스타일의 음성 합성이 가능해졌다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span&gt;3.3 결론&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;FastSpeech 2는 FastSpeech 1의 단점을 보완하여, 속도와 음질을 동시에 향상시킨 모델이다. 실험 결과에서도 Prosody의 자연스러움이 개선되었으며, 더욱 다양한 스타일의 음성 합성이 가능해졌다. 이를 통해 실제 TTS 응용에서 활용될 가능성이 더욱 높아졌다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span&gt;4. 장점과 한계&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span&gt;4.1 연구의 장점&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-spread=&quot;false&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;&lt;b&gt;Autoregressive 모델보다 빠른 속도&lt;/b&gt;&lt;/span&gt;&lt;span&gt;를 유지하면서도, 더욱 자연스러운 음성을 생성할 수 있다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;b&gt;Pitch 및 Energy 예측 모듈을 추가하여 Prosody 조절이 가능&lt;/b&gt;&lt;/span&gt;&lt;span&gt;해졌다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;b&gt;end-to-end 학습이 가능&lt;/b&gt;&lt;/span&gt;&lt;span&gt;하여 TTS 시스템의 구현이 간편해졌다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span&gt;4.2 연구의 한계와 개선 가능성&lt;/span&gt;&lt;/b&gt;&lt;span&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;추가적인 연구를 통해 아래 부분에 대한 개선이 가능할 것으로 기대된다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-spread=&quot;false&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;감정 표현의 정밀한 조절은 아직 부족하며, 더욱 세밀한 컨트롤이 필요&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;일부 경우에서 여전히 Tacotron 2 대비 음질이 약간 떨어짐&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Prosody의 자연스러움을 개선&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span&gt;5. 관련 연구 및 응용&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span&gt;5.1 관련 연구 비교&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-spread=&quot;false&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;&lt;b&gt;Tacotron 2&lt;/b&gt;&lt;/span&gt;&lt;span&gt; (2018): 자연스러운 음성을 합성할 수 있지만, 속도가 느리고 inference 과정에서 오류가 누적될 수 있다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;b&gt;FastSpeech 1&lt;/b&gt;&lt;/span&gt;&lt;span&gt; (2019): 속도가 빠르지만, Prosody 조절이 제한적이었다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;b&gt;FastSpeech 2&lt;/b&gt;&lt;/span&gt;&lt;span&gt; (2021): FastSpeech 1의 단점을 보완하여 더욱 자연스러운 음성을 합성할 수 있도록 개선되었다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span&gt;5.2 실질적 응용 가능성&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-spread=&quot;false&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;FastSpeech 2는 &lt;/span&gt;&lt;span&gt;&lt;b&gt;실시간 TTS 시스템&lt;/b&gt;&lt;/span&gt;&lt;span&gt;에 적합하여 음성 비서, 내비게이션, 오디오북 등 다양한 서비스에 활용될 수 있다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;b&gt;Edge Device&lt;/b&gt;&lt;/span&gt;&lt;span&gt;에서도 효율적으로 동작할 수 있어, 모바일 기기나 IoT 환경에서도 사용될 가능성이 크다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;향후 감정 표현 및 스타일 조절 기능이 강화된다면, 더욱 폭넓은 음성 합성 응용이 가능할 것으로 기대된다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span&gt;결론&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;FastSpeech 2는 기존 FastSpeech 모델의 한계를 개선하여, 속도와 음질을 동시에 향상시킨 중요한 연구이다. 특히 Pitch 및 Energy 예측을 통해 Prosody의 자연스러움을 개선하였으며, 이를 통해 실질적인 TTS 응용에서 더욱 강력한 성능을 발휘할 것으로 기대된다. 향후 연구를 통해 감정 표현 및 스타일 컨트롤이 더욱 정교해진다면, 실시간 음성 합성 분야에서 핵심적인 기술로 자리 잡을 가능성이 크다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Appendix(&lt;/b&gt;&lt;b&gt;추가 참고 문헌 등)&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;FastSpeech 1:&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://aliencoder.tistory.com/212&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;2025.03.29 - [Literature Review] - [논문 리뷰] FastSpeech: Fast, Robust and Controllable Text to Speech&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;DTW:&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://aliencoder.tistory.com/33&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;2022.02.09 - [Data Science/Data&amp;nbsp;Analysis] - [Python] 길이가 다른 데이터 유사도 측정을 위한 DTW(Dynamic Time Warping)&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://randomsampling.tistory.com/38&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://randomsampling.tistory.com/38&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Study/Literature Review</category>
      <category>fastspeech 2</category>
      <author>AlienCoder</author>
      <guid isPermaLink="true">https://aliencoder.tistory.com/213</guid>
      <comments>https://aliencoder.tistory.com/213#entry213comment</comments>
      <pubDate>Sun, 30 Mar 2025 18:08:10 +0900</pubDate>
    </item>
    <item>
      <title>[논문 리뷰] FastSpeech: Fast, Robust and Controllable Text to Speech</title>
      <link>https://aliencoder.tistory.com/212</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;논문 링크: &lt;a href=&quot;https://arxiv.org/abs/1905.09263&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://arxiv.org/abs/1905.09263&lt;/a&gt;&lt;/b&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1743237104668&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;FastSpeech: Fast, Robust and Controllable Text to Speech&quot; data-og-description=&quot;Neural network based end-to-end text to speech (TTS) has significantly improved the quality of synthesized speech. Prominent methods (e.g., Tacotron 2) usually first generate mel-spectrogram from text, and then synthesize speech from the mel-spectrogram us&quot; data-og-host=&quot;arxiv.org&quot; data-og-source-url=&quot;https://arxiv.org/abs/1905.09263&quot; data-og-url=&quot;https://arxiv.org/abs/1905.09263v5&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dCfRDa/hyYyTtvYKx/G87kkVoAwdgpyC24zRJnY0/img.png?width=1200&amp;amp;height=700&amp;amp;face=0_0_1200_700,https://scrap.kakaocdn.net/dn/b6K9Uj/hyYyLWxWhf/0ksCiNp9eMyYtfEkYDKPd1/img.png?width=1000&amp;amp;height=1000&amp;amp;face=0_0_1000_1000&quot;&gt;&lt;a href=&quot;https://arxiv.org/abs/1905.09263&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://arxiv.org/abs/1905.09263&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dCfRDa/hyYyTtvYKx/G87kkVoAwdgpyC24zRJnY0/img.png?width=1200&amp;amp;height=700&amp;amp;face=0_0_1200_700,https://scrap.kakaocdn.net/dn/b6K9Uj/hyYyLWxWhf/0ksCiNp9eMyYtfEkYDKPd1/img.png?width=1000&amp;amp;height=1000&amp;amp;face=0_0_1000_1000');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;FastSpeech: Fast, Robust and Controllable Text to Speech&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Neural network based end-to-end text to speech (TTS) has significantly improved the quality of synthesized speech. Prominent methods (e.g., Tacotron 2) usually first generate mel-spectrogram from text, and then synthesize speech from the mel-spectrogram us&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;arxiv.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-pm-slice=&quot;1 1 []&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span&gt;1. 서론&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span&gt;1.1 논문 선정 이유&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;b&gt;FastSpeech&lt;/b&gt;는 딥러닝 기반 음성 합성(Text-to-Speech, TTS) 모델로, 기존의 Autoregressive 방식의 TTS 모델이 가지는 느린 속도와 오류 전파 문제를 해결하기 위해 제안되었다. 본 논문 리뷰를 통해 FastSpeech의 핵심 아이디어, 실험 결과 및 한계점을 분석하고, 해당 연구가 TTS 분야에 미친 영향을 살펴보고자 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span&gt;2. 논문 요약&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span&gt;2.1 논문 제목 및 저자 정보&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-spread=&quot;false&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;&lt;b&gt;제목&lt;/b&gt;&lt;/span&gt;&lt;span&gt;: FastSpeech: Fast, Robust and Controllable Text to Speech&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;b&gt;저자&lt;/b&gt;&lt;/span&gt;&lt;span&gt;: Yi Ren, Yangjun Ruan, Xu Tan, Tao Qin, Sheng Zhao, Zhou Zhao, Tie-Yan Liu&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;b&gt;출처&lt;/b&gt;&lt;/span&gt;&lt;span&gt;: NeurIPS 2019&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span&gt;2.2 연구 배경 및 목적&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;기존의 TTS 모델은 주로 Autoregressive 방식으로 동작하며, 이를 통해 자연스러운 음성을 합성할 수 있다. Autoregressive 방식은 이전의 출력을 다음 시퀀스의 input으로 사용하는데 이는 품질 저하와 결과 도출 속도가 느리다는 단점이 있다. 또한 구조상 오류가 축적될 수 있으며, 이를 제어하는 것이 쉽지가 않다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;이를 해결하기 위해 FastSpeech는 비자동회귀(Non-Autoregressive) 방식을 사용하여 빠르고 안정적인 음성 합성을 목표로 한다.&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span&gt;3. 핵심 내용 분석&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span&gt;3.1 방법론&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;FastSpeech는 Transformer 기반의 Non-Autoregressive TTS 모델로, 다음과 같은 핵심 아이디어를 포함한다.&lt;/span&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot; data-spread=&quot;false&quot;&gt;
&lt;li&gt;&lt;span&gt;&lt;b&gt;길이 예측 모듈(Length Predictor)&lt;/b&gt;&lt;/span&gt;&lt;span&gt;: Autoregressive 방식에서 존재하는 디코더 스텝을 제거하고, 프레임 길이를 예측하여 병렬 합성을 가능하게 한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;b&gt;Feed-Forward Transformer&lt;/b&gt;&lt;/span&gt;&lt;span&gt;: 음성 합성에서 빠른 연산을 위해, 기존 Transformer를 수정하여 구조적으로 더 간단하고 효율적으로 설계하였다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;b&gt;지속성 및 안정성 향상&lt;/b&gt;&lt;/span&gt;&lt;span&gt;: 학습 과정에서 Teacher-Student 학습 방법을 활용하여, 빠르고 안정적인 음성 출력을 가능하게 한다.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FastSpeech 1의 모델 구조를 구성하는 주요 모듈은 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1442&quot; data-origin-height=&quot;745&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ooJ2H/btsM192TAcc/MNgK5SBbl2YuY8r6ofbdPk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ooJ2H/btsM192TAcc/MNgK5SBbl2YuY8r6ofbdPk/img.png&quot; data-alt=&quot;Fig. 1. FastSpeech Architecture&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ooJ2H/btsM192TAcc/MNgK5SBbl2YuY8r6ofbdPk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FooJ2H%2FbtsM192TAcc%2FMNgK5SBbl2YuY8r6ofbdPk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;703&quot; height=&quot;363&quot; data-origin-width=&quot;1442&quot; data-origin-height=&quot;745&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Fig. 1. FastSpeech Architecture&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;The overall architecture for FastSpeech. (a). The feed-forward Transformer. (b). The feed-forward Transformer block. (c). The length regulator. (d). The duration predictor. MSE loss denotes the loss between predicted and extracted duration, which only exists in the training process.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;(a) Feed-Forward Transformer (FFT) &lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Feed-Forward Transformer는 FastSpeech 1의 기본 구조이며, 텍스트 인코딩과 음향 특성 디코딩을 담당한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Multi-Head&amp;nbsp;Attention:&amp;nbsp;입력된&amp;nbsp;음소(Phoneme)&amp;nbsp;시퀀스에서&amp;nbsp;중요한&amp;nbsp;패턴을&amp;nbsp;학습하며,&amp;nbsp;서로&amp;nbsp;다른&amp;nbsp;위치&amp;nbsp;정보를&amp;nbsp;종합하는&amp;nbsp;역할을&amp;nbsp;한다.&lt;/li&gt;
&lt;li&gt;Add &amp;amp; Norm: Multi-Head Attention 출력을 원래 입력과 더한 후 정규화(Normalization)하여 안정적인 학습을 가능하게 한다.&lt;/li&gt;
&lt;li&gt;Conv1D:&amp;nbsp;1D&amp;nbsp;합성곱을&amp;nbsp;사용하여&amp;nbsp;국소적인&amp;nbsp;특징을&amp;nbsp;학습하며,&amp;nbsp;음소&amp;nbsp;간의&amp;nbsp;관계를&amp;nbsp;더욱&amp;nbsp;정밀하게&amp;nbsp;모델링한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;br /&gt;&lt;b&gt;(b)&amp;nbsp;FFT&amp;nbsp;Block &lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FFT&amp;nbsp;Block은&amp;nbsp;여러&amp;nbsp;개의&amp;nbsp;Feed-Forward&amp;nbsp;Transformer&amp;nbsp;레이어로&amp;nbsp;구성되며,&amp;nbsp;음소와&amp;nbsp;음향&amp;nbsp;특징&amp;nbsp;간의&amp;nbsp;변환을&amp;nbsp;수행한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Duration Predictor: 각 음소의 지속 시간을 예측하여 발화 길이를 조절하는 역할을 한다.&lt;/li&gt;
&lt;li&gt;  = 1.0: 길이 조정 인자로, 기본적으로 원래 예측된 길이를 그대로 사용한다.&lt;/li&gt;
&lt;li&gt; &amp;nbsp;=&amp;nbsp;[2,2,3,1]:&amp;nbsp;FFT&amp;nbsp;Block&amp;nbsp;내부의&amp;nbsp;레이어&amp;nbsp;구성을&amp;nbsp;나타내며,&amp;nbsp;여러&amp;nbsp;개의&amp;nbsp;블록을&amp;nbsp;통해&amp;nbsp;깊이&amp;nbsp;있는&amp;nbsp;특징&amp;nbsp;학습이&amp;nbsp;가능하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;br /&gt;&lt;b&gt;(c)&amp;nbsp;Length&amp;nbsp;Regulator &lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Length&amp;nbsp;Regulator는&amp;nbsp;음소의&amp;nbsp;지속&amp;nbsp;시간을&amp;nbsp;조정하는&amp;nbsp;모듈이며,&amp;nbsp;Autoregressive&amp;nbsp;Transformer&amp;nbsp;TTS에서&amp;nbsp;추출한&amp;nbsp;지속&amp;nbsp;시간&amp;nbsp;정보를&amp;nbsp;활용한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Duration Extractor: Tacotron 2에서 사용한 Attention Alignment 정보를 기반으로 각 음소의 지속 시간을 추출한다.&lt;/li&gt;
&lt;li&gt;Conv1D + Norm: 지속 시간 정보를 학습하기 위해 1D 합성곱과 정규화를 적용한다.&lt;/li&gt;
&lt;li&gt;Linear Layer: 지속 시간을 예측한 후, 이를 음소 길이 조정에 활용하기 위해 변환하는 역할을 한다.&lt;/li&gt;
&lt;li&gt;MSE Loss: 예측된 지속 시간과 실제 지속 시간 간의 Mean Squared Error(MSE) 손실을 계산하여 모델을 학습한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;br /&gt;&lt;b&gt;(d)&amp;nbsp;Duration&amp;nbsp;Predictor&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Duration&amp;nbsp;Predictor는&amp;nbsp;각&amp;nbsp;음소의&amp;nbsp;지속&amp;nbsp;시간을&amp;nbsp;예측하는&amp;nbsp;모듈이다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Conv1D 기반의 신경망 구조를 사용하며, 입력된 음소 시퀀스를 기반으로 지속 시간을 추론한다.&lt;/li&gt;
&lt;li&gt;Tacotron 2의 Attention 기반 길이 예측 방식과 달리, 명확한 목표값(ground-truth duration)을 학습하는 방식이다.&lt;/li&gt;
&lt;li&gt;예측된 지속 시간은 Length Regulator를 통해 조정된 후 음향 특성 변환 과정에 반영된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 구조를 통해 FastSpeech 1은 Autoregressive 모델 대비 빠른 속도로 음성을 합성할 수 있으나, Prosody 조절이 제한적이며 음질 최적화가 어렵다는 단점이 존재한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span&gt;3.2 실험 결과&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1381&quot; data-origin-height=&quot;456&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mBUcL/btsM2FmW5eR/hHZPB4ugcPBPncwR3wQa8k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mBUcL/btsM2FmW5eR/hHZPB4ugcPBPncwR3wQa8k/img.png&quot; data-alt=&quot;Table 1: The MOS with 95% confidence intervals&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mBUcL/btsM2FmW5eR/hHZPB4ugcPBPncwR3wQa8k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmBUcL%2FbtsM2FmW5eR%2FhHZPB4ugcPBPncwR3wQa8k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;663&quot; height=&quot;219&quot; data-origin-width=&quot;1381&quot; data-origin-height=&quot;456&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Table 1: The MOS with 95% confidence intervals&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;MOS(Mean Opinion Score)&lt;/b&gt;는 음성 합성, 통신, 오디오 및 비디오 품질 평가에서 사용되는 주관적 품질 평가 지표이다. MOS는 평가자가 시스템에서 생성한 음성 또는 비디오의 품질을 1에서 5까지의 점수로 평가하도록 한다. 보통 여러 명의 평가자(사람)가 각 샘플을 평가한 후, 그들의 점수를 평균 내어 결과를 도출한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;평점 기준은 일반적으로 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;5:&amp;nbsp;완벽한&amp;nbsp;품질&amp;nbsp;(완벽한&amp;nbsp;이해&amp;nbsp;가능성,&amp;nbsp;자연스러움) &lt;/li&gt;
&lt;li&gt;4:&amp;nbsp;매우&amp;nbsp;좋음&amp;nbsp;(거의&amp;nbsp;완벽하지만&amp;nbsp;미세한&amp;nbsp;결점&amp;nbsp;있음) &lt;/li&gt;
&lt;li&gt;3:&amp;nbsp;보통&amp;nbsp;(다소&amp;nbsp;부족하지만&amp;nbsp;수용&amp;nbsp;가능한&amp;nbsp;품질) &lt;/li&gt;
&lt;li&gt;2:&amp;nbsp;나쁨&amp;nbsp;(일부&amp;nbsp;인식이&amp;nbsp;어려운&amp;nbsp;부분이&amp;nbsp;있음) &lt;/li&gt;
&lt;li&gt;1:&amp;nbsp;매우&amp;nbsp;나쁨&amp;nbsp;(이해&amp;nbsp;불가능하거나&amp;nbsp;큰&amp;nbsp;결함이&amp;nbsp;있음)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1063&quot; data-origin-height=&quot;293&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xfdzc/btsMZ3b06TE/URCN2bNxVJZfmXFzKylur1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xfdzc/btsMZ3b06TE/URCN2bNxVJZfmXFzKylur1/img.png&quot; data-alt=&quot;Table 2: The comparison of inference latency with 95% confidence intervals. The evaluation is conducted on a server with 12 Intel Xeon CPU, 256GB memory, 1 NVIDIA V100 GPU and batch size of 1. The average length of the generated mel-spectrograms for the two systems are both about 560.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xfdzc/btsMZ3b06TE/URCN2bNxVJZfmXFzKylur1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fxfdzc%2FbtsMZ3b06TE%2FURCN2bNxVJZfmXFzKylur1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;639&quot; height=&quot;176&quot; data-origin-width=&quot;1063&quot; data-origin-height=&quot;293&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Table 2: The comparison of inference latency with 95% confidence intervals. The evaluation is conducted on a server with 12 Intel Xeon CPU, 256GB memory, 1 NVIDIA V100 GPU and batch size of 1. The average length of the generated mel-spectrograms for the two systems are both about 560.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1072&quot; data-origin-height=&quot;244&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/2JBCC/btsM2vxSJ4M/XQJmwmq0X1NBD2oxfKh7Ak/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/2JBCC/btsM2vxSJ4M/XQJmwmq0X1NBD2oxfKh7Ak/img.png&quot; data-alt=&quot;Table 3: The comparison of robustness between FastSpeech and other systems on the 50 particularly hard sentences. Each kind of word error is counted at most once per sentence&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/2JBCC/btsM2vxSJ4M/XQJmwmq0X1NBD2oxfKh7Ak/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F2JBCC%2FbtsM2vxSJ4M%2FXQJmwmq0X1NBD2oxfKh7Ak%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;623&quot; height=&quot;142&quot; data-origin-width=&quot;1072&quot; data-origin-height=&quot;244&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Table 3: The comparison of robustness between FastSpeech and other systems on the 50 particularly hard sentences. Each kind of word error is counted at most once per sentence&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-spread=&quot;false&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;MOS 평가에서 FastSpeech는 기존의 Autoregressive 모델과 유사한 수준의 음질을 유지한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;FastSpeech는&amp;nbsp;Autoregressive&amp;nbsp;방식의&amp;nbsp;TTS&amp;nbsp;모델(Tacotron&amp;nbsp;2)보다&amp;nbsp;10배&amp;nbsp;빠른&amp;nbsp;합성&amp;nbsp;속도를&amp;nbsp;보여준다. &lt;br /&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;길이 예측 모듈을 통한 비자동회귀 방식을 사용하여 inference 과정에서 발생하는 불안정성을 줄이는 데 성공하였다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span&gt;3.3 결론&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;FastSpeech는 비자동회귀 방식을 도입하여 TTS 모델의 속도와 안정성을 크게 개선하였다. 실험 결과에서도 기존 Autoregressive 모델과 비교하여 속도와 품질 측면에서 경쟁력 있는 성능을 보였다. 이는 실시간 음성 합성 시스템에 적용될 가능성을 높이는 중요한 연구 결과이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span&gt;4. 장점과 한계&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span&gt;4.1 연구의 장점&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-spread=&quot;false&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;Autoregressive 방식보다 &lt;/span&gt;&lt;span&gt;&lt;b&gt;현저히 빠른 속도&lt;/b&gt;&lt;/span&gt;&lt;span&gt;를 제공한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;b&gt;Non-Autoregressive 구조&lt;/b&gt;&lt;/span&gt;&lt;span&gt;로 인해 디코딩 시 오류 전파 문제가 발생하지 않는다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Transformer 기반 모델을 활용하여 &lt;/span&gt;&lt;span&gt;&lt;b&gt;학습 및 추론 안정성을 향상&lt;/b&gt;&lt;/span&gt;&lt;span&gt;시켰다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span&gt;4.2 연구의 한계와 개선 가능성&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-spread=&quot;false&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;Non-Autoregressive 방식이지만, 길이 예측의 정확도에 따라 음질이 변할 가능성이 있다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;FastSpeech 1에서는 &lt;/span&gt;&lt;span&gt;&lt;b&gt;음질이 Tacotron 2보다 다소 낮은 문제&lt;/b&gt;&lt;/span&gt;&lt;span&gt;가 보고되었으며, 이를 해결하기 위해 후속 연구(FastSpeech 2)에서 변형이 이루어졌다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;b&gt;Prosody(억양 및 강세) 제어가 제한적&lt;/b&gt;&lt;/span&gt;&lt;span&gt;이기 때문에, 보다 세밀한 음성 조절 기능이 추가될 필요가 있다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span&gt;5. 관련 연구 및 응용&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span&gt;5.1 관련 연구 비교&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-spread=&quot;false&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;&lt;b&gt;Tacotron 2&lt;/b&gt;&lt;/span&gt;&lt;span&gt; (2018): Autoregressive 방식으로 자연스러운 음성 합성이 가능하지만 속도가 느리고, inference 과정에서 오류가 누적될 수 있다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;b&gt;WaveNet&lt;/b&gt;&lt;/span&gt;&lt;span&gt; (2016): 음질이 뛰어나지만, 실시간 처리 속도가 느려 실용성이 낮다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;b&gt;FastSpeech 2&lt;/b&gt;&lt;/span&gt;&lt;span&gt; (2020): FastSpeech의 단점을 보완하여 더 자연스러운 음성을 생성할 수 있도록 개선되었다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span&gt;5.2 실질적 응용 가능성&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-spread=&quot;false&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;FastSpeech는 &lt;/span&gt;&lt;span&gt;&lt;b&gt;실시간 TTS 시스템&lt;/b&gt;&lt;/span&gt;&lt;span&gt;에 적합하여 음성 비서, 내비게이션, 오디오북 등 다양한 서비스에 활용될 수 있다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;b&gt;Edge Device&lt;/b&gt;&lt;/span&gt;&lt;span&gt;에서 효율적으로 동작할 수 있어, 모바일 기기나 IoT 환경에서도 사용될 가능성이 크다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;향후 감정 표현 및 억양 조절 기능이 강화된다면, 더욱 폭넓은 음성 합성 응용이 가능할 것으로 기대된다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span&gt;결론&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;FastSpeech는 기존 TTS 모델이 가진 속도 및 안정성 문제를 해결하며, Non-Autoregressive 방식의 가능성을 보여준 중요한 연구이다. 다만, 음질과 Prosody 조절 측면에서 추가적인 개선이 필요하며, 이를 해결하기 위한 후속 연구가 지속적으로 진행되고 있다. 향후 FastSpeech 기반 모델이 더 정교해진다면, 실시간 음성 합성 기술의 핵심 요소로 자리 잡을 것으로 예상된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Study/Literature Review</category>
      <category>fastspeech</category>
      <author>AlienCoder</author>
      <guid isPermaLink="true">https://aliencoder.tistory.com/212</guid>
      <comments>https://aliencoder.tistory.com/212#entry212comment</comments>
      <pubDate>Sat, 29 Mar 2025 17:48:30 +0900</pubDate>
    </item>
    <item>
      <title>[Pytorch] view, reshape, transpose, permute 함수 사용법과 contiguous 특성</title>
      <link>https://aliencoder.tistory.com/211</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;python.jpg&quot; data-origin-width=&quot;850&quot; data-origin-height=&quot;440&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Hz4yB/btsLLTNx0s7/4yc93qR9ofHkKZfK2p9iI0/tfile.dat&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Hz4yB/btsLLTNx0s7/4yc93qR9ofHkKZfK2p9iI0/tfile.dat&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Hz4yB/btsLLTNx0s7/4yc93qR9ofHkKZfK2p9iI0/tfile.dat&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHz4yB%2FbtsLLTNx0s7%2F4yc93qR9ofHkKZfK2p9iI0%2Ftfile.dat&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;711&quot; height=&quot;368&quot; data-filename=&quot;python.jpg&quot; data-origin-width=&quot;850&quot; data-origin-height=&quot;440&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Pytorch를 이용하여 코드를 구현할 때 데이터의 차원을 수정하거나 조작할 때 가장 많이 사용되는 함수는 &lt;b&gt;view, reshape, transpose, permute&lt;/b&gt;이다. 이 함수들에 대해 제대로 된 사용법과 특징을 정리할 필요가 있다 생각하여 남겨두고자 한다. 또한 &lt;b&gt;contiguous&lt;/b&gt;한 특성에 따라 어떤 함수를 사용하여야 하는지도 함께 정리하려 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1141&quot; data-origin-height=&quot;398&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eRmNvC/btsMFgBD0Xj/FURY0KuoKDUMqiGmNF1rl1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eRmNvC/btsMFgBD0Xj/FURY0KuoKDUMqiGmNF1rl1/img.png&quot; data-alt=&quot;Fig 1. 함수별 특징&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eRmNvC/btsMFgBD0Xj/FURY0KuoKDUMqiGmNF1rl1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeRmNvC%2FbtsMFgBD0Xj%2FFURY0KuoKDUMqiGmNF1rl1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;625&quot; height=&quot;218&quot; data-origin-width=&quot;1141&quot; data-origin-height=&quot;398&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Fig 1. 함수별 특징&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;view()&lt;/b&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;메모리를 공유하며 차원 변경&lt;/li&gt;
&lt;li&gt;연속된(Contiguous) 메모리를 사용할 때만 가능&lt;/li&gt;
&lt;li&gt;원본 텐서와 메모리를 공유 (즉, view()를 바꿔도 원본도 바뀜)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1741501242939&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import torch
x = torch.arange(6)  # [0, 1, 2, 3, 4, 5]
y = x.view(2, 3)  
print(y)

# 출력
# tensor([[0, 1, 2],  
#         [3, 4, 5]])&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;view()는 x의 메모리를 그대로 사용하여 2x3 행렬로 변경&lt;/li&gt;
&lt;li&gt;하지만 메모리가 연속적(contiguous)이지 않으면 view() 사용이 불가&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;reshape()&lt;/b&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;새로운&amp;nbsp;메모리&amp;nbsp;또는&amp;nbsp;기존&amp;nbsp;메모리&amp;nbsp;사용&lt;/li&gt;
&lt;li&gt;내부적으로&amp;nbsp;view()를&amp;nbsp;시도하지만,&amp;nbsp;필요하면&amp;nbsp;새로운&amp;nbsp;메모리를&amp;nbsp;할당함&lt;/li&gt;
&lt;li&gt;메모리를&amp;nbsp;공유할&amp;nbsp;수도&amp;nbsp;있고,&amp;nbsp;새로&amp;nbsp;만들&amp;nbsp;수도&amp;nbsp;있음&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1741501342734&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;x = torch.arange(6)
y = x.reshape(2, 3)
print(y)

# 출력
# tensor([[0, 1, 2],  
#         [3, 4, 5]])&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;reshape()는 view()와 비슷하지만 연속적인 메모리가 아니어도 동작 가능&lt;/li&gt;
&lt;li&gt;view()가 안 되면 자동으로 새로운 메모리를 할당해서 reshape 처리&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;transpose()&lt;/b&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;두&amp;nbsp;차원의&amp;nbsp;위치&amp;nbsp;변경&lt;/li&gt;
&lt;li&gt;메모리&amp;nbsp;구조는&amp;nbsp;유지하면서&amp;nbsp;차원만&amp;nbsp;바꿈&lt;/li&gt;
&lt;li&gt;view()처럼&amp;nbsp;메모리를&amp;nbsp;공유하지만,&amp;nbsp;연속적이지&amp;nbsp;않게&amp;nbsp;될&amp;nbsp;수&amp;nbsp;있음&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1D&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1741501550647&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;x = torch.tensor([[1, 2, 3],  
                  [4, 5, 6]])  
y = x.transpose(0, 1)  # (2x3) -&amp;gt; (3x2)
print(y)

# 출력
# tensor([[1, 4],  
#         [2, 5],  
#         [3, 6]])&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;transpose(0, 1) &amp;rarr; 0번 차원과 1번 차원을 바꿈&lt;/li&gt;
&lt;li&gt;메모리가 연속적이지 않아서 view()를 사용할 수 없을 수도 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2D&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1741501735692&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import torch

x = torch.tensor([[1, 2, 3],  
                  [4, 5, 6]])

y = x.transpose(0, 1)  # 0번(행)과 1번(열) 차원을 바꿈
print(y)

# 출력
# tensor([[1, 4],
#         [2, 5],
#         [3, 6]])&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3D&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1741501758499&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;x = torch.randn(2, 3, 4)  # (batch, height, width)

y = x.transpose(1, 2)  # height와 width를 바꿈
print(y.shape)  # (2, 4, 3)&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;x.shape = (2, 3, 4)&lt;/li&gt;
&lt;li&gt;y.shape = (2, 4, 3) &amp;rarr; 1번(height)과 2번(width) 차원이 변경됨&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;permute()&lt;/b&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;새로운&amp;nbsp;메모리&amp;nbsp;또는&amp;nbsp;기존&amp;nbsp;메모리&amp;nbsp;사용&lt;/li&gt;
&lt;li&gt;내부적으로&amp;nbsp;view()를&amp;nbsp;시도하지만,&amp;nbsp;필요하면&amp;nbsp;새로운&amp;nbsp;메모리를&amp;nbsp;할당함&lt;/li&gt;
&lt;li&gt;메모리를&amp;nbsp;공유할&amp;nbsp;수도&amp;nbsp;있고,&amp;nbsp;새로&amp;nbsp;만들&amp;nbsp;수도&amp;nbsp;있음&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1741501589427&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;x = torch.randn(2, 3, 4)  # (batch, height, width)
y = x.permute(1, 0, 2)    # (height, batch, width)
print(y.shape)

# 출력
# torch.Size([3, 2, 4])&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;permute(1, 0, 2) &amp;rarr; 1번 차원을 앞으로, 0번 차원을 뒤로, 2번 차원은 그대로 유지&lt;/li&gt;
&lt;li&gt;transpose()보다 더 유연함&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;contiguous()&lt;/b&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;메모리를 연속적으로 변경&lt;/li&gt;
&lt;li&gt;transpose()나&amp;nbsp;permute()&amp;nbsp;후&amp;nbsp;비연속적(non-contiguous)인&amp;nbsp;텐서를&amp;nbsp;연속적(contiguous)으로&amp;nbsp;변경&lt;/li&gt;
&lt;li&gt;view()를 사용하려면 contiguous()를 먼저 호출해야 할 수도 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1741501666820&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;x = torch.tensor([[1, 2, 3],  
                  [4, 5, 6]])  
y = x.transpose(0, 1)  # 비연속적인 메모리
z = y.contiguous().view(1, 6)  # 연속적인 메모리로 변환 후 view() 적용
print(z)

# 출력
# tensor([[1, 4, 2, 5, 3, 6]])&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;transpose()는&amp;nbsp;비연속적이므로&amp;nbsp;view()를&amp;nbsp;바로&amp;nbsp;사용할&amp;nbsp;수&amp;nbsp;없음&lt;/li&gt;
&lt;li&gt;contiguous()를 호출해서 메모리를 새로 정렬한 후 view() 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Languages/Python</category>
      <category>Contiguous</category>
      <category>Permute</category>
      <category>pytorch</category>
      <category>reshape</category>
      <category>transpose</category>
      <category>view</category>
      <author>AlienCoder</author>
      <guid isPermaLink="true">https://aliencoder.tistory.com/211</guid>
      <comments>https://aliencoder.tistory.com/211#entry211comment</comments>
      <pubDate>Sun, 9 Mar 2025 15:31:54 +0900</pubDate>
    </item>
    <item>
      <title>Inductive Bias(귀납적 편향)</title>
      <link>https://aliencoder.tistory.com/209</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Transformer 관련 논문을 읽어보다 표에서 Inductive Bias라는 항목을 보아 무엇인지 좀 더 상세히 알아보려 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;머신러닝 모델이 새로운 데이터를 학습할 때, 단순히 모든 가능성을 고려하는 것이 아니라 특정한 방향성을 가지고 일반화하는 경향이 있다. 이러한 경향성을 &lt;b&gt;Inductive Bias(귀납적 편향)&lt;/b&gt;이라고 한다. 즉, 모델이 학습되지 않은 데이터에 대해 합리적인 예측을 수행하기 위해 가지는 &lt;b&gt;선험적 가정(Prior Assumption)&lt;/b&gt;이다. 쉽게 말해 학습 알고리즘에서 학습자가 아직 접하지 않은 주어진 입력의 출력을 예측하기 위해 사용하는 일련의 가정이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 &lt;b&gt;Bias(편향)&lt;/b&gt;이란 어떤 것인지 먼저 살펴보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-pm-slice=&quot;1 1 []&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span&gt;Bias(편향)이란?&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;b&gt;Bias&lt;/b&gt;는 일반적으로 데이터 또는 모델이 특정 방향으로 치우치는 경향을 의미한다. 머신러닝에서의 편향은 주어진 데이터에 대한 해석 방식이 특정한 방식으로 정해지는 것을 뜻한다. &lt;/span&gt;&lt;span&gt;편향은 아래와 같이 크게 두 가지 유형으로 나뉠 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-spread=&quot;false&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span&gt;&lt;b&gt;편향(bias)&lt;/b&gt;&lt;/span&gt;&lt;span&gt;: 모델이 데이터의 특정 패턴을 따르는 방식 또는 가정을 의미한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;b&gt;편향-분산 트레이드오프(Bias-Variance Tradeoff)&lt;/b&gt;&lt;/span&gt;&lt;span&gt;: 모델이 단순할수록 편향이 커지고, 복잡할수록 분산이 커지는 경향을 나타낸다.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;Inductive Bias는 이러한 편향의 한 종류로, 모델이 데이터를 학습할 때 특정한 일반화 방식이나 규칙을 따르는 것을 의미한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Inductive Bias(귀납적 편향)의 중요성&lt;/b&gt;&lt;/h2&gt;
&lt;p data-pm-slice=&quot;1 1 []&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;머신러닝 모델은 학습 데이터에 대한 일반화를 목표로 한다. 그러나 데이터만으로는 완벽한 일반화를 보장할 수 없기 때문에 모델이 특정한 방식으로 데이터를 해석하도록 유도하는 inductive bias가 필요하다. 올바른 귀납적 편향이 적용되지 않으면 모델이 과적합(Overfitting)되거나 일반화 성능이 저하될 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-pm-slice=&quot;1 1 []&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;예를 들어, 선형 회귀 모델은 &quot;데이터는 선형 관계를 따른다&quot;라는 가정을 가지며, Convolutional Neural Network(CNN)는 &quot;이미지는 공간적으로 연관된 패턴을 가진다&quot;라는 귀납적 편향을 내재하고 있다. 이러한 편향이 없으면 모델이 학습할 수 있는 패턴이 너무 광범위해지고, 효과적인 학습이 어려워질 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-pm-slice=&quot;1 1 []&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span&gt;대표적인 Inductive Bias의 예시&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span&gt;1. &lt;/span&gt;&lt;span&gt;매끄러움(Smoothness)&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-spread=&quot;false&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;가까운 데이터 포인트들은 비슷한 출력을 가질 것이라는 가정이다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;K-최근접 이웃(K-NN) 알고리즘이나 커널 방법에서 활용된다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span&gt;2. &lt;/span&gt;&lt;span&gt;단순성(Simplicity, Occam&amp;rsquo;s Razor)&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-spread=&quot;false&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;가능한 한 단순한 가설을 선택해야 한다는 원칙이다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;선형 회귀 모델에서 과적합을 방지하기 위해 간단한 모델을 선택하는 것이 대표적인 예시이다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span&gt;3. &lt;/span&gt;&lt;span&gt;선형성(Linearity)&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-spread=&quot;false&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;데이터가 선형 관계를 가질 것이라는 가정이다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;선형 회귀, 로지스틱 회귀 등의 모델이 이에 해당한다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span&gt;4. &lt;/span&gt;&lt;span&gt;지역성(Locality)&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-spread=&quot;false&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;특정 데이터 포인트의 예측은 그 주변의 데이터에 의해 크게 영향을 받을 것이라는 가정이다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;CNN이 지역적인 특징을 추출하여 학습하는 것이 이에 해당한다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span&gt;5. &lt;/span&gt;&lt;span&gt;불변성(Invariance)&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-spread=&quot;false&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;데이터가 특정 변환(예: 회전, 이동, 크기 변화 등)에 대해 불변성을 가질 것이라는 가정이다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;CNN의 합성곱 연산이 이를 반영하고 있다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span&gt;6. &lt;/span&gt;&lt;span&gt;모듈성(Modularity)&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-spread=&quot;false&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;복잡한 시스템은 여러 개의 작은 모듈로 나눌 수 있으며, 이러한 모듈이 독립적으로 학습될 수 있다는 가정이다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;트랜스포머 모델과 같은 신경망 아키텍처가 이러한 모듈성을 활용한다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span&gt;7. &lt;/span&gt;&lt;span&gt;계층적 표현(Hierarchical Representation)&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-spread=&quot;false&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;데이터는 계층적으로 구성될 수 있으며, 낮은 수준의 특징이 결합되어 고차원적인 의미를 형성한다는 가정이다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;CNN이나 RNN이 이러한 계층적 표현을 학습하는 대표적인 예시이다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span&gt;Inductive Bias의 조절&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;Inductive Bias는 모델의 성능을 결정하는 중요한 요소이며, 적절한 수준에서 조절해야 한다. 너무 강한 편향이 적용되면 모델이 복잡한 패턴을 학습하지 못할 수 있고, 반대로 너무 약하면 과적합이 발생할 가능성이 높아진다. 따라서, 데이터와 문제의 특성을 고려하여 적절한 inductive bias를 선택하는 것이 중요하다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;이를 위해 &lt;/span&gt;&lt;span&gt;&lt;b&gt;정규화(Regularization), 데이터 증강(Data Augmentation), 전이 학습(Transfer Learning)&lt;/b&gt;&lt;/span&gt;&lt;span&gt; 등의 기법을 활용할 수 있다. 예를 들어, L1/L2 정규화는 모델이 너무 복잡해지는 것을 방지하는 역할을 하며, 데이터 증강은 불변성을 강화하여 모델의 일반화 성능을 향상시킨다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-pm-slice=&quot;1 1 []&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;Inductive Bias는 머신러닝 모델이 데이터를 학습하고 일반화하는 데 중요한 역할을 한다. 적절한 귀납적 편향을 적용하면 모델의 성능을 극대화할 수 있지만, 잘못된 편향이 적용되면 오히려 학습 성능이 저하될 수 있다. 따라서, 데이터의 특성을 파악하고 적절한 inductive bias를 선택하는 것이 머신러닝 모델 개발에서 필수적인 과정이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;데이터 양과 Inductive Bias의 관계&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터가 적으면, 강한 inductive bias가 있을 때 모델이 빠르게 학습하고 좋은 성능을 낼 가능성이 높다. 예를 들어, CNN처럼 이미지 데이터를 처리하는 데 특화된 구조는 이미지의 특정 특성을 잘 파악하는 inductive bias를 가지고 있어서, 적은 데이터로도 좋은 성능을 낼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;반면 데이터가 많으면 Inductive bias가 약한 모델이 더 유리할 수 있다. 예를 들어, Transformer는 inductive bias가 적고, 다양한 데이터를 학습하는 데 강력하지만, 그만큼 많은 데이터가 필요하다. 이는 강한 inductive bias가 오히려 일반화 능력(generalization)을 떨어뜨릴 수 있기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Bias-Variance Trade-off&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Bias-Variance&amp;nbsp;Trade-off&lt;/b&gt;는 모델이 데이터에서 어떻게 학습할지를 결정하는 두 가지 요소인 Bias(편향)와 Variance(분산)의 균형을 맞추는 문제이다. Bias는 모델이 데이터에 대해 너무 단순하게 예측하는 경향으로, 학습 데이터에 잘 맞지 않거나 과소적합(Underfitting)을 초래할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;Variance는 모델이 데이터에 대해 복잡하게 학습하는 경향으로, 학습 데이터에 너무 잘 맞춰져서 과적합(Overfitting)되는 문제를 일으킬 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면 Inductive bias가 너무 강하면 bias가 커지고, variance는 작아지지만, 과소적합(Underfitting)이 발생할 수 있다. 반대로 Inductive bias가 너무 약하면 variance가 커지고, 과적합이 발생할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;참고 자료&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://velog.io/@euisuk-chung/Inductive-Bias%EB%9E%80&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://velog.io/@euisuk-chung/Inductive-Bias%EB%9E%80&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://moon-walker.medium.com/transformer%EB%8A%94-inductive-bias%EC%9D%B4-%EB%B6%80%EC%A1%B1%ED%95%98%EB%8B%A4%EB%9D%BC%EB%8A%94-%EC%9D%98%EB%AF%B8%EB%8A%94-%EB%AC%B4%EC%97%87%EC%9D%BC%EA%B9%8C-4f6005d32558&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://moon-walker.medium.com/transformer%EB%8A%94-inductive-bias%EC%9D%B4-%EB%B6%80%EC%A1%B1%ED%95%98%EB%8B%A4%EB%9D%BC%EB%8A%94-%EC%9D%98%EB%AF%B8%EB%8A%94-%EB%AC%B4%EC%97%87%EC%9D%BC%EA%B9%8C-4f6005d32558&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>AI/ML &amp;amp; DL</category>
      <author>AlienCoder</author>
      <guid isPermaLink="true">https://aliencoder.tistory.com/209</guid>
      <comments>https://aliencoder.tistory.com/209#entry209comment</comments>
      <pubDate>Tue, 11 Feb 2025 09:12:53 +0900</pubDate>
    </item>
    <item>
      <title>[ML][Python] 서포트 벡터 머신(Support Vector Machine, SVM)</title>
      <link>https://aliencoder.tistory.com/208</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;SVM(Support&amp;nbsp;Vector&amp;nbsp;Machine)&lt;/b&gt;은 지도 학습(Supervised Learning) 알고리즘 중 하나로, 주어진 데이터가 어떤 카테고리에 속하는지를 분류하는 데 사용된다. 특히 이진 분류 문제에서 강력한 성능을 발휘하며, 고차원 공간에서도 효과적으로 작동하는 특징을 가진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SVM은 다양한 분야에서 활용되고 있다. 얼굴 인식, 손글씨 숫자 인식 등의 이미지 분류 문제에서 높은 성능을 보인다. 또한 스팸 메일 필터링, 감성 분석과 같은 자연어 처리 분야에서도 활용할 수 있다. 더 나아가 유전자 데이터 분석에도 활용되는데 질병 예측, 유전자 패턴 분석 등에서 사용 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;SVM이란&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;SVM&lt;/b&gt;은 데이터를 가장 잘 분리할 수 있는 &lt;b&gt;결정 경계(Decision Boundary)&lt;/b&gt;를 찾는 것이 핵심이다.&amp;nbsp;쉽게 말해, 결정 경계를 최대한 넓게 설정하여 마진(margin)을 최대화하는 방향으로 학습을 진행한다. 여기서 마진이란, 결정 경계와 각 클래스의 가장 가까운 데이터 포인트(서포트 벡터) 사이의 거리이다. 마진이 클수록 일반화 성능이 좋아질 가능성이 높다. 이 결정 경계는 아래 그림과 같이 가중치 벡터 w와 직교하면서 margin이 최대가 되는 선형을 찾는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;938&quot; data-origin-height=&quot;563&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/SM8Lx/btsMekjLrTj/b5Gk31edPJuCG0rXKWuF7K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/SM8Lx/btsMekjLrTj/b5Gk31edPJuCG0rXKWuF7K/img.png&quot; data-alt=&quot;Fig 1. SVM&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/SM8Lx/btsMekjLrTj/b5Gk31edPJuCG0rXKWuF7K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FSM8Lx%2FbtsMekjLrTj%2Fb5Gk31edPJuCG0rXKWuF7K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;623&quot; height=&quot;374&quot; data-origin-width=&quot;938&quot; data-origin-height=&quot;563&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Fig 1. SVM&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SVM의 주요 요소는 아래와 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;서포트 벡터(Support Vector)&lt;/b&gt;: 결정 경계와 가장 가까운 데이터 포인트로, 이 점들이 결정 경계를 형성하는 데 중요한 역할을 한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;마진(Margin)&lt;/b&gt;: 결정 경계와 서포트 벡터 사이의 거리로, 이 거리를 최대화하는 것이 SVM의 목표이다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;커널&amp;nbsp;함수(Kernel&amp;nbsp;Function)&lt;/b&gt;:&amp;nbsp;SVM은&amp;nbsp;비선형&amp;nbsp;데이터도&amp;nbsp;분류할&amp;nbsp;수&amp;nbsp;있도록&amp;nbsp;커널&amp;nbsp;함수를&amp;nbsp;사용한다.&amp;nbsp;이를&amp;nbsp;통해&amp;nbsp;저차원&amp;nbsp;데이터를&amp;nbsp;고차원&amp;nbsp;공간으로&amp;nbsp;변환하여&amp;nbsp;선형적으로&amp;nbsp;분리할&amp;nbsp;수&amp;nbsp;있도록&amp;nbsp;만든다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 Fig 1과 같이 단순해보이는 데이터 분포에선 결정 경계의 구간을 비교적 쉽게 예상할 수 있다. 하지만 현실 세계에선 아래와 같은 상황이 더욱 빈번하게 발생한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;838&quot; data-origin-height=&quot;334&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/clU6Dx/btsMcIsBguQ/vXww5L4Ustvcrs9lKmJKt1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/clU6Dx/btsMcIsBguQ/vXww5L4Ustvcrs9lKmJKt1/img.png&quot; data-alt=&quot;Fig 2. Kernel Trick&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/clU6Dx/btsMcIsBguQ/vXww5L4Ustvcrs9lKmJKt1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FclU6Dx%2FbtsMcIsBguQ%2FvXww5L4Ustvcrs9lKmJKt1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;670&quot; height=&quot;267&quot; data-origin-width=&quot;838&quot; data-origin-height=&quot;334&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Fig 2. Kernel Trick&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Fig 2의 왼쪽과 같은 데이터 분포가 나타난다면 2차원 평면에서 단순 선형으로 분류해 내긴 쉽지 않다. 따라서 저차원 공간(low dimensional space)을 고차원 공간(high dimensional space)으로 매핑해 주는 작업을 통해 분류해 낼 수 있는데 이를 &lt;b&gt;커널 트릭(Kernel Trick)&lt;/b&gt;이라고 한다. 이를 위한 커널 함수는 여러 종류가 있다. 아래에서 좀 더 살펴보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;SVM의&amp;nbsp;커널&amp;nbsp;함수&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SVM은 단순한 선형 분류기뿐만 아니라, 다양한 커널 함수를 활용하여 비선형 데이터를 분류하는 데에도 활용된다. 대표적인 커널 함수는 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;1000&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bFDWRf/btsMdF23UdL/LIvRtX5PxppaD3ZKPXHEH1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bFDWRf/btsMdF23UdL/LIvRtX5PxppaD3ZKPXHEH1/img.png&quot; data-alt=&quot;Fig 3. Kernel&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bFDWRf/btsMdF23UdL/LIvRtX5PxppaD3ZKPXHEH1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbFDWRf%2FbtsMdF23UdL%2FLIvRtX5PxppaD3ZKPXHEH1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;441&quot; height=&quot;441&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;1000&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Fig 3. Kernel&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;선형 커널(Linear Kernel)&lt;/b&gt;: 데이터가 선형적으로 분리 가능할 때 사용한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;다항식 커널(Polynomial Kernel)&lt;/b&gt;: 다항식 형태로 데이터를 변환하여 선형적으로 분리할 수 있도록 한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;RBF(Radial Basis Function) 커널&lt;/b&gt;: 가장 많이 사용되는 커널 중 하나로, 데이터가 선형적으로 분리되지 않을 때 적합하다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;시그모이드(Sigmoid)&amp;nbsp;커널&lt;/b&gt;:&amp;nbsp;신경망에서&amp;nbsp;사용하는&amp;nbsp;활성화&amp;nbsp;함수와&amp;nbsp;유사한&amp;nbsp;형태를&amp;nbsp;가지며,&amp;nbsp;특정한&amp;nbsp;경우에&amp;nbsp;유용하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 &lt;b&gt;python&lt;/b&gt;과 &lt;b&gt;sklearn&lt;/b&gt;을 사용해 구현한 소스 코드이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1739186967968&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import numpy as np
import matplotlib.pyplot as plt
from sklearn import svm, datasets

# 데이터 생성
X, y = datasets.make_moons(n_samples=100, noise=0.1, random_state=42)

# 서로 다른 커널을 사용하여 SVM 모델 생성
kernels = ['linear', 'poly', 'rbf', 'sigmoid']
models = []

for kernel in kernels:
    if kernel == 'poly':
        clf = svm.SVC(kernel=kernel, degree=3)
    else:
        clf = svm.SVC(kernel=kernel)
    clf.fit(X, y)
    models.append((kernel, clf))

# 시각화
fig, axes = plt.subplots(2, 2, figsize=(10, 10))
axes = axes.ravel()

for idx, (kernel, model) in enumerate(models):
    ax = axes[idx]
    ax.set_title(f&quot;Kernel: {kernel}&quot;)

    # 결정 경계 그리기
    x_min, x_max = X[:, 0].min() - 0.5, X[:, 0].max() + 0.5
    y_min, y_max = X[:, 1].min() - 0.5, X[:, 1].max() + 0.5
    xx, yy = np.meshgrid(np.linspace(x_min, x_max, 100), np.linspace(y_min, y_max, 100))

    Z = model.predict(np.c_[xx.ravel(), yy.ravel()])
    Z = Z.reshape(xx.shape)

    ax.contourf(xx, yy, Z, alpha=0.3)
    ax.scatter(X[:, 0], X[:, 1], c=y, edgecolors='k')

plt.tight_layout()
plt.show()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SVM은 강력한 분류 성능을 자랑하는 알고리즘으로, 특히 이진 분류 문제에서 좋은 성능을 보인다. 또한 커널 트릭을 활용하여 비선형 데이터도 효과적으로 처리할 수 있다. 그러나 데이터 크기가 커질수록 연산량이 많아지는 단점이 있어, 적절한 데이터 전처리와 하이퍼파라미터 튜닝이 필요하다. 과적합(Overfitting) 문제도 물론 조심해야 하는 부분 중 하나이다. 머신러닝의 지도학습의 주요 방법 중 하나인 SVM의 개념과 원리를 이해하고, 실제 데이터에 적용해 보는 것이 중요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;참고 자료&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.linkedin.com/pulse/role-svm-model-current-data-science-deepak-kumar/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.linkedin.com/pulse/role-svm-model-current-data-science-deepak-kumar/&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.codeover.in/blog/introduction-to-svm-machine-learning&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.codeover.in/blog/introduction-to-svm-machine-learning&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.reneshbedre.com/blog/support-vector-machine.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.reneshbedre.com/blog/support-vector-machine.html&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://sanghyu.tistory.com/7&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://sanghyu.tistory.com/7&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://bkshin.tistory.com/entry/%EB%A8%B8%EC%8B%A0%EB%9F%AC%EB%8B%9D-2%EC%84%9C%ED%8F%AC%ED%8A%B8-%EB%B2%A1%ED%84%B0-%EB%A8%B8%EC%8B%A0-SVM&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://bkshin.tistory.com/entry/%EB%A8%B8%EC%8B%A0%EB%9F%AC%EB%8B%9D-2%EC%84%9C%ED%8F%AC%ED%8A%B8-%EB%B2%A1%ED%84%B0-%EB%A8%B8%EC%8B%A0-SVM&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>AI/ML &amp;amp; DL</category>
      <category>Decision Boundary</category>
      <category>kernel</category>
      <category>Support Vector Machine</category>
      <category>SVM</category>
      <category>결정 경계</category>
      <author>AlienCoder</author>
      <guid isPermaLink="true">https://aliencoder.tistory.com/208</guid>
      <comments>https://aliencoder.tistory.com/208#entry208comment</comments>
      <pubDate>Mon, 10 Feb 2025 20:30:45 +0900</pubDate>
    </item>
    <item>
      <title>[Python] 백준 27961 - 고양이는 많을수록 좋다</title>
      <link>https://aliencoder.tistory.com/207</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;문제&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/27961&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.acmicpc.net/problem/27961&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;해설&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2배수로 늘려가며 탐색해 보면 쉽게 해결된다. 0이 될 수 있는 조건은 주의해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Python&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1737017190904&quot; class=&quot;python&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;from sys import stdin


def magic(n: int):
    if n == 0:
        return 0
    
    cnt = 1
    cat = 1

    while cat &amp;lt; n:
        cnt += 1
        cat *= 2

    return cnt


cat_num = int(stdin.readline())
print(magic(cat_num))&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Study/Coding Test</category>
      <author>AlienCoder</author>
      <guid isPermaLink="true">https://aliencoder.tistory.com/207</guid>
      <comments>https://aliencoder.tistory.com/207#entry207comment</comments>
      <pubDate>Mon, 10 Feb 2025 19:28:57 +0900</pubDate>
    </item>
    <item>
      <title>공분산(Covariance)과 상관 계수(Correlation Coefficient)</title>
      <link>https://aliencoder.tistory.com/206</link>
      <description>&lt;script src=&quot;https://polyfill.io/v3/polyfill.min.js?features=es6&quot; data-mce-fragment=&quot;1&quot;&gt;&lt;/script&gt;
&lt;script src=&quot;https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js&quot; data-mce-fragment=&quot;1&quot;&gt;&lt;/script&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;공분산(Covariance)&lt;/b&gt;과 &lt;b&gt;상관 계수(Correlation Coefficient)&lt;/b&gt;는 통계학, 데이터 분석, 머신러닝, 금융, 경제학 등에서 널리 사용되고 있다. 대표적으로 데이터 분석 분야에선 변수 간의 관계를 분석하여 데이터의 패턴을 파악하는 데 활용된다. 뿐만 아니라 피처 선택(Feature Selection)에서 상관성이 높은 변수 중 일부를 제거하여 &lt;b&gt;다중공선성 문제&lt;/b&gt;를 방지할 때 사용된다. 현재 관심 분야인 신호 처리 및 공학 분야에선 센서 데이터 분석에서 여러 신호 간의 관계를 파악하거나 이미지 및 음성 처리에서도 변수 간의 연관성을 분석할 때 활용된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;공분산(Covariance)&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공분산에 대해 설명하기 전 &lt;b&gt;분산(Variance)&lt;/b&gt;에 대해 간단히 짚고 넘어가야 한다. 분산이란 내가 가진 데이터가 평균값을 중심으로 퍼져 있는 평균적인 거리를 표현해 줄 수 있다. 분산의 수식은 아래와 같다.&lt;br /&gt;$$ \text{Var(x)} = \ \sigma^2_X = \frac{1}{n-1} \sum_{i=1}^{n} (x_i - \bar{x})^2 $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공분산은 두 변수의 평균값을 중심으로 퍼져 있는 평균적인 거리를 의미한다. 수식은 아래와 같다.&lt;br /&gt;$$ \text{Cov(x, y)} = \ \sigma_{XY} = \frac{1}{n-1} \sum_{i=1}^{n} (x_i - \bar{x})(y_i - \bar{y}) $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;분모를 여기선 \( n-1 \)으로 설정하였는데 보통 표본 분산과 표본 공분산을 계산하는 경우엔 \( n-1 \)로 분모를 두기 때문이다. 전체 모집단의 분산과 공분산에 대해 도출할 때는 분모를 \( n \)으로 둔다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;분산과 공분산 수식을 놓고 보면 분자 부분이 \( (x_i - \bar{x})^2 \)와 \( (x_i - \bar{x})(y_i - \bar{y}) \)라는 차이만 있다. 좌표 평면으로 옮겨놓고 보면 아래와 같을 것이다. 개인적으로 가장 직관적으로 이해할 수 있는 그림이라고 생각한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1482&quot; data-origin-height=&quot;509&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/4ove6/btsMceRHhRP/nlH6HttbeeYqWxr7yrI6Wk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/4ove6/btsMceRHhRP/nlH6HttbeeYqWxr7yrI6Wk/img.png&quot; data-alt=&quot;Fig 1. 분산과 공분산&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/4ove6/btsMceRHhRP/nlH6HttbeeYqWxr7yrI6Wk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F4ove6%2FbtsMceRHhRP%2FnlH6HttbeeYqWxr7yrI6Wk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;729&quot; height=&quot;250&quot; data-origin-width=&quot;1482&quot; data-origin-height=&quot;509&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Fig 1. 분산과 공분산&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 분산은 한 가지 변수의 데이터가 평균에서 얼마나 퍼져있는가에 대한 수치이고, 공분산은 2개 이상 다중 변수의 평균값을 중심으로 얼마나 퍼져있는가에 대한 수치이다.&amp;nbsp; 참고로 2개 이상의 변수는 공변량이라 부른다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 공분산은 두 변수 간의 관계를 측정하는 값인데, 만약 두 변수의 변화가 서로 독립적인 경우 공분산은 0이 된다. 예를 들어, 학생들의 수학 점수와 음악 감상 시간을 공분산으로 표현해 보자. 이 두 변수는 일반적으로 큰 상관관계가 없다고 가정할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;학생&amp;nbsp;5명의&amp;nbsp;수학&amp;nbsp;점수와&amp;nbsp;하루&amp;nbsp;평균&amp;nbsp;음악&amp;nbsp;감상&amp;nbsp;시간을&amp;nbsp;다음과&amp;nbsp;같이&amp;nbsp;가정한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;X(수학&amp;nbsp;점수):&amp;nbsp;[85,&amp;nbsp;90,&amp;nbsp;75,&amp;nbsp;80,&amp;nbsp;95]&lt;/li&gt;
&lt;li&gt;Y(음악&amp;nbsp;감상&amp;nbsp;시간,&amp;nbsp;단위:&amp;nbsp;시간):&amp;nbsp;[2,&amp;nbsp;1,&amp;nbsp;3,&amp;nbsp;4,&amp;nbsp;2]&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공분산 수식에 대입하여 계산하면 아래와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ \bar{X} = \frac{85 + 90 + 75 + 80 + 95}{5} = 85 $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ \bar{Y} = \frac{2 + 1 + 3 + 4 + 2}{5} = 2.4 $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ \text{X 편차 계산: } X - \bar{X} = [0, 5, -10, -5, 10] $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ \text{Y 편차 계산: } Y - \bar{Y} = [-0.4, -1.4, 0.6, 1.6, -0.4] $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ \text{곱&amp;nbsp;계산:}&amp;nbsp;\quad (X - \bar{X}) \times (Y - \bar{Y}) $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ = [0 \times -0.4, 5 \times -1.4, -10 \times 0.6, -5 \times 1.6, 10 \times -0.4] $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ = [0, -7, -6, -8, -4] $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ \quad \text{Cov}(X, Y) = \frac{1}{5} \sum [0, -7, -6, -8, -4] = \frac{-25}{5} = -5 $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 공분산 값이 -5로 작고, 음수임을 확인할 수 있다. 이는 약간의 역상관이 있을 가능성을 보여주지만, 이 값이 크지 않으므로 \( X \)와 \( Y \) 사이에는 실질적인 상관관계가 거의 없다고 결론 내릴 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 공분산이 어느 정도 되어야 상관관계가 있다고 할 수 있는 걸까? 사실 공분산 자체는 상관관계를 판단하기 위한 절대적인 기준으로 사용되기 어렵다. 공분산은 각 변수들 간의 관계가 양의 상관관계인지 음의 상관관계인지 정도만 반영하고 있으며 얼마나 크게 상관이 있는지는 알 수 없기 때문이다. 다시 말해 공분산 값이 변수들의 스케일(단위)에 따라 달라지기 때문이다. 따라서 두 변수 간 관계를 판단하기 위해서는 &lt;b&gt;상관계수(Correlation Coefficient)&lt;/b&gt;를 사용하는 것이 더 적절하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;상관 계수(Correlation Coefficient)&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;공분산(\(Cov(X, Y)\))&lt;/b&gt;은 두 변수의 관계의 방향성과 크기를 나타내는 수치였다. 단위가 변수의 단위에 따라 달라지므로, 값을 비교하기 어렵다. 반면 &lt;b&gt;상관 계수(\(r\))&lt;/b&gt;는 공분산을 표준화한 값으로, 두 변수 간의 상관관계를 -1에서 1 사이의 값으로 표현한다. 상관계수는 단위와 무관하며, 직접적으로 상관성을 판단할 수 있다. 상관 계수의 수식은 아래와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ r = \frac{\text{Cov}(X, Y)}{\sigma_X \cdot \sigma_Y} $$&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;\( \sigma_X\) : \(X\)의 표준편차&lt;/li&gt;
&lt;li&gt;\( \sigma_Y\) : \(Y\)의 표준편차&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상관계수는 다음과 같이 해석할 수 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;\( r = 1 \): 완벽한 양의 상관관계. \( X\)가 증가하면 \( Y \)도 항상 일정 비율로 증가)&lt;/li&gt;
&lt;li&gt;\( r = -1 \): 완벽한 음의 상관관계. \( X \)가 증가하면 \( Y \)가 항상 일정 비율로 감소)&lt;/li&gt;
&lt;li&gt;\( r = 0 \): 두 변수 간에 상관관계가 없음&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;\( \left| r \right| \)의 크기에 따른 해석:&lt;br /&gt;\begin{cases} &lt;br /&gt;0.0&amp;nbsp;\leq&amp;nbsp;\left|&amp;nbsp;r&amp;nbsp;\right|&amp;nbsp;&amp;lt;&amp;nbsp;0.3&amp;nbsp;&amp;amp;&amp;nbsp;:&amp;nbsp;\text{거의&amp;nbsp;상관관계가&amp;nbsp;없음}&amp;nbsp;\\ &lt;br /&gt;0.3&amp;nbsp;\leq&amp;nbsp;\left|&amp;nbsp;r&amp;nbsp;\right|&amp;nbsp;&amp;lt;&amp;nbsp;0.7&amp;nbsp;&amp;amp;&amp;nbsp;:&amp;nbsp;\text{약한&amp;nbsp;상관관계}&amp;nbsp;\\ &lt;br /&gt;0.7&amp;nbsp;\leq&amp;nbsp;\left|&amp;nbsp;r&amp;nbsp;\right|&amp;nbsp;&amp;lt;&amp;nbsp;1.0&amp;nbsp;&amp;amp;&amp;nbsp;:&amp;nbsp;\text{강한&amp;nbsp;상관관계} &lt;br /&gt;\end{cases}&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 예제인 수학 점수와 음악 감상 시간을 사용해 상관계수를 계산해 보자. 위 공분산에서 도출된 값인 \( Cov(X, Y) = -5 \)를 가져와 아래 표준 편차를 도출하는데 사용하면 된다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;X(수학&amp;nbsp;점수):&amp;nbsp;[85,&amp;nbsp;90,&amp;nbsp;75,&amp;nbsp;80,&amp;nbsp;95]&lt;/li&gt;
&lt;li&gt;Y(음악&amp;nbsp;감상&amp;nbsp;시간,&amp;nbsp;단위:&amp;nbsp;시간):&amp;nbsp;[2,&amp;nbsp;1,&amp;nbsp;3,&amp;nbsp;4,&amp;nbsp;2]&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ \text{X 편차 계산: } X - \bar{X} = [0, 5, -10, -5, 10] $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ \text{Y 편차 계산: } Y - \bar{Y} = [-0.4, -1.4, 0.6, 1.6, -0.4] $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ \sigma_X = \sqrt{\frac{1}{n} \sum (X - \bar{X})^2} = \sqrt{\frac{1}{5} (0^2 + 5^2 + (-10)^2 + (-5)^2 + 10^2)} \approx 7.07 $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ \sigma_Y = \sqrt{\frac{1}{n} \sum (Y - \bar{Y})^2} = \sqrt{\frac{1}{5} ((-0.4)^2 + (-1.4)^2 + 0.6^2 + 1.6^2 + (-0.4)^2)} \approx 1.02 $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ r = \frac{-5}{7.07 \times 1.02} \approx -0.69 $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 \( r \approx -0.69 \)로 두 변수 간에 약간 강한 음의 상관관계가 있음을 의미한다. 이는 \( X \)가 증가할수록 \( Y \)가 감소하는 경향이 있음을 나타낸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공분산만 보고 아까는 연관이 크지 않을거라고 판단하였는데 상관계수로 살펴보니 비교적 큰 연관관계가 있다고 나오는 것 아닌가라는 의문이 들 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 공분산과 상관계수의 스케일과 해석 방식의 차이 때문이다. 수학 점수와 음악 감상 시간은 수치적으로 큰 차이가 나고 있는 데이터이다. 앞서 언급한 것처럼 공분산 값은 변수의 단위와 크기에 따라 달라진다. 두 변수의 관계가 얼마나 강한지 알 수 있지만, 값의 절대적인 크기를 해석하기 어렵다. 상관 계수는 공분산을 두 변수의 표준편차로 나누어 단위의 영향을 제거하였고 \( r \)은 -1~1 사이로 표준화되어 두 변수 간 관계를 명확히 해석할 수 있게 해 준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결론적으로 공분산에서 도출된 대로 두 변수 간의 방향성(-5: 음의 방향)에 대해서 판단이 가능하였고, 상관 계수를 도출하여 얼마나 크게 두 변수가 상관관계의 강도(강도: -0.69)를 가지고 있는지 판단할 수 있었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공부할 시간에 노래 들으면 집중이 안되거나 딴짓을 할 수 있으니 이치에 맞는 것 같기도 하다. 저 두 데이터가 어떠한 이유로 강한 음의 상관관계를 띄게 하였는지 알아내는 것도 연구자의 몫이기도 하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;관련 포스팅&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://aliencoder.tistory.com/116&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;2024.04.10 - [Data Science/Data&amp;nbsp;Analysis] - 상관관계(correlation)와 인과관계(causation)&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://aliencoder.tistory.com/13&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;2021.12.27 - [Data Science/Data&amp;nbsp;Analysis] - [Python] 상관 분석(Correlation Analysis)과 상관 계수(Correlation Coefficient), 자기 상관 계수(Autocorrelation Coefficient)&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;참고 자료&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://david-kim2028.tistory.com/entry/%EC%83%81%EA%B4%80%EA%B4%80%EA%B3%84%EC%99%80-%EA%B3%B5%EB%B6%84%EC%82%B0%EC%97%90-%EB%8C%80%ED%95%B4-%EC%95%8C%EC%95%84%EB%B3%B4%EC%9E%90&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://david-kim2028.tistory.com/entry/%EC%83%81%EA%B4%80%EA%B4%80%EA%B3%84%EC%99%80-%EA%B3%B5%EB%B6%84%EC%82%B0%EC%97%90-%EB%8C%80%ED%95%B4-%EC%95%8C%EC%95%84%EB%B3%B4%EC%9E%90&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>AI/Data Analysis</category>
      <category>correlation coefficient</category>
      <category>covariance</category>
      <category>Variance</category>
      <category>공분산</category>
      <category>분산</category>
      <category>상관 계수</category>
      <category>상관 관계</category>
      <author>AlienCoder</author>
      <guid isPermaLink="true">https://aliencoder.tistory.com/206</guid>
      <comments>https://aliencoder.tistory.com/206#entry206comment</comments>
      <pubDate>Sat, 8 Feb 2025 17:02:58 +0900</pubDate>
    </item>
    <item>
      <title>[Python/Java] 백준 15686 - 치킨 배달 [Brute Force]</title>
      <link>https://aliencoder.tistory.com/205</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;문제&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/15686&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.acmicpc.net/problem/15686&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;해설&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 치킨집의 조합을 만들어서 Brute Force를 이용하여 해결하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;백준이 지금 시간 기준으로 이상한건지는 모르겠지만 Python과 Java 코드 모두 제출하면 성공일 때도 있고 컴파일 에러나 런타임 에러(NZEC)가 발생할 때도 있다. 정답이었던 코드를 그대로 제출해도 이런 결과가 나와서 원인이 뭔지 찾지 못하였다. 혹시 비슷한 현상이 일어나거나 업로드한 코드에서 문제가 있는 부분을 알고 계신다면 댓글 부탁드립니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Python&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1737017190904&quot; class=&quot;python&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;from sys import stdin


def get_distance(combination: list):
    total_distance = 0

    for hx, hy in houses:
        min_distance = float('inf')
        for cx, cy in combination:
            min_distance = min(min_distance, abs(hx - cx) + abs(hy - cy))
        total_distance += min_distance
    return total_distance


def generate_combinations(arr, m, start, selected):
    if len(selected) == m:
        all_combinations.append(selected[:])  # 깊은 복사해서 저장
        return

    for i in range(start, len(arr)):
        selected.append(arr[i])
        generate_combinations(arr, m, i + 1, selected)
        selected.pop()  # 백트래킹 (원래 상태로 되돌리기)


n, m = map(int, stdin.readline().split(&quot; &quot;))
city = [list(map(int, stdin.readline().strip().split(&quot; &quot;))) for _ in range(n)]

houses = []
chickens = []
for r in range(n):
    for c in range(n):
        if city[r][c] == 1:
            houses.append((r, c))
        elif city[r][c] == 2:
            chickens.append((r, c))

all_combinations = []
generate_combinations(chickens, m, 0, [])

answer = float('inf')
for selected in all_combinations:
    answer = min(answer, get_distance(selected))
print(answer)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Java&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1737017205334&quot; class=&quot;java&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.io.*;
import java.util.*;

public class Main {
    static int n, m;
    static List&amp;lt;int[]&amp;gt; houses = new ArrayList&amp;lt;&amp;gt;();
    static List&amp;lt;int[]&amp;gt; chickens = new ArrayList&amp;lt;&amp;gt;();
    static List&amp;lt;List&amp;lt;int[]&amp;gt;&amp;gt; allCombinations = new ArrayList&amp;lt;&amp;gt;();
    static int minCityDistance = Integer.MAX_VALUE;

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

        String[] condition = br.readLine().split(&quot; &quot;);
        n = Integer.parseInt(condition[0]);
        m = Integer.parseInt(condition[1]);

        for(int r = 0; r &amp;lt; n; r++) {
            String[] line = br.readLine().split(&quot; &quot;);
            for(int c = 0; c &amp;lt; n; c++) {
                int value = Integer.parseInt(line[c]);
                if (value == 1) houses.add(new int[]{r, c});
                else if (value == 2) chickens.add(new int[]{r, c});
            }
        }

        generateCombinations(0, new ArrayList&amp;lt;&amp;gt;());
        for(List&amp;lt;int[]&amp;gt; selected : allCombinations) {
            minCityDistance = Math.min(minCityDistance, getDistance(selected));
        }

        System.out.println(minCityDistance);
    }

    // 백트래킹을 이용한 조합 생성
    static void generateCombinations(int start, List&amp;lt;int[]&amp;gt; selected) {
        if(selected.size() == m) {
            allCombinations.add(new ArrayList&amp;lt;&amp;gt;(selected));
            return;
        }

        for(int i = start; i &amp;lt; chickens.size(); i++) {
            selected.add(chickens.get(i));
            generateCombinations(i + 1, selected);
            selected.remove(selected.size() - 1);  // 백트래킹
        }
    }

    static int getDistance(List&amp;lt;int[]&amp;gt; combination) {
        int totalDistance = 0;

        for(int[] house : houses) {
            int hx = house[0], hy = house[1];
            int minDistance = Integer.MAX_VALUE;

            for(int[] chicken : combination) {
                int cx = chicken[0], cy = chicken[1];
                minDistance = Math.min(minDistance, Math.abs(hx - cx) + Math.abs(hy - cy));
            }

            totalDistance += minDistance;
        }

        return totalDistance;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Study/Coding Test</category>
      <author>AlienCoder</author>
      <guid isPermaLink="true">https://aliencoder.tistory.com/205</guid>
      <comments>https://aliencoder.tistory.com/205#entry205comment</comments>
      <pubDate>Sat, 8 Feb 2025 15:19:12 +0900</pubDate>
    </item>
    <item>
      <title>[Python/Java] 백준 2615 - 오목 [Brute Force]</title>
      <link>https://aliencoder.tistory.com/204</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;문제&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/2615&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.acmicpc.net/problem/2615&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;해설&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반복문을 이용한 완전 탐색 방법으로 해결할 수 있었다. DFS로도 접근해보았는데 잘 해결되지 않아 방법을 바꾸어 다시 접근하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;6목이 되는 케이스에 대한 검사가 필요했고, directions 부분에 왔던 방향을 되돌아가지 않도록 설정하는 아이디어도 주요했던 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Python&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1737017190904&quot; class=&quot;python&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;from sys import stdin


# 방향: 오른쪽, 아래, 대각선(↘), 대각선(↗)
directions = [(0, 1), (1, 0), (1, 1), (-1, 1)]


def check_winner(row: int, col: int, direction: tuple):
    stone = board[row][col]
    depth = 1

    x, y = row, col
    for _ in range(4):
        x += direction[0]
        y += direction[1]
        if 0 &amp;lt;= x &amp;lt; 19 and 0 &amp;lt;= y &amp;lt; 19 and board[x][y] == stone:
            depth += 1
        else:
            break

    # 정방향(positive direction) 확인
    px, py = x + direction[0], y + direction[1]
    if 0 &amp;lt;= px &amp;lt; 19 and 0 &amp;lt;= py &amp;lt; 19:
        if board[px][py] == stone:
            return False

    # 역방향(negative direction) 확인
    nx, ny = row - direction[0], col - direction[1]
    if 0 &amp;lt;= nx &amp;lt; 19 and 0 &amp;lt;= ny &amp;lt; 19:
        if board[nx][ny] == stone:
            return False

    if depth == 5:
        return True
    return False


board = [list(map(int, stdin.readline().strip().split(&quot; &quot;))) for _ in range(19)]

win = False
for i in range(19):
    for j in range(19):
        if board[i][j] != 0:
            for direction in directions:
                win = check_winner(i, j, direction)
                if win:
                    print(board[i][j])
                    print(i+1, j+1)
                    exit(0)
if not win:
    print(0)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Java&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1737017205334&quot; class=&quot;java&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.io.*;
import java.util.*;

public class Main {
    static int[][] board = new int[19][19];
    static int[][] directions = {{0, 1}, {1, 0}, {1, 1}, {-1, 1}}; // &amp;rarr;, &amp;darr;, ↘, ↗

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        
        for(int i = 0; i &amp;lt; 19; i++) {
            StringTokenizer st = new StringTokenizer(br.readLine());
            for(int j = 0; j &amp;lt; 19; j++) {
                board[i][j] = Integer.parseInt(st.nextToken());
            }
        }

        for(int i = 0; i &amp;lt; 19; i++) {
            for(int j = 0; j &amp;lt; 19; j++) {
                if(board[i][j] != 0) {
                    for(int[] direction : directions) {
                        if(checkWinner(i, j, direction)) {
                            System.out.println(board[i][j]);
                            System.out.println((i + 1) + &quot; &quot; + (j + 1));
                            return;
                        }
                    }
                }
            }
        }
        
        System.out.println(0);
    }

    public static boolean checkWinner(int row, int col, int[] direction) {
        int stone = board[row][col];
        int count = 1;

        int x = row, y = col;
        for(int i = 0; i &amp;lt; 4; i++) { // 4칸 더 확인
            x += direction[0];
            y += direction[1];
            if (x &amp;gt;= 0 &amp;amp;&amp;amp; x &amp;lt; 19 &amp;amp;&amp;amp; y &amp;gt;= 0 &amp;amp;&amp;amp; y &amp;lt; 19 &amp;amp;&amp;amp; board[x][y] == stone) {
                count++;
            } else {
                break;
            }
        }

        // 정방향(positive direction) 확인
        int px = x + direction[0], py = y + direction[1];
        if(px &amp;gt;= 0 &amp;amp;&amp;amp; px &amp;lt; 19 &amp;amp;&amp;amp; py &amp;gt;= 0 &amp;amp;&amp;amp; py &amp;lt; 19 &amp;amp;&amp;amp; board[px][py] == stone) {
            return false;
        }

        // 역방향(negative direction) 확인
        int nx = row - direction[0], ny = col - direction[1];
        if(nx &amp;gt;= 0 &amp;amp;&amp;amp; nx &amp;lt; 19 &amp;amp;&amp;amp; ny &amp;gt;= 0 &amp;amp;&amp;amp; ny &amp;lt; 19 &amp;amp;&amp;amp; board[nx][ny] == stone) {
            return false;
        }

        return count == 5;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Study/Coding Test</category>
      <author>AlienCoder</author>
      <guid isPermaLink="true">https://aliencoder.tistory.com/204</guid>
      <comments>https://aliencoder.tistory.com/204#entry204comment</comments>
      <pubDate>Fri, 7 Feb 2025 14:20:27 +0900</pubDate>
    </item>
    <item>
      <title>[Python/Java] 백준 2529 - 부등호 [DFS]</title>
      <link>https://aliencoder.tistory.com/203</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;문제&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/2529&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.acmicpc.net/problem/2529&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;해설&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Brute Force로 접근하였는데 생각보다 풀어내기 쉽지 않았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 생각을 바꿔 DFS를 이용하였다. 뿐만 아니라 백트래킹(Backtracking)에 대한 이해도 필요하다. 탐색 도중 더 깊은 depth에 대한 탐색이 필요 없을 때 pruning이라 불리는 가지치기를 이용하여 유망한(promising) 경로만 계속 진행한다. 이 과정은 &quot;선택 - 탐색 - 복구&quot; 구조로 구성되어 있고, 이를 통해 가능한 모든 경우의 수를 탐색할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 Backtracking은 마치 임계구역 문제에서 한 번에 한 경로만 특정 숫자를 사용할 수 있도록 보호하는 역할과 유사한데, 공유 메모리 문제에서 고려되는 Critical Section에 대한 Lock을 수행하는 아이디어가 떠오르지 않아 개인적으론 어려웠던 문제였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 문제에선 Python이나 Java 모두 Single Thread 상태이므로 공유 변수에 대한 통제와 스케줄러 시퀀스는 고려하지 않아도 되지만 실무에선 어느 정도 통제가 필요할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Python&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1737017190904&quot; class=&quot;java&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;from sys import stdin


def is_valid(prev, current, sign):
    if sign == '&amp;lt;':
        return prev &amp;lt; current
    else:
        return prev &amp;gt; current


def dfs(depth: int, answer: list):
    if depth == n + 1:
        num_str = ''.join(map(str, answer))
        result.append(num_str)
        return

    for current in range(10):
        if not visited[current]:
            if depth == 0 or is_valid(answer[-1], current, signs[depth - 1]):
                visited[current] = True
                dfs(depth + 1, answer + [current])
                visited[current] = False	# Backtracking


n = int(stdin.readline())
signs = stdin.readline().strip().split(&quot; &quot;)
visited = [False for _ in range(10)]
result = []
dfs(0, [])
print(max(result))
print(min(result))&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Java&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1737017205334&quot; class=&quot;java&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.io.*;
import java.util.*;

public class Main {
    static int k;
    static String[] signs;
    static boolean[] visited = new boolean[10];
    static List&amp;lt;String&amp;gt; result = new ArrayList&amp;lt;&amp;gt;();

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

        k = Integer.parseInt(br.readLine().trim());
        signs = br.readLine().split(&quot; &quot;);

        dfs(0, new ArrayList&amp;lt;&amp;gt;());

        Collections.sort(result);
        System.out.println(result.get(result.size() - 1)); 
        System.out.println(result.get(0)); 
    }

    public static void dfs(int depth, List&amp;lt;Integer&amp;gt; answer) {
        if(depth == (k + 1)) {
            StringBuilder numStr = new StringBuilder();
            for(int num : answer) {
                numStr.append(num);
            }
            result.add(numStr.toString());
            return;
        }

        for(int current=0; current&amp;lt;10; current++) {
            if(!visited[i]) {
                if(depth == 0 || isValid(answer.get(answer.size() - 1), current, signs[depth - 1])) {
                    visited[i] = true;
                    answer.add(i);
                    dfs(depth + 1, answer);
                    answer.remove(answer.size() - 1);
                    visited[i] = false;
                }
            }
        }
    }

    public static boolean isValid(int prev, int current, String sign) {
        if (sign.equals(&quot;&amp;lt;&quot;)) {
            return prev &amp;lt; current;
        } else {
            return prev &amp;gt; current;
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Study/Coding Test</category>
      <author>AlienCoder</author>
      <guid isPermaLink="true">https://aliencoder.tistory.com/203</guid>
      <comments>https://aliencoder.tistory.com/203#entry203comment</comments>
      <pubDate>Wed, 5 Feb 2025 17:15:18 +0900</pubDate>
    </item>
    <item>
      <title>[Python/Java] 백준 1051 - 숫자 정사각형 [Brute Force]</title>
      <link>https://aliencoder.tistory.com/202</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;문제&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/1051&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.acmicpc.net/problem/1051&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;해설&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Brute Force를 이용하여 모든 경우의 수를 탐색하여 해결하였다. CNN에서의 Sliding Window를 연상해보면 더 쉽게 해결할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Python&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1737017190904&quot; class=&quot;java&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;from sys import stdin


def brute_force(matrix: list, n: int, m: int):
    max_size = n if n &amp;lt; m else m

    result = []
    for i in range(n):
        for j in range(m):
            for k in range(max_size):
                if i + k &amp;lt; n and j + k &amp;lt; m:
                    if matrix[i][j] == matrix[i + k][j] == matrix[i][j + k] == matrix[i + k][j + k]:
                        result.append((k + 1) ** 2)
    print(max(result))


n, m = map(int, stdin.readline().split(&quot; &quot;))
matrix = [list(stdin.readline().strip()) for _ in range(n)]
brute_force(matrix, n, m)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Java&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1737017205334&quot; class=&quot;java&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.io.*;
import java.util.*;

public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringBuilder sb = new StringBuilder();

        String[] condition = br.readLine().split(&quot; &quot;);
        int n = Integer.parseInt(condition[0]);
        int m = Integer.parseInt(condition[1]);

        int[][] matrix = new int[n][m];

        for(int i = 0; i &amp;lt; n; i++) {
            String[] rows = br.readLine().split(&quot;&quot;);
            for(int j = 0; j &amp;lt; rows.length; j++) {
                matrix[i][j] = Integer.parseInt(rows[j]);
            }
        }

        sb.append(bruteForce(matrix, n, m));
        System.out.println(sb);
    }

    public static int bruteForce(int[][] matrix, int n, int m) {
        List&amp;lt;Integer&amp;gt; result = new ArrayList&amp;lt;&amp;gt;();

        int maxSize;
        if(n&amp;lt;m){ maxSize = n; }
        else{ maxSize = m; }

        for(int i=0; i&amp;lt;n; i++){
            for(int j=0; j&amp;lt;m; j++){
                for(int k=0; k&amp;lt;maxSize; k++){
                    if((i+k &amp;lt; n) &amp;amp;&amp;amp; (j+k &amp;lt; m)){
                        if((matrix[i][j] == matrix[i+k][j])
                                &amp;amp;&amp;amp; (matrix[i+k][j]) == (matrix[i][j+k])
                                &amp;amp;&amp;amp; (matrix[i][j+k] == matrix[i+k][j+k])){
                            result.add((k+1)*(k+1));
                        }
                    }
                }
            }            
        }

        return Collections.max(result);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Study/Coding Test</category>
      <author>AlienCoder</author>
      <guid isPermaLink="true">https://aliencoder.tistory.com/202</guid>
      <comments>https://aliencoder.tistory.com/202#entry202comment</comments>
      <pubDate>Wed, 5 Feb 2025 11:18:06 +0900</pubDate>
    </item>
    <item>
      <title>[Python] 완전 탐색(Brute Force)</title>
      <link>https://aliencoder.tistory.com/201</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;
&lt;script src=&quot;https://polyfill.io/v3/polyfill.min.js?features=es6&quot;&gt;&lt;/script&gt;
&lt;script src=&quot;https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js&quot;&gt;&lt;/script&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;핵심 개념&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;완전&amp;nbsp;탐색(Brute&amp;nbsp;Force)&lt;/b&gt;은&amp;nbsp;가능한&amp;nbsp;모든&amp;nbsp;경우의&amp;nbsp;수를&amp;nbsp;전부&amp;nbsp;탐색하여&amp;nbsp;정답을&amp;nbsp;찾는&amp;nbsp;방법이다.&amp;nbsp;최적화&amp;nbsp;없이&amp;nbsp;모든&amp;nbsp;경우를&amp;nbsp;시도하기&amp;nbsp;때문에&amp;nbsp;구현이&amp;nbsp;간단하지만,&amp;nbsp;경우의&amp;nbsp;수가&amp;nbsp;많아지면&amp;nbsp;비효율적일&amp;nbsp;수&amp;nbsp;있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt; 시간 복잡도&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;완전 탐색의 시간 복잡도는 일반적으로 \( O(N!) \) 또는 \( O(2^N) \) 등 매우 높은 편이다. 예를 들어, n개의 요소가 있을 때 가능한 모든 순열을 탐색하는 경우 \( O(N!) \)이 된다. 따라서 입력 크기가 커질수록 실행 시간이 급격히 증가한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;동작방식 &amp;amp; 구현&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;완전 탐색은 보통 다음과 같은 방식으로 동작한다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;가능한 모든 경우의 수를 생성한다.&lt;/li&gt;
&lt;li&gt;각 경우가 문제의 조건을 만족하는지 확인한다.&lt;/li&gt;
&lt;li&gt;원하는&amp;nbsp;결과를&amp;nbsp;찾으면&amp;nbsp;반환하거나&amp;nbsp;최적의&amp;nbsp;해를&amp;nbsp;갱신한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;활용&amp;nbsp;가능한&amp;nbsp;문제&amp;nbsp;유형&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;완전 탐색은 다양한 문제에서 활용할 수 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;순열과 조합 문제: 가능한 모든 순열이나 조합을 생성해 정답을 찾는 문제&lt;/li&gt;
&lt;li&gt;부분 집합 생성: 특정 조건을 만족하는 부분 집합 찾기&lt;/li&gt;
&lt;li&gt;비밀번호 크래킹: 가능한 모든 비밀번호를 대입해 찾는 방식&lt;/li&gt;
&lt;li&gt;최적의&amp;nbsp;경로&amp;nbsp;찾기:&amp;nbsp;외판원&amp;nbsp;문제(TSP)와&amp;nbsp;같이&amp;nbsp;모든&amp;nbsp;경로를&amp;nbsp;탐색해&amp;nbsp;최적의&amp;nbsp;해를&amp;nbsp;찾는&amp;nbsp;문제&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;예제 코드(Python)&lt;/b&gt;&lt;/h2&gt;
&lt;pre id=&quot;code_1738583259519&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def generate_subsets(arr, subset=[], index=0):
    if index == len(arr):
        print(subset)
        return
    
    # 현재 요소를 포함하지 않는 경우
    generate_subsets(arr, subset, index + 1)
    
    # 현재 요소를 포함하는 경우
    generate_subsets(arr, subset + [arr[index]], index + 1)

# 예제 실행
arr = [1, 2, 3]
generate_subsets(arr)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;결론&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;완전&amp;nbsp;탐색은&amp;nbsp;간단하고&amp;nbsp;직관적인&amp;nbsp;알고리즘이지만,&amp;nbsp;경우의&amp;nbsp;수가&amp;nbsp;많아질수록&amp;nbsp;비효율적이&amp;nbsp;될&amp;nbsp;수&amp;nbsp;있다.&amp;nbsp;따라서&amp;nbsp;입력&amp;nbsp;크기가&amp;nbsp;작거나&amp;nbsp;최적화가&amp;nbsp;필요하지&amp;nbsp;않은&amp;nbsp;경우에&amp;nbsp;유용하게&amp;nbsp;사용할&amp;nbsp;수&amp;nbsp;있다.&amp;nbsp;보다&amp;nbsp;효율적인&amp;nbsp;해결&amp;nbsp;방법이&amp;nbsp;필요한&amp;nbsp;경우&amp;nbsp;백트래킹,&amp;nbsp;동적&amp;nbsp;계획법(DP),&amp;nbsp;분할&amp;nbsp;정복&amp;nbsp;등의&amp;nbsp;기법과&amp;nbsp;함께&amp;nbsp;사용하면&amp;nbsp;좋다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Computer Science/Algorithm</category>
      <category>brute force</category>
      <category>완전 탐색</category>
      <author>AlienCoder</author>
      <guid isPermaLink="true">https://aliencoder.tistory.com/201</guid>
      <comments>https://aliencoder.tistory.com/201#entry201comment</comments>
      <pubDate>Tue, 4 Feb 2025 13:33:21 +0900</pubDate>
    </item>
    <item>
      <title>[Python] 너비 우선 탐색(BFS, Breadth-First Search), 깊이 우선 탐색(DFS, Depth Frist Search)</title>
      <link>https://aliencoder.tistory.com/200</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;
&lt;script src=&quot;https://polyfill.io/v3/polyfill.min.js?features=es6&quot;&gt;&lt;/script&gt;
&lt;script src=&quot;https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js&quot;&gt;&lt;/script&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;핵심 개념&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;DFS(Depth-First&amp;nbsp;Search,&amp;nbsp;깊이&amp;nbsp;우선&amp;nbsp;탐색)&lt;/b&gt;와&amp;nbsp;&lt;b&gt;BFS(Breadth-First&amp;nbsp;Search,&amp;nbsp;너비&amp;nbsp;우선&amp;nbsp;탐색)&lt;/b&gt;는&amp;nbsp;그래프&amp;nbsp;탐색&amp;nbsp;알고리즘이다.&amp;nbsp;DFS는&amp;nbsp;한&amp;nbsp;방향으로&amp;nbsp;끝까지&amp;nbsp;탐색한&amp;nbsp;후&amp;nbsp;돌아오는&amp;nbsp;방식이며,&amp;nbsp;BFS는&amp;nbsp;가까운&amp;nbsp;노드부터&amp;nbsp;탐색하는&amp;nbsp;방식이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt; 시간 복잡도&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 탐색 방법의 시간 복잡도는 일반적으로 \( O(V + E) \)이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 \( V \)는 vertex(or 노드) 개수, \( E \)는 edge(간선) 개수이다. DFS와 BFS 모두 모든 노드를 방문하기 때문에 그래프의 크기에 따라 탐색 시간이 결정된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;동작방식 &amp;amp; 구현&lt;/b&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;750&quot; data-origin-height=&quot;312&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BARLP/btsL4Li0psp/BZKmdWGDHAKnFCJnXcioek/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BARLP/btsL4Li0psp/BZKmdWGDHAKnFCJnXcioek/img.png&quot; data-alt=&quot;Fig 1. BFS, DFS&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BARLP/btsL4Li0psp/BZKmdWGDHAKnFCJnXcioek/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBARLP%2FbtsL4Li0psp%2FBZKmdWGDHAKnFCJnXcioek%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;623&quot; height=&quot;259&quot; data-origin-width=&quot;750&quot; data-origin-height=&quot;312&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Fig 1. BFS, DFS&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;DFS&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;639&quot; data-origin-height=&quot;669&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/efdh9Z/btsL4QYGOl4/Xoqi8p7UX3KkGQov4fQOKK/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/efdh9Z/btsL4QYGOl4/Xoqi8p7UX3KkGQov4fQOKK/img.gif&quot; data-alt=&quot;Fig 2. DFS&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/efdh9Z/btsL4QYGOl4/Xoqi8p7UX3KkGQov4fQOKK/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/efdh9Z/btsL4QYGOl4/Xoqi8p7UX3KkGQov4fQOKK/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;386&quot; height=&quot;404&quot; data-origin-width=&quot;639&quot; data-origin-height=&quot;669&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Fig 2. DFS&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;시작 노드에서 한 방향으로 갈 수 있는 만큼 탐색한다.&lt;/li&gt;
&lt;li&gt;더 이상 이동할 곳이 없으면 이전 노드로 돌아가 다른 경로를 탐색한다.&lt;/li&gt;
&lt;li&gt;모든&amp;nbsp;노드를&amp;nbsp;방문할&amp;nbsp;때까지&amp;nbsp;반복한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;BFS&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1080&quot; data-origin-height=&quot;1080&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lkYJq/btsL45H8JHG/ZuBXWK25Kf5K6c0V23Lynk/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lkYJq/btsL45H8JHG/ZuBXWK25Kf5K6c0V23Lynk/img.gif&quot; data-alt=&quot;Fig 3. BFS&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lkYJq/btsL45H8JHG/ZuBXWK25Kf5K6c0V23Lynk/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/lkYJq/btsL45H8JHG/ZuBXWK25Kf5K6c0V23Lynk/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;337&quot; height=&quot;337&quot; data-origin-width=&quot;1080&quot; data-origin-height=&quot;1080&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Fig 3. BFS&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;시작 노드에서 가까운 노드부터 탐색한다.&lt;/li&gt;
&lt;li&gt;큐(Queue)를 활용해 방문할 노드를 관리한다.&lt;/li&gt;
&lt;li&gt;모든&amp;nbsp;노드를&amp;nbsp;방문할&amp;nbsp;때까지&amp;nbsp;반복한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;활용&amp;nbsp;가능한&amp;nbsp;문제&amp;nbsp;유형&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;DFS&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;경로 찾기: 미로 탐색, 게임 맵 탐색 등&lt;/li&gt;
&lt;li&gt;백트래킹: N-Queen 문제, 부분 수열 찾기 등&lt;/li&gt;
&lt;li&gt;사이클 탐색: 그래프 내 사이클 존재 여부 확인&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;BFS&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;최단 거리 문제: 미로 최단 거리, 네트워크 거리 계산&lt;/li&gt;
&lt;li&gt;최소 이동 횟수 문제: 퍼즐 문제, 말 이동 횟수 등&lt;/li&gt;
&lt;li&gt;레벨&amp;nbsp;탐색:&amp;nbsp;트리의&amp;nbsp;특정&amp;nbsp;깊이에&amp;nbsp;있는&amp;nbsp;노드&amp;nbsp;탐색&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;예제 코드(Python)&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;DFS&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1738584388513&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def dfs(graph, node, visited):
    if node not in visited:
        visited.add(node)
        print(node, end=' ')
        for neighbor in graph[node]:
            dfs(graph, neighbor, visited)

# 예제 실행
graph = {
    'A': ['B', 'C'],
    'B': ['A', 'D', 'E'],
    'C': ['A', 'F', 'G'],
    'D': ['B'],
    'E': ['B', 'H'],
    'F': ['C'],
    'G': ['C'],
    'H': ['E']
}
visited = set()
dfs(graph, 'A', visited)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;BFS&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1738584401692&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from collections import deque

def bfs(graph, start):
    visited = set()
    queue = deque([start])
    
    while queue:
        node = queue.popleft()
        if node not in visited:
            visited.add(node)
            print(node, end=' ')
            queue.extend(graph[node])

# 예제 실행
bfs(graph, 'A')&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;결론&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DFS와&amp;nbsp;BFS는&amp;nbsp;그래프&amp;nbsp;탐색의&amp;nbsp;핵심&amp;nbsp;알고리즘이다.&amp;nbsp;DFS는&amp;nbsp;백트래킹과&amp;nbsp;재귀적&amp;nbsp;탐색에&amp;nbsp;유리하며,&amp;nbsp;BFS는&amp;nbsp;최단&amp;nbsp;거리&amp;nbsp;문제에서&amp;nbsp;강력한&amp;nbsp;성능을&amp;nbsp;발휘한다.&amp;nbsp;문제의&amp;nbsp;특성에&amp;nbsp;따라&amp;nbsp;적절한&amp;nbsp;탐색&amp;nbsp;방법을&amp;nbsp;선택하는&amp;nbsp;것이&amp;nbsp;중요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style5&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;참고 자료&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://velog.io/@vagabondms/DFS-vs-BFS&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://velog.io/@vagabondms/DFS-vs-BFS&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://oliviagallucci.com/graph-algorithm-basics-bfs-and-dfs/#dfs&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://oliviagallucci.com/graph-algorithm-basics-bfs-and-dfs/#dfs&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.batoi.com/blogs/developers/introduction-data-structures-tree-60660d6769715&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.batoi.com/blogs/developers/introduction-data-structures-tree-60660d6769715&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Computer Science/Algorithm</category>
      <category>BFS</category>
      <category>Breadth-First Search</category>
      <category>depth frist search</category>
      <category>DFS</category>
      <category>깊이 우선 탐색</category>
      <category>너비 우선 탐색</category>
      <author>AlienCoder</author>
      <guid isPermaLink="true">https://aliencoder.tistory.com/200</guid>
      <comments>https://aliencoder.tistory.com/200#entry200comment</comments>
      <pubDate>Tue, 4 Feb 2025 09:16:54 +0900</pubDate>
    </item>
    <item>
      <title>[Python] 투 포인터(Two Pointer)</title>
      <link>https://aliencoder.tistory.com/199</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;
&lt;script src=&quot;https://polyfill.io/v3/polyfill.min.js?features=es6&quot;&gt;&lt;/script&gt;
&lt;script src=&quot;https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js&quot;&gt;&lt;/script&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;핵심 개념&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;투&amp;nbsp;포인터(Two&amp;nbsp;Pointer)&lt;/b&gt;는&amp;nbsp;배열이나&amp;nbsp;리스트에서&amp;nbsp;두&amp;nbsp;개의&amp;nbsp;포인터를&amp;nbsp;활용해&amp;nbsp;문제를&amp;nbsp;해결하는&amp;nbsp;기법이다.&amp;nbsp;주로&amp;nbsp;정렬된&amp;nbsp;배열에서&amp;nbsp;특정&amp;nbsp;조건을&amp;nbsp;만족하는&amp;nbsp;부분을&amp;nbsp;찾거나,&amp;nbsp;두&amp;nbsp;개의&amp;nbsp;배열을&amp;nbsp;병합할&amp;nbsp;때&amp;nbsp;사용된다.&amp;nbsp;탐색&amp;nbsp;범위를&amp;nbsp;줄여&amp;nbsp;효율적으로&amp;nbsp;동작하기&amp;nbsp;때문에&amp;nbsp;많은&amp;nbsp;알고리즘&amp;nbsp;문제에서&amp;nbsp;활용된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt; 시간 복잡도&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Two Pointer 기법의 시간 복잡도는 일반적으로 \( O(N) \)이다. 각 포인터가 배열을 한 번씩만 훑고 지나가기 때문에 불필요한 중복 연산이 줄어든다. 반면, Brute Force 접근법은 \( O(N^2) \)인 경우가 많아 투 포인터를 사용하면 성능을 크게 향상시킬 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;동작방식 &amp;amp; 구현&lt;br /&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1080&quot; data-origin-height=&quot;600&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/F1U9M/btsL6bt5fob/K04l8Ho1gnwcoENpFUDM50/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/F1U9M/btsL6bt5fob/K04l8Ho1gnwcoENpFUDM50/img.gif&quot; data-alt=&quot;Fig 1. Two Pointer&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/F1U9M/btsL6bt5fob/K04l8Ho1gnwcoENpFUDM50/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/F1U9M/btsL6bt5fob/K04l8Ho1gnwcoENpFUDM50/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;608&quot; height=&quot;338&quot; data-origin-width=&quot;1080&quot; data-origin-height=&quot;600&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Fig 1. Two Pointer&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;투 포인터는 보통 다음과 같은 방식으로 동작한다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;시작점과 끝점을 설정: 두 개의 포인터를 배열의 시작과 끝 또는 특정 기준에 맞게 배치한다.&lt;/li&gt;
&lt;li&gt;조건에 따라 이동: 특정 조건을 만족할 때까지 포인터를 이동시키며 탐색한다.&lt;/li&gt;
&lt;li&gt;결과&amp;nbsp;도출:&amp;nbsp;문제에서&amp;nbsp;요구하는&amp;nbsp;조건을&amp;nbsp;만족하는&amp;nbsp;경우&amp;nbsp;값을&amp;nbsp;저장하거나&amp;nbsp;연산을&amp;nbsp;수행한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;활용&amp;nbsp;가능한&amp;nbsp;문제&amp;nbsp;유형&lt;/b&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;합이 특정 값이 되는 두 수 찾기: 위의 예제처럼 두 개의 요소 합이 특정 값을 만족하는지 찾는 문제&lt;/li&gt;
&lt;li&gt;구간 합(연속된 부분 배열의 합 찾기): 특정 조건을 만족하는 연속된 부분 배열의 합을 찾는 문제&lt;/li&gt;
&lt;li&gt;두 개의 정렬된 배열 병합(Merge): 정렬된 두 개의 배열을 하나로 합치는 과정에서 효율적으로 사용됨&lt;/li&gt;
&lt;li&gt;팰린드롬 검사: 문자열이 팰린드롬인지 검사할 때, 양쪽에서 포인터를 이동시키며 비교하는 방식으로 활용 가능&lt;/li&gt;
&lt;li&gt;투&amp;nbsp;포인터&amp;nbsp;+&amp;nbsp;슬라이딩&amp;nbsp;윈도우:&amp;nbsp;투&amp;nbsp;포인터를&amp;nbsp;활용하여&amp;nbsp;가변&amp;nbsp;길이의&amp;nbsp;윈도우를&amp;nbsp;이동시키며&amp;nbsp;문제&amp;nbsp;해결&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;예제 코드(Python)&lt;/b&gt;&lt;/h2&gt;
&lt;pre id=&quot;code_1738583259519&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def two_sum(arr, target):
    arr.sort()  # 정렬된 상태에서 투 포인터 사용
    left, right = 0, len(arr) - 1
    
    while left &amp;lt; right:
        current_sum = arr[left] + arr[right]
        if current_sum == target:
            return (arr[left], arr[right])
        elif current_sum &amp;lt; target:
            left += 1
        else:
            right -= 1
    
    return None

# 예제 실행
arr = [1, 3, 5, 7, 9]
target = 10
print(two_sum(arr, target))  # 출력: (1, 9)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;결론&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;투&amp;nbsp;포인터는&amp;nbsp;배열&amp;nbsp;탐색을&amp;nbsp;최적화할&amp;nbsp;수&amp;nbsp;있는&amp;nbsp;강력한&amp;nbsp;알고리즘&amp;nbsp;기법이다.&amp;nbsp;단순한&amp;nbsp;브루트포스&amp;nbsp;방식보다&amp;nbsp;시간&amp;nbsp;복잡도를&amp;nbsp;개선할&amp;nbsp;수&amp;nbsp;있으며,&amp;nbsp;특정&amp;nbsp;문제&amp;nbsp;유형에서는&amp;nbsp;가장&amp;nbsp;효율적인&amp;nbsp;해결책이&amp;nbsp;될&amp;nbsp;수&amp;nbsp;있다.&amp;nbsp;문제&amp;nbsp;유형에&amp;nbsp;따라&amp;nbsp;투&amp;nbsp;포인터를&amp;nbsp;적절히&amp;nbsp;적용하면&amp;nbsp;훨씬&amp;nbsp;빠르고&amp;nbsp;효율적인&amp;nbsp;코드를&amp;nbsp;작성할&amp;nbsp;수&amp;nbsp;있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style5&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;참고 자료&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://medium.com/@lucien1999s.pro/two-pointer-a1dd79fefd1a&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://medium.com/@lucien1999s.pro/two-pointer-a1dd79fefd1a&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Computer Science/Algorithm</category>
      <category>two pointer</category>
      <category>투 포인터</category>
      <author>AlienCoder</author>
      <guid isPermaLink="true">https://aliencoder.tistory.com/199</guid>
      <comments>https://aliencoder.tistory.com/199#entry199comment</comments>
      <pubDate>Mon, 3 Feb 2025 20:56:27 +0900</pubDate>
    </item>
    <item>
      <title>[Python/Java] 백준 1018 - 체스판 다시 칠하기 [Brute Force]</title>
      <link>https://aliencoder.tistory.com/197</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;문제&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/1018&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.acmicpc.net/problem/1018&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;해설&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;BFS로 접근하는 것으로 착각하여 시간 소비를 많이 하였다. Brute Force를 이용하여 완전 탐색을 진행하면 쉽게 해결할 수 있는 문제였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Python&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1737017190904&quot; class=&quot;java&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;from sys import stdin


def brute_force(matrix: list, x: int, y: int):
    w_start = 0
    b_start = 0

    for i in range(8):
        for j in range(8):
            if (i+j) % 2 == 0:
                if matrix[x+i][y+j] != &quot;W&quot;:
                    w_start += 1
                elif matrix[x+i][y+j] != &quot;B&quot;:
                    b_start += 1
            else:
                if matrix[x+i][y+j] != &quot;B&quot;:
                    w_start += 1
                elif matrix[x+i][y+j] != &quot;W&quot;:
                    b_start += 1

    return min(w_start, b_start)


n, m = map(int, stdin.readline().split(&quot; &quot;))
board = [list(stdin.readline().strip()) for _ in range(n)]

result = []
for row in range(n-7):
    for col in range(m-7):
        result.append(brute_force(board, row, col))
print(min(result))&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Java&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1737017205334&quot; class=&quot;java&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.io.*;
import java.util.*;

public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

        String[] condition = br.readLine().split(&quot; &quot;);
        int n = Integer.parseInt(condition[0]);
        int m = Integer.parseInt(condition[1]);
        
        List&amp;lt;List&amp;lt;String&amp;gt;&amp;gt; matrix = new ArrayList&amp;lt;&amp;gt;();
        for(int i = 0; i &amp;lt; n; i++) {
            matrix.add(new ArrayList&amp;lt;&amp;gt;(Arrays.asList(br.readLine().trim().split(&quot;&quot;))));
        }
        
        List&amp;lt;Integer&amp;gt; result = new ArrayList&amp;lt;&amp;gt;();
        for(int row=0; row&amp;lt;n-7; row++){
            for(int col=0; col&amp;lt;m-7; col++){
                result.add(bruteForce(matrix, row, col));
            }
        }

        System.out.println(Collections.min(result));
    }

    static int bruteForce(List&amp;lt;List&amp;lt;String&amp;gt;&amp;gt; matrix, int x, int y) {
        int wStart = 0;
        int bStart = 0;

        for(int i=0; i&amp;lt;8; i++){
            for(int j=0; j&amp;lt;8; j++) {
                if((i + j) % 2 == 0) {
                    if(!matrix.get(x + i).get(y + j).equals(&quot;W&quot;)) { wStart++; }
                    if(!matrix.get(x + i).get(y + j).equals(&quot;B&quot;)) { bStart++; }
                } else {
                    if(!matrix.get(x + i).get(y + j).equals(&quot;B&quot;)) { wStart++; }
                    if(!matrix.get(x + i).get(y + j).equals(&quot;W&quot;)) { bStart++; }
                }
            }
        }

        return Math.min(bStart, wStart);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Study/Coding Test</category>
      <author>AlienCoder</author>
      <guid isPermaLink="true">https://aliencoder.tistory.com/197</guid>
      <comments>https://aliencoder.tistory.com/197#entry197comment</comments>
      <pubDate>Mon, 3 Feb 2025 20:40:28 +0900</pubDate>
    </item>
    <item>
      <title>[Python/Java] 백준 2573 - 빙산 [BFS]</title>
      <link>https://aliencoder.tistory.com/196</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;문제&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/2573&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.acmicpc.net/problem/2573&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;해설&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;BFS를 이용하여 해결할 수 있었다. melting, multipartite 검사, BFS로 각 스텝을 구성하였다. 녹는 빙하에 대해서는 loop를 줄이기 위해 BFS에서 녹아야 하는 빙하의 좌표를 미리 입력받았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Python&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1737017190904&quot; class=&quot;python&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;from sys import stdin
from collections import deque


command = [[-1, 0], [0, -1], [0, 1], [1, 0]]


def bfs(graph: list, visited: list, start_x: int, start_y: int, melt_list: list):
    queue = deque()
    queue.append([start_x, start_y])
    visited[start_x][start_y] = True

    while queue:
        row, col = queue.popleft()

        for r, c in command:
            r_move = row + r
            c_move = col + c
            if graph[r_move][c_move] == 0:
                melt_list.append([row, col])

            if 0 &amp;lt;= r_move &amp;lt; len(graph) and 0 &amp;lt;= c_move &amp;lt; len(graph[0]):
                if not visited[r_move][c_move] and graph[r_move][c_move] &amp;gt; 0:
                    visited[r_move][c_move] = True
                    queue.append((r_move, c_move))


def is_multipartite(rows: int, cols: int, graph: list, melt_list: list):
    visited = [[False for _ in range(cols)] for _ in range(rows)]
    count = 0

    for i in range(rows):
        for j in range(cols):
            if 0 &amp;lt; graph[i][j] and not visited[i][j]:
                bfs(graph, visited, i, j, melt_list)
                count += 1
                if 1 &amp;lt; count:
                    return True
    return False


def melting(rows: int, cols: int, graph: list):
    year = 0

    while True:
        melt_list = []
        if is_multipartite(rows, cols, graph, melt_list):
            return year
        if not melt_list:
            return 0

        for r, c in melt_list:
            if 0 &amp;lt; graph[r][c]:
                graph[r][c] -= 1

        year += 1


n, m = map(int, stdin.readline().split(&quot; &quot;))
graph = [list(map(int, stdin.readline().split(&quot; &quot;))) for _ in range(n)]
print(melting(n, m, graph))&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Java&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1737017205334&quot; class=&quot;java&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.io.*;
import java.util.*;

public class Main {
    static int[] command = {-1, 0, 0, -1, 0, 1, 1, 0}; 

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringBuilder sb = new StringBuilder();

        String[] condition = br.readLine().split(&quot; &quot;);
        int n = Integer.parseInt(condition[0]);
        int m = Integer.parseInt(condition[1]);

        int[][] graph = new int[n][m];

        for(int i = 0; i &amp;lt; n; i++) {
            String[] rows = br.readLine().split(&quot; &quot;);
            for(int j = 0; j &amp;lt; rows.length; j++) {
                graph[i][j] = Integer.parseInt(rows[j]);
            }
        }

        sb.append(melting(n, m, graph));
        System.out.println(sb);
    }

    public static void bfs(int[][] graph, boolean[][] visited, int startX, int startY, List&amp;lt;int[]&amp;gt; meltList) {
        Queue&amp;lt;int[]&amp;gt; queue = new LinkedList&amp;lt;&amp;gt;();
        queue.add(new int[]{startX, startY});
        visited[startX][startY] = true;

        while(!queue.isEmpty()) {
            int[] current = queue.poll();
            int row = current[0];
            int col = current[1];

            for(int i = 0; i &amp;lt; 4; i++) {
                int rMove = row + command[i * 2];
                int cMove = col + command[i * 2 + 1];

                if(0 &amp;lt;= rMove &amp;amp;&amp;amp; rMove &amp;lt; graph.length &amp;amp;&amp;amp; 0 &amp;lt;= cMove &amp;amp;&amp;amp; cMove &amp;lt; graph[0].length) {
                    if(graph[rMove][cMove] == 0) {
                        meltList.add(new int[]{row, col});
                    }
                    if(!visited[rMove][cMove] &amp;amp;&amp;amp; 0 &amp;lt; graph[rMove][cMove]) {
                        visited[rMove][cMove] = true;
                        queue.add(new int[]{rMove, cMove});
                    }
                }
            }
        }
    }

    public static boolean isMultipartite(int[][] graph, boolean[][] visited, List&amp;lt;int[]&amp;gt; meltList) {
        int count = 0;

        for(int i = 0; i &amp;lt; graph.length; i++) {
            for(int j = 0; j &amp;lt; graph[0].length; j++) {
                if(0 &amp;lt; graph[i][j] &amp;amp;&amp;amp; !visited[i][j]) {
                    bfs(graph, visited, i, j, meltList);
                    count++;
                    if(1 &amp;lt; count) { return true; }
                }
            }
        }
        return false;
    }

    public static int melting(int n, int m, int[][] graph) {
        int year = 0;

        while(true) {
            List&amp;lt;int[]&amp;gt; meltList = new ArrayList&amp;lt;&amp;gt;();
            if(isMultipartite(graph, new boolean[n][m], meltList)) {
                return year;
            }

            if(meltList.isEmpty()) { return 0; }

            for(int[] coords : meltList) {
                int r = coords[0];
                int c = coords[1];
                if(graph[r][c] &amp;gt; 0) {
                    graph[r][c]--;
                }
            }

            year++;
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Study/Coding Test</category>
      <author>AlienCoder</author>
      <guid isPermaLink="true">https://aliencoder.tistory.com/196</guid>
      <comments>https://aliencoder.tistory.com/196#entry196comment</comments>
      <pubDate>Mon, 3 Feb 2025 17:18:22 +0900</pubDate>
    </item>
    <item>
      <title>[Python/Java] 백준 1707 - 이분 그래프 [DFS/BFS]</title>
      <link>https://aliencoder.tistory.com/195</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;문제&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/1707&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.acmicpc.net/problem/1707&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;해설&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DFS와 BFS 모두 사용가능한 문제이다. DFS를 이용하는 것이 개인적으로 이번 문제에선 이해가 잘 되었던 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Python&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Python은 파이썬의 기본 재귀 깊이 제한은 1000이기 때문에 setrecursionlimit를 사용하지 않으면 런타임 에러가 발생한다. 너무 크게 설정해도 메모리 오류가 발생하므로 적절한 크기로 조절이 필요하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;DFS&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1737017190904&quot; class=&quot;java&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;from sys import stdin, setrecursionlimit
setrecursionlimit(100000)


def dfs(graph: list, visited: list, colors: list, node: int, color: int):
    visited[node] = True
    colors[node] = color

    for neighbor in graph[node]:
        if not visited[neighbor]:
            if not dfs(graph, visited, colors, neighbor, 1 - color):
                return False
        elif colors[neighbor] == colors[node]:
            return False
    return True


def is_bipartite(n: int, graph: list):
    visited = [False] * (n + 1)
    colors = [-1] * (n + 1)

    for i in range(1, n + 1):
        if not visited[i]:
            if not dfs(graph, visited, colors, i, 0):
                return &quot;NO&quot;
    return &quot;YES&quot;


k = int(stdin.readline())
for _ in range(k):
    vertex, edge = map(int, stdin.readline().split(&quot; &quot;))
    adj_list = [[] for _ in range(vertex + 1)]  # 인접 리스트로 변경

    for _ in range(edge):
        u, v = map(int, stdin.readline().split(&quot; &quot;))
        adj_list[u].append(v)
        adj_list[v].append(u)

    print(is_bipartite(vertex, adj_list))&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;BFS&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1738547125641&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from sys import stdin
from collections import deque


def bfs(graph: list, visited: list, start: int, colors: list):
    queue = deque([start])
    visited[start] = True
    colors[start] = 1

    while queue:
        node = queue.popleft()

        for neighbor in graph[node]:
            if not visited[neighbor]:
                queue.append(neighbor)
                visited[neighbor] = True
                colors[neighbor] = -colors[node]
            elif colors[node] == colors[neighbor]:
                return False
    return True


def is_bipartite(n: int, graph: list):
    visited = [False] * (n+1)
    colors = [-1] * (n+1)

    for i in range(1, n+1):
        if not visited[i]:
            if not bfs(graph, visited, i, colors):
                return &quot;NO&quot;
    return &quot;YES&quot;


k = int(stdin.readline())
for _ in range(k):
    vertex, edge = map(int, stdin.readline().split(&quot; &quot;))
    adj_list = [[] for _ in range(vertex + 1)]  # 인접 리스트로 변경

    for _ in range(edge):
        u, v = map(int, stdin.readline().split(&quot; &quot;))
        adj_list[u].append(v)
        adj_list[v].append(u)

    print(is_bipartite(vertex, adj_list))&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Java&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;DFS&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1737017205334&quot; class=&quot;java&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.io.*;
import java.util.*;

public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        int k = Integer.parseInt(br.readLine().trim());

        for (int t = 0; t &amp;lt; k; t++) {
            String[] condition = br.readLine().split(&quot; &quot;);
            int vertex = Integer.parseInt(condition[0]);
            int edge = Integer.parseInt(condition[1]);

            List&amp;lt;Integer&amp;gt;[] adjList = new ArrayList[vertex + 1];
            for (int i = 0; i &amp;lt;= vertex; i++) {
                adjList[i] = new ArrayList&amp;lt;&amp;gt;();
            }

            for (int i = 0; i &amp;lt; edge; i++) {
                String[] edgeInfo = br.readLine().split(&quot; &quot;);
                int u = Integer.parseInt(edgeInfo[0]);
                int v = Integer.parseInt(edgeInfo[1]);

                adjList[u].add(v);
                adjList[v].add(u);
            }

            System.out.println(isBipartite(vertex, adjList) ? &quot;YES&quot; : &quot;NO&quot;);
        }
    }

    static boolean dfs(List&amp;lt;Integer&amp;gt;[] graph, int[] colors, int node, int color) {
        colors[node] = color;

        for (int neighbor : graph[node]) {
            if (colors[neighbor] == -1) {
                if (!dfs(graph, colors, neighbor, 1 - color)) {
                    return false;
                }
            } else if (colors[neighbor] == color) {
                return false;
            }
        }
        return true;
    }

    static boolean isBipartite(int n, List&amp;lt;Integer&amp;gt;[] graph) {
        int[] colors = new int[n + 1];
        Arrays.fill(colors, -1);

        for (int i = 1; i &amp;lt;= n; i++) {
            if (colors[i] == -1) {
                if (!dfs(graph, colors, i, 0)) {
                    return false;
                }
            }
        }
        return true;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;BFS&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1738547272811&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.io.*;
import java.util.*;

public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        int k = Integer.parseInt(br.readLine().trim());

        for (int t = 0; t &amp;lt; k; t++) {
            String[] condition = br.readLine().split(&quot; &quot;);
            int vertex = Integer.parseInt(condition[0]);
            int edge = Integer.parseInt(condition[1]);

            List&amp;lt;Integer&amp;gt;[] adjList = new ArrayList[vertex + 1];
            for (int i = 0; i &amp;lt;= vertex; i++) {
                adjList[i] = new ArrayList&amp;lt;&amp;gt;();
            }

            for (int i = 0; i &amp;lt; edge; i++) {
                String[] edgeInfo = br.readLine().split(&quot; &quot;);
                int u = Integer.parseInt(edgeInfo[0]);
                int v = Integer.parseInt(edgeInfo[1]);

                adjList[u].add(v);
                adjList[v].add(u);
            }

            System.out.println(isBipartite(vertex, adjList) ? &quot;YES&quot; : &quot;NO&quot;);
        }
    }

    static boolean bfs(List&amp;lt;Integer&amp;gt;[] graph, boolean[] visited, int[] colors, int start) {
        Queue&amp;lt;Integer&amp;gt; queue = new LinkedList&amp;lt;&amp;gt;();
        queue.add(start);
        visited[start] = true;
        colors[start] = 1;

        while (!queue.isEmpty()) {
            int node = queue.poll();

            for (int neighbor : graph[node]) {
                if (!visited[neighbor]) {
                    queue.add(neighbor);
                    visited[neighbor] = true;
                    colors[neighbor] = -colors[node];
                } else if (colors[neighbor] == colors[node]) {
                    return false;
                }
            }
        }
        return true;
    }

    static boolean isBipartite(int n, List&amp;lt;Integer&amp;gt;[] graph) {
        boolean[] visited = new boolean[n + 1];
        int[] colors = new int[n + 1];
        Arrays.fill(colors, 0);

        for (int i = 1; i &amp;lt;= n; i++) {
            if (!visited[i]) {
                if (!bfs(graph, visited, colors, i)) {
                    return false;
                }
            }
        }
        return true;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Study/Coding Test</category>
      <author>AlienCoder</author>
      <guid isPermaLink="true">https://aliencoder.tistory.com/195</guid>
      <comments>https://aliencoder.tistory.com/195#entry195comment</comments>
      <pubDate>Sun, 2 Feb 2025 11:25:50 +0900</pubDate>
    </item>
    <item>
      <title>[Python/Java] 백준 2667 - 단지번호붙이기 [DFS/BFS]</title>
      <link>https://aliencoder.tistory.com/194</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;문제&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/2667&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.acmicpc.net/problem/2667&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;해설&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DFS와 BFS 모두 사용하여 풀이가 가능한 문제라고 한다. 두 방법 모두 이용하여 풀어보았다. 아직 실력이 초보에 가까워 직관적으로 구현하려고 노력하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 문제는 DFS가 더 적절한 방법이라고 생각한다. 이 문제에선 DFS의 탐색 속도가 조금 더 빠르기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;BFS는 주변의 요소만 판단하며 거의 마지막 그리드까지 하나씩 확인해나가야 한다. 반면, DFS는 하나의 hit를 찾았을 때 한 번에 끝까지 모든 인접 경로를 탐색하는 방식이므로 visited 검사를 통해 BFS보다 좀 더 빠르게 조건문에서 필터가 가능하다. 다만 DFS를 사용하였을 땐 Stack Overflow의 위험이 내재되어 있다는 점은 인지하여야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Python&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;DFS&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1737017190904&quot; class=&quot;java&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;from sys import stdin


def dfs(row: int, col: int):
    global house_count
    visited[row][col] = True
    house_count += 1

    if 0 &amp;lt; row and not visited[row-1][col] and arr[row-1][col]:     # up
        dfs(row-1, col)
    if row &amp;lt; n-1 and not visited[row+1][col] and arr[row+1][col]:   # down
        dfs(row+1, col)
    if 0 &amp;lt; col and not visited[row][col-1] and arr[row][col-1]:     # left
        dfs(row, col-1)
    if col &amp;lt; n-1 and not visited[row][col+1] and arr[row][col+1]:   # right
        dfs(row, col+1)


n = int(stdin.readline())
arr = [list(map(int, list(stdin.readline().strip()))) for _ in range(n)]
visited = [[False] * n for _ in range(n)]

result = []
for i in range(n):
    for j in range(n):
        if not visited[i][j] and arr[i][j] == 1:
            house_count = 0
            dfs(i, j)
            result.append(house_count)

print(len(result))
[print(v) for v in sorted(result)]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;BFS&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1737611816491&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from sys import stdin
from collections import deque


def bfs(row: int, col: int):
    queue = deque()
    queue.append([row, col])
    visited[row][col] = True

    house_count = 1
    while queue:
        row, col = queue.popleft()

        if 0 &amp;lt; row and not visited[row-1][col] and arr[row-1][col]:     # up
            house_count += 1
            visited[row-1][col] = True
            queue.append((row-1, col))
        if row &amp;lt; n-1 and not visited[row+1][col] and arr[row+1][col]:   # down
            house_count += 1
            visited[row+1][col] = True
            queue.append((row+1, col))
        if 0 &amp;lt; col and not visited[row][col-1] and arr[row][col-1]:     # left
            house_count += 1
            visited[row][col-1] = True
            queue.append((row, col-1))
        if col &amp;lt; n-1 and not visited[row][col+1] and arr[row][col+1]:   # right
            house_count += 1
            visited[row][col+1] = True
            queue.append((row, col+1))
    return house_count


n = int(stdin.readline())
arr = [list(map(int, list(stdin.readline().strip()))) for _ in range(n)]
visited = [[False] * n for _ in range(n)]

result = []

for i in range(n):
    for j in range(n):
        if not visited[i][j] and arr[i][j]:
            result.append(bfs(i, j))

print(len(result))
[print(v) for v in sorted(result)]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Java&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;DFS&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1737017205334&quot; class=&quot;java&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.io.*;
import java.util.*;

public class Main {
    static int house_count;

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringBuilder sb = new StringBuilder();

        int n = Integer.parseInt(br.readLine());

        int[][] arr = new int[n][n];
        int[][] visited = new int[n][n];

        for(int i=0; i&amp;lt;n; i++){
            String row = br.readLine();
            for(int j=0; j&amp;lt;row.length(); j++){
                arr[i][j] = Integer.parseInt(row.substring(j, j+1));
                visited[i][j] = 0;
            }
        }

        List&amp;lt;Integer&amp;gt; result = new ArrayList&amp;lt;&amp;gt;();
        for(int i=0; i&amp;lt;n; i++){
            for(int j=0; j&amp;lt;n; j++){
                if(visited[i][j]==0 &amp;amp;&amp;amp; arr[i][j]==1){
                    house_count = 0;
                    dfs(arr, visited, n, i, j);
                    result.add(house_count);
                }
            }
        }

        Collections.sort(result);
        sb.append(result.size()).append(&quot;\n&quot;);
        for(int v : result){
            sb.append(v).append(&quot;\n&quot;);
        }
        System.out.println(sb);
    }

    public static void dfs(int[][] arr, int[][] visited, int n, int row, int col) {
        visited[row][col] = 1;
        house_count += 1;

        if(0&amp;lt;row &amp;amp;&amp;amp; visited[row-1][col]==0 &amp;amp;&amp;amp; arr[row-1][col]==1){  // up
            dfs(arr, visited, n, row-1, col);
        }
        if(row&amp;lt;n-1 &amp;amp;&amp;amp; visited[row+1][col]==0 &amp;amp;&amp;amp; arr[row+1][col]==1){  // down
            dfs(arr, visited, n, row+1, col);
        }
        if(0&amp;lt;col &amp;amp;&amp;amp; visited[row][col-1]==0 &amp;amp;&amp;amp; arr[row][col-1]==1){  // left
            dfs(arr, visited, n, row, col-1);
        }
        if(col&amp;lt;n-1 &amp;amp;&amp;amp; visited[row][col+1]==0 &amp;amp;&amp;amp; arr[row][col+1]==1){  // right
            dfs(arr, visited, n, row, col+1);
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;BFS&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1737618943040&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.*;

public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringBuilder sb = new StringBuilder();

        int n = Integer.parseInt(br.readLine());

        int[][] arr = new int[n][n];
        int[][] visited = new int[n][n];

        for(int i=0; i&amp;lt;n; i++){
            String row = br.readLine();
            for(int j=0; j&amp;lt;row.length(); j++){
                arr[i][j] = Integer.parseInt(row.substring(j, j+1));
                visited[i][j] = 0;
            }
        }

        List&amp;lt;Integer&amp;gt; result = new ArrayList&amp;lt;&amp;gt;();
        for(int i=0; i&amp;lt;n; i++){
            for(int j=0; j&amp;lt;n; j++){
                if(visited[i][j]==0 &amp;amp;&amp;amp; arr[i][j]==1){
                    result.add(bfs(arr, visited, n, i, j));
                }
            }
        }

        Collections.sort(result);
        sb.append(result.size()).append(&quot;\n&quot;);
        for(int v : result){
            sb.append(v).append(&quot;\n&quot;);
        }
        System.out.println(sb);
    }

    public static int bfs(int[][] arr, int[][] visited, int n, int row, int col) {
        Queue&amp;lt;List&amp;lt;Integer&amp;gt;&amp;gt; queue = new LinkedList&amp;lt;&amp;gt;();
        queue.add(Arrays.asList(row, col));
        visited[row][col] = 1;

        int house_count = 1;
        while(!queue.isEmpty()){
            List&amp;lt;Integer&amp;gt; coord = queue.poll();
            row = coord.get(0);
            col = coord.get(1);

            if(0&amp;lt;row &amp;amp;&amp;amp; visited[row-1][col]==0 &amp;amp;&amp;amp; arr[row-1][col]==1){  // up
                house_count++;
                visited[row-1][col] = 1;
                queue.add(Arrays.asList(row-1, col));
            }
            if(row&amp;lt;n-1 &amp;amp;&amp;amp; visited[row+1][col]==0 &amp;amp;&amp;amp; arr[row+1][col]==1){  // down
                house_count++;
                visited[row+1][col] = 1;
                queue.add(Arrays.asList(row+1, col));
            }
            if(0&amp;lt;col &amp;amp;&amp;amp; visited[row][col-1]==0 &amp;amp;&amp;amp; arr[row][col-1]==1){  // left
                house_count++;
                visited[row][col-1] = 1;
                queue.add(Arrays.asList(row, col-1));
            }
            if(col&amp;lt;n-1 &amp;amp;&amp;amp; visited[row][col+1]==0 &amp;amp;&amp;amp; arr[row][col+1]==1){  // right
                house_count++;
                visited[row][col+1] = 1;
                queue.add(Arrays.asList(row, col+1));
            }
        }

        return house_count;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Study/Coding Test</category>
      <author>AlienCoder</author>
      <guid isPermaLink="true">https://aliencoder.tistory.com/194</guid>
      <comments>https://aliencoder.tistory.com/194#entry194comment</comments>
      <pubDate>Thu, 23 Jan 2025 17:03:03 +0900</pubDate>
    </item>
    <item>
      <title>[Python/Java] 백준 1697 - 숨바꼭질 [BFS]</title>
      <link>https://aliencoder.tistory.com/193</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;문제&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/1697&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.acmicpc.net/problem/1697&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;해설&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최단 경로를 탐색하는 문제이므로 BFS를 사용하였다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Python&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;queue에서 pop하는 과정은 &lt;/span&gt;deque 자료구조를 활용하여 시간초과에 관한 문제를 개선하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 set을 이용하여 visited를 저장하였지만, 메모리 초과가 발생하였다. 이 부분을 list를 이용하여 최대 사이즈를 지정하여 메모리 초과를 해결할 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1737017190904&quot; class=&quot;python&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;from sys import stdin
from collections import deque


MAX = 100001


def bfs(start: int, end: int):
    visited = [False] * MAX
    visited[start] = True
    queue = deque([(start, 0)])

    while queue:
        current, steps = queue.popleft()

        if current == end:
            return steps

        for neighbor in [current - 1, current + 1, current * 2]:
            if 0 &amp;lt;= neighbor &amp;lt; MAX and not visited[neighbor]:
                visited[neighbor] = True
                queue.append((neighbor, steps + 1))


n, k = map(int, stdin.readline().split(&quot; &quot;))
print(bfs(n, k))&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Java&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1737017205334&quot; class=&quot;java&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.io.*;
import java.util.*;


public class Main {
    static int MAX = 1000001;

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

        String[] condition = br.readLine().split(&quot; &quot;);
        int n = Integer.parseInt(condition[0]);
        int k = Integer.parseInt(condition[1]);
        
        System.out.println(bfs(n, k));
    }

    public static int bfs(int start, int end){
        int[] visited = new int[MAX];
        visited[start] = 1;

        Deque&amp;lt;List&amp;lt;Integer&amp;gt;&amp;gt; queue = new ArrayDeque&amp;lt;&amp;gt;();
        queue.add(List.of(start, 0));

        while(!queue.isEmpty()){
            List&amp;lt;Integer&amp;gt; item = queue.poll();
            int current = item.get(0);
            int steps = item.get(1);

            if(current==end){ return steps; }

            for(int neighbor : List.of(current-1, current+1, current*2)){
                if(0&amp;lt;=neighbor &amp;amp;&amp;amp; neighbor&amp;lt;MAX &amp;amp;&amp;amp; visited[neighbor]!=1){
                    visited[neighbor] = 1;
                    queue.add(List.of(neighbor, steps+1));
                }
            }
        }
        return 0;
    } 
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Study/Coding Test</category>
      <author>AlienCoder</author>
      <guid isPermaLink="true">https://aliencoder.tistory.com/193</guid>
      <comments>https://aliencoder.tistory.com/193#entry193comment</comments>
      <pubDate>Tue, 21 Jan 2025 18:52:50 +0900</pubDate>
    </item>
    <item>
      <title>[Python/Java] 백준 1260 - DFS와 BFS [DFS/BFS]</title>
      <link>https://aliencoder.tistory.com/192</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;문제&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/1260&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.acmicpc.net/problem/1260&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;해설&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;알고리즘 문제에서 자주 활용되는 DFS와 BFS 구현에 대한 문제였다. 모든 Vertex(정점)을 방문할 땐 DFS, BFS 모두 사용하여도 무관하지만 경로의 특징에 따라 처리가 되는 문제라면 DFS를 사용하는 것이 유리하다. 반면 최단거리를 구하는 문제는 BFS를 사용하는 것이 유리하다. 깊이 내려가지 않고 가까운 곳에서 탐색할 수 있기 때문이다. 참고로 간선은 Edge라고 표현한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Python&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1737017190904&quot; class=&quot;python&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;from sys import stdin


def dfs(graph: list, start: int, visited: list):
    dfs_result.append(str(start))
    visited[start] = True

    for neighbor in range(1, len(graph[start])):
        if not visited[neighbor] and graph[start][neighbor] == 1:
            dfs(graph, neighbor, visited)


def bfs(graph: list, start: int, visited: list):
    bfs_result.append(str(start))
    visited[start] = True

    queue = [start]
    while queue:
        start = queue.pop(0)
        for neighbor in range(1, len(graph[start])):
            if not visited[neighbor] and graph[start][neighbor] == 1:
                bfs_result.append(str(neighbor))
                visited[neighbor] = True
                queue.append(neighbor)


n, m, v = map(int, stdin.readline().split(&quot; &quot;))
graph = [[0 for _ in range(n+1)] for _ in range(n+1)]
for _ in range(m):
    v1, v2 = map(int, stdin.readline().split(&quot; &quot;))
    graph[v1][v2] = 1
    graph[v2][v1] = 1

visited = [False for _ in range(n+1)]
dfs_result = []
dfs(graph, v, visited)
print(&quot; &quot;.join(dfs_result))

visited = [False for _ in range(n+1)]
bfs_result = []
bfs(graph, v, visited)
print(&quot; &quot;.join(bfs_result))&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Java&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1737017205334&quot; class=&quot;java&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.io.*;
import java.util.*;

public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringBuilder sb = new StringBuilder();

        String[] condition = br.readLine().split(&quot; &quot;);
        int n = Integer.parseInt(condition[0]);
        int m = Integer.parseInt(condition[1]);
        int v = Integer.parseInt(condition[2]);

        int[][] graph = new int[n+1][n+1];
        int[] visited = new int[n+1];
        for(int i=0; i&amp;lt;m; i++){
            String[] edge = br.readLine().split(&quot; &quot;);
            int v1 = Integer.parseInt(edge[0]);
            int v2 = Integer.parseInt(edge[1]);
            graph[v1][v2] = 1;
            graph[v2][v1] = 1;
        }

        for(int i=0; i&amp;lt;n+1; i++){ visited[i] = 0; }
        dfs(graph, v, visited, sb);
        System.out.print(sb.append(&quot;\n&quot;).toString());

        sb.setLength(0);

        for(int i=0; i&amp;lt;n+1; i++){ visited[i] = 0; }
        bfs(graph, v, visited, sb);
        System.out.print(sb.append(&quot;\n&quot;).toString());
    }

    public static void dfs(int[][] graph, int start, int[] visited, StringBuilder sb) {
        sb.append(start).append(&quot; &quot;);
        visited[start] = 1;

        for(int neighbor=1; neighbor&amp;lt;graph[start].length; neighbor++){
            if(visited[neighbor]==0 &amp;amp;&amp;amp; graph[start][neighbor]==1){
                dfs(graph, neighbor, visited, sb);
            }
        }
    }

    public static void bfs(int[][] graph, int start, int[] visited, StringBuilder sb) {
        sb.append(start).append(&quot; &quot;);
        visited[start] = 1;
        
        Queue&amp;lt;Integer&amp;gt; q = new LinkedList&amp;lt;&amp;gt;();
        q.add(start);

        while(!q.isEmpty()){
            start = q.poll();
            for(int neighbor=1; neighbor&amp;lt;graph[start].length; neighbor++){
                if(visited[neighbor]==0 &amp;amp;&amp;amp; graph[start][neighbor]==1){
                    sb.append(neighbor).append(&quot; &quot;);
                    visited[neighbor] = 1;
                    q.add(neighbor);
                }
            }    
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Study/Coding Test</category>
      <author>AlienCoder</author>
      <guid isPermaLink="true">https://aliencoder.tistory.com/192</guid>
      <comments>https://aliencoder.tistory.com/192#entry192comment</comments>
      <pubDate>Tue, 21 Jan 2025 13:44:53 +0900</pubDate>
    </item>
    <item>
      <title>[논문 리뷰] Sequence to Sequence Learning with Neural Networks</title>
      <link>https://aliencoder.tistory.com/191</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;논문 링크: &lt;/b&gt;&lt;b&gt;&lt;a href=&quot;https://arxiv.org/abs/1409.3215&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://arxiv.org/abs/1409.3215&lt;/a&gt;&lt;/b&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1737168869112&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Sequence to Sequence Learning with Neural Networks&quot; data-og-description=&quot;Deep Neural Networks (DNNs) are powerful models that have achieved excellent performance on difficult learning tasks. Although DNNs work well whenever large labeled training sets are available, they cannot be used to map sequences to sequences. In this pap&quot; data-og-host=&quot;arxiv.org&quot; data-og-source-url=&quot;https://arxiv.org/abs/1409.3215&quot; data-og-url=&quot;https://arxiv.org/abs/1409.3215v3&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cP8MXm/hyX4sXt9s9/O8g0s9pE6hckKoMjG3v61K/img.png?width=1200&amp;amp;height=700&amp;amp;face=0_0_1200_700,https://scrap.kakaocdn.net/dn/f6el0/hyX4u8PzYm/TJH4kiMqNRBQlrC8Yor1Rk/img.png?width=1000&amp;amp;height=1000&amp;amp;face=0_0_1000_1000&quot;&gt;&lt;a href=&quot;https://arxiv.org/abs/1409.3215&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://arxiv.org/abs/1409.3215&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cP8MXm/hyX4sXt9s9/O8g0s9pE6hckKoMjG3v61K/img.png?width=1200&amp;amp;height=700&amp;amp;face=0_0_1200_700,https://scrap.kakaocdn.net/dn/f6el0/hyX4u8PzYm/TJH4kiMqNRBQlrC8Yor1Rk/img.png?width=1000&amp;amp;height=1000&amp;amp;face=0_0_1000_1000');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Sequence to Sequence Learning with Neural Networks&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Deep Neural Networks (DNNs) are powerful models that have achieved excellent performance on difficult learning tasks. Although DNNs work well whenever large labeled training sets are available, they cannot be used to map sequences to sequences. In this pap&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;arxiv.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;1. 서론&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1.1 논문 선정 이유&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자연어 처리 분야에서 가장 강력한 모델로 평가받는 Attention 매커니즘과 transformer의 토대가 된 논문으로 알고 있다. 가장 최신의 기술을 습득하기 위해 가장 기초적인 부분에 대해 이해하는 것이 중요하다 생각하여 해당 논문을 선정하게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;2. 논문 요약&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2.1&amp;nbsp;논문&amp;nbsp;제목&amp;nbsp;및&amp;nbsp;저자&amp;nbsp;정보&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;제목&lt;/b&gt;:&amp;nbsp;Sequence&amp;nbsp;to&amp;nbsp;Sequence&amp;nbsp;Learning&amp;nbsp;with&amp;nbsp;Neural&amp;nbsp;Networks&lt;/li&gt;
&lt;li&gt;&lt;b&gt;저자&lt;/b&gt;:&amp;nbsp;Ilya&amp;nbsp;Sutskever,&amp;nbsp;Oriol&amp;nbsp;Vinyals,&amp;nbsp;and&amp;nbsp;Quoc&amp;nbsp;V.&amp;nbsp;Le&lt;/li&gt;
&lt;li&gt;&lt;b&gt;출처&lt;/b&gt;:&amp;nbsp;Advances&amp;nbsp;in&amp;nbsp;Neural&amp;nbsp;Information&amp;nbsp;Processing&amp;nbsp;Systems&amp;nbsp;(NeurIPS),&amp;nbsp;2014&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2.2 연구 배경 및 목적&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존의&amp;nbsp;RNN&amp;nbsp;기반&amp;nbsp;자연어&amp;nbsp;처리&amp;nbsp;및&amp;nbsp;기계&amp;nbsp;번역&amp;nbsp;시스템은&amp;nbsp;이전의&amp;nbsp;규칙&amp;nbsp;기반&amp;nbsp;시스템이나&amp;nbsp;통계적&amp;nbsp;모델에&amp;nbsp;비해&amp;nbsp;가변&amp;nbsp;길이&amp;nbsp;입력과&amp;nbsp;출력&amp;nbsp;처리가&amp;nbsp;가능했으나,&amp;nbsp;&lt;b&gt;긴&amp;nbsp;시퀀스의&amp;nbsp;학습과&amp;nbsp;정보&amp;nbsp;손실&amp;nbsp;문제&lt;/b&gt;에서&amp;nbsp;성능의&amp;nbsp;한계가&amp;nbsp;있었다.&amp;nbsp;특히&amp;nbsp;입력과&amp;nbsp;출력의&amp;nbsp;길이가&amp;nbsp;크게&amp;nbsp;다르거나&amp;nbsp;복잡한&amp;nbsp;맥락이&amp;nbsp;요구되는&amp;nbsp;경우,&amp;nbsp;기존&amp;nbsp;RNN&amp;nbsp;구조는&amp;nbsp;효율적으로&amp;nbsp;학습하거나&amp;nbsp;처리하는&amp;nbsp;데&amp;nbsp;어려움이&amp;nbsp;있었고,&amp;nbsp;이를&amp;nbsp;개선하기&amp;nbsp;위한&amp;nbsp;추가적인&amp;nbsp;설계가&amp;nbsp;필요하였다.&lt;br /&gt;&lt;br /&gt;이 논문은 뉴럴 네트워크를 기반으로 한 새로운 Sequence-to-Sequence(Seq2Seq) 학습 프레임워크를 제안하여, 입력 시퀀스를 고정된 길이의 벡터로 인코딩하고 이를 다시 출력 시퀀스로 디코딩하는 방식을 통해 기존의 한계를 극복하고자 한다. 특히, 기계 번역과 같은 복잡한 시퀀스 변환 작업에서 뛰어난 성능을 보이는 모델을 설계하고, 학습 가능한 뉴럴 네트워크의 가능성을 입증하는 것이 주요 목표이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2.3 Contributions&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Seq2Seq 모델 구조 제안&lt;/b&gt;&lt;br /&gt;: &lt;b&gt;RNN(Recurrent Neural Network)&lt;/b&gt;을 기반으로 입력 시퀀스를 고정된 벡터 표현으로 압축하는 &lt;b&gt;Encoder&lt;/b&gt;와, 이를 기반으로 출력 시퀀스를 생성하는 &lt;b&gt;Decoder&lt;/b&gt; 구조 설계&lt;/li&gt;
&lt;li&gt;&lt;b&gt;LSTM(Long&amp;nbsp;Short-Term&amp;nbsp;Memory)의&amp;nbsp;활용&lt;/b&gt;&lt;br /&gt;: 기존 RNN의 장기 의존성 문제를 해결하기 위해, LSTM 셀을 Seq2Seq 모델에 적용하여 더 안정적이고 효과적인 학습이 가능하도록 개선&lt;/li&gt;
&lt;li&gt;&lt;b&gt;기계&amp;nbsp;번역의&amp;nbsp;성능&amp;nbsp;향상&lt;/b&gt;&lt;br /&gt;: 영어-프랑스어 기계 번역에서 BLEU 점수를 기존 모델 대비 대폭 개선하여 Seq2Seq 모델의 실제 응용 가능성 입증&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Variable-length(가변 길이) 시퀀스 처리&lt;/b&gt;&lt;br /&gt;: 입력 및 출력 시퀀스의 길이가 가변적인 문제를 해결할 수 있는 학습 구조를 제안하여, 다양한 자연어 처리 및 시퀀스 기반 문제로의 확장 가능성을 보여줌&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Attention의 토대&lt;/b&gt;&lt;br /&gt;: 초기 Seq2Seq 모델은 이전에 비해 기계 번역에서 높은 성능을 보여주었고, 이후 발표된 Attention 논문의 기초를 마련함&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;3. 핵심 내용 분석&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3.1&amp;nbsp;방법론&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;864&quot; data-origin-height=&quot;190&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/de2H1B/btsLSSVzlIL/8jrlFbVJc96z6Jc1TXlJn0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/de2H1B/btsLSSVzlIL/8jrlFbVJc96z6Jc1TXlJn0/img.png&quot; data-alt=&quot;Fig 1. seq2seq with LSTM&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/de2H1B/btsLSSVzlIL/8jrlFbVJc96z6Jc1TXlJn0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fde2H1B%2FbtsLSSVzlIL%2F8jrlFbVJc96z6Jc1TXlJn0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;760&quot; height=&quot;167&quot; data-origin-width=&quot;864&quot; data-origin-height=&quot;190&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Fig 1. seq2seq with LSTM&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;699&quot; data-origin-height=&quot;198&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c2xCfO/btsLSfxf1JI/xfVqDh7v5Qk9EfkkGEWFsK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c2xCfO/btsLSfxf1JI/xfVqDh7v5Qk9EfkkGEWFsK/img.png&quot; data-alt=&quot;Fig 2. seq2seq translation example&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c2xCfO/btsLSfxf1JI/xfVqDh7v5Qk9EfkkGEWFsK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc2xCfO%2FbtsLSfxf1JI%2FxfVqDh7v5Qk9EfkkGEWFsK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;753&quot; height=&quot;213&quot; data-origin-width=&quot;699&quot; data-origin-height=&quot;198&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Fig 2. seq2seq translation example&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;385&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/EjhDr/btsLRLC9tKa/dIHTsOeI8czDaBGRJJR3R0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/EjhDr/btsLRLC9tKa/dIHTsOeI8czDaBGRJJR3R0/img.png&quot; data-alt=&quot;Fig 3. seq2seq question answering example&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/EjhDr/btsLRLC9tKa/dIHTsOeI8czDaBGRJJR3R0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FEjhDr%2FbtsLRLC9tKa%2FdIHTsOeI8czDaBGRJJR3R0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;753&quot; height=&quot;226&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;385&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Fig 3. seq2seq question answering example&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;Our model reads an input sentence &amp;ldquo;ABC&amp;rdquo; and produces &amp;ldquo;WXYZ&amp;rdquo; as the output sentence. The model stops making predictions after outputting the end-of-sentence token. Note that the LSTM reads the input sentence in reverse, because doing so introduces many short term dependencies in the data that make the optimization problem much easier&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Fig 1의 설명은 모델이 입력 문장을 역순으로 읽는 방식과 관련이 있다. 프랑스어 문장을 영어로 번역한다고 가정해보자. 그렇다면 &lt;b&gt;Encoder&lt;/b&gt; 부분에서 프랑스어 입력 데이터를 역순으로 제공하고, 출력 부분은 영어 데이터를 정방향으로 제공하여 &lt;b&gt;LSTM Decoder&lt;/b&gt;는 순차적으로 단어를 예측한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어, &quot;Bonjour [EOS]&quot;는 &quot;ruojnoB [EOS]&quot;로 뒤집힌 형태로 &lt;b&gt;Encoder&lt;/b&gt;에 전달하고, &lt;b&gt;Decoder&lt;/b&gt;는 &quot;Hello [EOS]&quot;를 순서대로 생성하는 것이다. 각 입력과 출력에서 &lt;b&gt;End-of-Sentence(EOS) Token&lt;/b&gt;을 사용한다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;EOS 토큰을 사용하는 이유는 출력의 종료 시점을 명확히 정의하여 모델이 불필요한 단어 예측을 피하고, 가변 길이의 문장을 처리할 수 있도록 하기 위해서이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모델이 입력 문장을 역순으로 읽는 것이 정말 도움이 되는가에 대한 의문이 생길 수 있다. 입력을 역순으로 바꾸어 사용하는 이유는 다음과 같다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫째로 &lt;b&gt;장기 종속성&lt;/b&gt;을 개선시키기 위함이다. 입력 문장을 역순으로 읽음으로써, 입력 문장의 첫 단어가 목표 문장의 첫 단어와 가까워지고, 전체적으로 데이터의 최적화 문제가 단순해지기 때문에 모델이 더 효과적으로 학습할 수 있도록 돕는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;두 번째 이유는 &lt;b&gt;최적화 문제의 단순화&lt;/b&gt;하기 위해서이다. LSTM이 입력 문장을 역순으로 읽을 때, 입력의 각 단어가 목표 단어에 대해 가까운 위치에 위치하게 되어, Backpropagation 과정에서 모델이 더 쉽게 정보를 학습하고 전달할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래에서 위와 같은 주요 개념을 이용하여 실험 진행에 대해 하나씩 살펴보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;(a) Dataset details&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용 데이터는 WMT'14 영어-프랑스어 데이터셋의 하위 집합인 12M 문장(348M 프랑스어 단어와 304M 영어 단어)을 사용하였다. 소스 언어의 160,000개 가장 빈번한 단어와 타겟 언어의 80,000개 가장 빈번한 단어를 고정된 어휘로 사용하고, OOV(Out-of-Vocabulary) 단어는 &quot;UNK&quot; 토큰으로 대체해서 학습 데이터로 사용하였다. 공개된 토큰화된 트레이닝 및 테스트 세트와 SMT의 1000-best 리스트가 제공된 점을 활용하여 모델을 훈련하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;(b) Decoding and Rescoring&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Decoding&lt;/b&gt;은 주로 번역 과정 전반에서의 &lt;b&gt;단어 생성 과정&lt;/b&gt;을 의미하며, Decoder는 그 과정에서 실제로 단어를 생성하는 모델의 구성 요소를 의미한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문장 번역은 LSTM 모델을 통해 문장 Pair(쌍)를 학습하며, 문장의 log 확률을 최대화하는 것을 목표로 하였다. &lt;b&gt;Beam Search&lt;/b&gt;를 이용해 단어 생성 과정에서 특정 개수의 가능한 가설을 유지하고, 가능한 가장 높은 확률을 가진 가설을 선택하는 방식을 사용하였다. 여기서 Beam&amp;nbsp;Search란 시퀀스&amp;nbsp;생성을&amp;nbsp;위해&amp;nbsp;사용되는&amp;nbsp;알고리즘으로,&amp;nbsp;확률이&amp;nbsp;높은&amp;nbsp;가능한&amp;nbsp;단어들&amp;nbsp;중에서&amp;nbsp;특정&amp;nbsp;수의&amp;nbsp;후보를&amp;nbsp;유지하며&amp;nbsp;단어를&amp;nbsp;확장해&amp;nbsp;나가는&amp;nbsp;방식이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 &lt;b&gt;Statistical Machine Translation(SMT)&lt;/b&gt;의 1000-best 리스트를 LSTM으로 재평가하여 확률을 평균화하였다. SMT란 시스템이 생성한 가장 가능한 1000개의 번역 가설 목록을 의미하는데, 이 리스트에서 각 번역 가설에 대해 LSTM 모델을 사용하여 확률을 다시 평가(Rescoring)하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;(c) Reversing the Source Sentences(문장 뒤집기)&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;소스 문장을 역순으로 뒤집으면 문장의 단기 종속성을 증가시키며, LSTM의 perplexity가 감소하고 BLEU 점수가 향상되는 결과가 도출되었다. 이는 역순으로 뒤집음으로써 초기 단어가 타겟 문장과 가까워져 장기 의존성을 더 잘 학습할 수 있게 되며, backpropagation이 더 효과적으로 작동하였기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;(d) Training details&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모델은 Deep LSTM Network(4 레이어, 각 레이어 1000 셀)와 1000 차원의 단어 임베딩 사용. 학습된 LSTM은 384M 매개변수로 구성되며, 64M은 순환 연결로 구성되어 있다. SGD를 사용하며, 학습률을 단계적으로 감소시키며 7.5 epochs 동안 훈련 진행하였고, 8개의 GPU를 활용하여 LSTM을 병렬화하여 빠른 처리가 가능했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;(e) Parallelization&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각각의 LSTM 레이어가 다른 GPU에 배치되며, softmax도 병렬화하여 초당 6,300단어를 훈련하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3.2&amp;nbsp;실험&amp;nbsp;결과&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;856&quot; data-origin-height=&quot;294&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/GpkFh/btsM13hyJ3n/f1R2Je8RlzlXMw9UVUvUuk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/GpkFh/btsM13hyJ3n/f1R2Je8RlzlXMw9UVUvUuk/img.png&quot; data-alt=&quot;Table 1: The performance of the LSTM on WMT&amp;amp;rsquo;14 English to French test set (ntst14). Note that an ensemble of 5 LSTMs with a beam of size 2 is cheaper than of a single LSTM with a beam of size 12.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/GpkFh/btsM13hyJ3n/f1R2Je8RlzlXMw9UVUvUuk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGpkFh%2FbtsM13hyJ3n%2Ff1R2Je8RlzlXMw9UVUvUuk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;638&quot; height=&quot;219&quot; data-origin-width=&quot;856&quot; data-origin-height=&quot;294&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Table 1: The performance of the LSTM on WMT&amp;rsquo;14 English to French test set (ntst14). Note that an ensemble of 5 LSTMs with a beam of size 2 is cheaper than of a single LSTM with a beam of size 12.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1093&quot; data-origin-height=&quot;271&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bkop7h/btsM0JEBx7P/Xo57lFx3bHoo4JkKcUzk5k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bkop7h/btsM0JEBx7P/Xo57lFx3bHoo4JkKcUzk5k/img.png&quot; data-alt=&quot;Table 2: Methods that use neural networks together with an SMT system on the WMT&amp;amp;rsquo;14 English to French test set (ntst14).&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bkop7h/btsM0JEBx7P/Xo57lFx3bHoo4JkKcUzk5k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbkop7h%2FbtsM0JEBx7P%2FXo57lFx3bHoo4JkKcUzk5k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;642&quot; height=&quot;159&quot; data-origin-width=&quot;1093&quot; data-origin-height=&quot;271&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Table 2: Methods that use neural networks together with an SMT system on the WMT&amp;rsquo;14 English to French test set (ntst14).&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;영어-프랑스어 번역 데이터셋에서 BLEU 점수를 기존 모델 대비 대폭 개선&lt;/li&gt;
&lt;li&gt;입력 및 출력 시퀀스의 길이가 가변적인 상황에서도 높은 성능을 기록&lt;/li&gt;
&lt;li&gt;제한된 데이터셋에서도 효율적인 학습 성능을 보임&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3.3 결론 &lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Seq2Seq 모델은 입력과 출력 시퀀스 길이가 다르거나 가변적인 문제를 효과적으로 처리할 수 있는 학습 구조를 제공&lt;/li&gt;
&lt;li&gt;LSTM 기반의 Encoder-Decoder 구조는 자연어 처리 및 기계 번역의 성능을 크게 향상 시킴&lt;/li&gt;
&lt;li&gt;Seq2Seq는&amp;nbsp;이후&amp;nbsp;Attention&amp;nbsp;메커니즘&amp;nbsp;및&amp;nbsp;다양한&amp;nbsp;시퀀스&amp;nbsp;변환&amp;nbsp;작업의&amp;nbsp;기초를&amp;nbsp;마련한&amp;nbsp;핵심적인&amp;nbsp;연구임&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;4. 장점과 한계 &lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;4.1&amp;nbsp;연구의&amp;nbsp;장점&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;본 연구는 LSTM 모델을 사용하여 시퀀스 번역에서 단기 종속성을 개선하고, 단어 순서를 역순으로 하여 더 나은 성능을 달성&lt;/li&gt;
&lt;li&gt;SMT와의 결합을 통해 1000-best 리스트를 활용하여 더욱 정교한 번역 확률 평가가 가능&lt;/li&gt;
&lt;li&gt;LSTM 모델이 심층화됨에 따라 더 많은 정보를 학습하고 긴 문장에서도 효과적으로 장기 의존성 학습 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;4.2&amp;nbsp;연구의&amp;nbsp;한계와&amp;nbsp;개선&amp;nbsp;가능성 &lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LSTM 모델은 긴 문장에서의 기억 최적화 문제에 여전히 취약하며, 이를 개선하기 위해 더 복잡한 메커니즘이 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 역순 시퀀스를 사용한 학습이 단기 종속성에 긍정적인 영향을 미치지만, 전체적인 성능을 향상시키기 위해 더 다양한 데이터 처리 방법이 필요할 수 있다. &lt;br /&gt;마지막으로 SMT와 LSTM의 결합은 시간이 많이 소요되며, 하드웨어 자원을 많이 사용한다는 점에서 병렬화 및 효율화가 필요하다고 생각된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;5. 관련 연구 및 응용&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;5.1 실질적 응용 가능성 &lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LSTM 모델과 SMT의 결합은 다양한 자연어 처리(NLP) 응용 분야에서 활용될 수 있다. 예를 들어, 번역 시스템 외에도 텍스트 요약, 문서 자동 분류, 다중 언어 텍스트 처리 등에서 성능 개선이 기대된다. 또한,&amp;nbsp;의료,&amp;nbsp;법률&amp;nbsp;등&amp;nbsp;특정&amp;nbsp;도메인에서&amp;nbsp;LSTM을&amp;nbsp;활용한&amp;nbsp;모델은&amp;nbsp;높은&amp;nbsp;정확도를&amp;nbsp;제공할&amp;nbsp;수&amp;nbsp;있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Appendix(&lt;/b&gt;&lt;b&gt;추가 참고 문헌 등)&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Recurrent Continuous Translation Models: &lt;a href=&quot;https://aclanthology.org/D13-1176.pdf&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://aclanthology.org/D13-1176.pdf&lt;/a&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Learning Phrase Representations using RNN Encoder&amp;ndash;Decoder for Statistical Machine Translation: &lt;a href=&quot;https://arxiv.org/abs/1406.1078&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://arxiv.org/abs/1406.1078&lt;/a&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;NEURAL MACHINE TRANSLATION BY JOINTLY LEARNING TO ALIGN AND TRANSLATE: &lt;a href=&quot;https://arxiv.org/abs/1409.0473&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://arxiv.org/abs/1409.0473&lt;/a&gt;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://velog.io/@khs0415p/NLP-Seq2Seq&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://velog.io/@khs0415p/NLP-Seq2Seq&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://velog.io/@bailando/Sequence-to-Sequence&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://velog.io/@bailando/Sequence-to-Sequence&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Study/Literature Review</category>
      <category>seq2seq</category>
      <author>AlienCoder</author>
      <guid isPermaLink="true">https://aliencoder.tistory.com/191</guid>
      <comments>https://aliencoder.tistory.com/191#entry191comment</comments>
      <pubDate>Sat, 18 Jan 2025 16:25:12 +0900</pubDate>
    </item>
  </channel>
</rss>