<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>민성 Skystarry의 연구 창고</title>
    <link>https://skystarry.tistory.com/</link>
    <description>현재 AI/ML에 몰두하고 있는 10년차 청소년 개발자입니다.</description>
    <language>ko</language>
    <pubDate>Sat, 4 Jul 2026 12:13:38 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>민성 Skystarry</managingEditor>
    <image>
      <title>민성 Skystarry의 연구 창고</title>
      <url>https://tistory1.daumcdn.net/tistory/8485284/attach/8613c8fb1edc417db62653afb23f2d22</url>
      <link>https://skystarry.tistory.com</link>
    </image>
    <item>
      <title>[딥러닝] 트랜스포머 Multi-Head Attention: 여러 개의 눈으로 세상을 보는 법</title>
      <link>https://skystarry.tistory.com/4</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;a href=&quot;https://skystarry.tistory.com/3&quot;&gt;지난 글&lt;/a&gt;에서 이어집니다.&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;Attention을 안다&quot;고 말하는 사람들이 많습니다. 하지만 $Q, K, V$의 개념을 넘어, 왜 굳이 '여러 개의 머리(Multi-Head)'를 달아야 했는지 그 설계 철학까지 깊게 고민해 본 사람은 많지 않습니다.&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;b&gt;'공짜 점심'의 마법&lt;/b&gt;에 대해 이야기해 보겠습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;하나의 시선이 가진 한계&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;잠시 $d_{model}=512$인 단일 헤드(Single-Head) 어텐션을 상상해 봅시다. 문장을 읽을 때, 하나의 단어는 문맥 속에서 여러 단어와 동시에 복잡한 관계를 맺습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&quot;그 동물은 길을 건너지 않았다. 왜냐하면 그것은 너무 피곤했기 때문이다.&quot;&lt;/b&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 &lt;b&gt;'그것(it)'&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;&lt;b&gt;참조 관계:&lt;/b&gt; '그것'이 가리키는 대상이 '그 동물'임을 알아야 합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;상태 관계:&lt;/b&gt; '피곤하다'는 형용사가 '그것'의 상태임을 연결해야 합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;행위 관계:&lt;/b&gt; '건너지 않았다'는 동사의 주체가 '그것'임을 파악해야 합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 어텐션 헤드가 하나라면, 이 모든 관계를 하나의 확률 분포(Attention Score)로 뭉뚱그려야 합니다. '동물'에도 좀 집중하고, '피곤함'에도 좀 집중해서 평균을 내버리는 식이죠. &lt;b&gt;결국 정보가 섞이고 희석됩니다.&lt;/b&gt; 문법적 관계를 보는 눈과 의미론적 관계를 보는 눈이 분리되지 않은 채 흐릿해지는 셈입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;해결책: 역할을 쪼개자 (Subspace Representation)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;트랜스포머의 저자들은 이 문제를 해결하기 위해 &lt;b&gt;Representation Subspace(표현 부분 공간)&lt;/b&gt;이라는 개념을 도입합니다. 쉽게 말해 &lt;b&gt;&quot;서로 다른 관점&quot;&lt;/b&gt;을 동시에 유지하는 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리는 전체 차원($d_{model}$)이 고정되어 있다는 전제하에, 이를 $h$개의 헤드로 쪼갭니다. 보통 8개의 헤드를 사용하니, 각 헤드는 $512 / 8 = 64$ 차원을 갖게 됩니다. 이제 8개의 헤드는 각자의 역할에 집중합니다.&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;Head 1:&lt;/b&gt; 주어-동사 관계에만 집중 (문법 담당)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Head 2:&lt;/b&gt; 대명사가 지칭하는 대상 찾기 (참조 담당)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Head 3:&lt;/b&gt; 시제나 장소 정보 파악 (배경 담당)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나중에 이 결과들을 다시 합치면(Concat), '그것'이라는 단어는 문법적 정보와 의미적 정보를 모두 뚜렷하게 가진 강력한 벡터가 됩니다. &lt;b&gt;정보를 섞지 않고 각자의 고유한 특징을 보존하는 것&lt;/b&gt;, 이것이 Multi-Head의 핵심인 '다양성(Diversity)'입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;연산량의 마법: 공짜 점심은 있다?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 드는 합리적인 의문이 있습니다. &quot;헤드를 8개나 만들면 연산량이 8배로 늘어나는 거 아닌가요?&quot;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 부분이 트랜스포머 설계의 백미입니다. &lt;b&gt;놀랍게도 전체 연산량은 단일 헤드일 때와 거의 같습니다.&lt;/b&gt; 비결은 바로 '차원 쪼개기'에 있습니다. 총 차원($d_{model}$)이 고정되어 있다면, 행렬 연산의 총량은 변하지 않기 때문입니다.&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;Single Head:&lt;/b&gt; $d_{model} \times d_{model}$ 연산 1회&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Multi Head:&lt;/b&gt; $(d_{model} \times d_k)$ 연산 $h$회 (단, $h \times d_k = d_{model}$)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전체 파라미터 수와 연산량(FLOPs)은 비슷하게 유지하면서 표현력만 8배 풍부하게 만든 것입니다. 또한, 이는 모델 내부에서 일종의 &lt;b&gt;앙상블(Ensemble) 효과&lt;/b&gt;를 냅니다. 특정 헤드가 실수로 이상한 곳에 집중하더라도, 나머지 헤드들이 올바른 정보를 잡고 있다면 최종 결과물에서는 노이즈가 상쇄됩니다. 모델의 &lt;b&gt;강건성(Robustness)&lt;/b&gt;을 높이고 과적합을 방지하는 아주 영리한 안전장치인 셈입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;마치며: 완벽해 보이는 MHA의 '비싼' 뒷모습&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Multi-Head Attention은 분명 혁신적입니다. 하지만 이 '공짜 점심'은 연산량에만 해당할 뿐, 실제 서비스를 운영하는 엔지니어들에게는 또 다른 고민거리를 안겨주었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;바로 &lt;b&gt;KV 캐시(KV Cache)의 효율성&lt;/b&gt; 때문입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MHA 구조에서는 &lt;b&gt;모든 쿼리(Query) 헤드가 각자 독립적인 Key와 Value 헤드를 가져야만 합니다.&lt;/b&gt; 즉, 헤드가 8개라면 문장 하나를 처리할 때도 8개 세트의 K, V 데이터를 메모리에 유지해야 하죠. 문장이 길어질수록 이 중복된 듯한 데이터가 VRAM을 가득 채우며 '메모리 병목'을 일으키게 됩니다. 배치 사이즈를 키우고 싶어도 이 메모리 점유율 때문에 발목을 잡히게 되죠.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 최근 Llama 3나 Mistral 같은 최신 거대 언어 모델(LLM)들은 순수한 MHA의 비효율을 개선한 변형 구조를 사용합니다.&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;MQA(Multi-Query Attention)&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;성능과 효율의 황금 밸런스를 찾은 &lt;b&gt;GQA(Grouped-Query Attention)&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;긴 문맥도 가볍게 처리하는 &lt;b&gt;SWA(Sliding Window Attention)&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지난 글에서 &lt;b&gt;RoPE&lt;/b&gt;를 언급하며 현대적 트랜스포머의 서막을 알렸듯, 이 기법들 역시 나중에 다룰 &lt;b&gt;[고급] 현대 트랜스포머 시리즈&lt;/b&gt;에서 아주 깊게 파헤쳐 볼 예정입니다.&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;b&gt;Feed Forward Network (FFN)&lt;/b&gt;와 깊은 층을 쌓게 해주는 일등공신 &lt;b&gt;Add &amp;amp; Norm&lt;/b&gt;을 다루겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 다음 글에서 뵙겠습니다!&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;해당 게시글은 Gemini 3 Flash를 이용해 작업하였음을 알립니다.&lt;/i&gt;&lt;/p&gt;</description>
      <category>트랜스포머 시리즈</category>
      <category>GQA</category>
      <category>LLM</category>
      <category>MQA</category>
      <category>multi-head attention</category>
      <category>nlp</category>
      <category>딥러닝</category>
      <category>어텐션</category>
      <category>트랜스포머</category>
      <author>민성 Skystarry</author>
      <guid isPermaLink="true">https://skystarry.tistory.com/4</guid>
      <comments>https://skystarry.tistory.com/4#entry4comment</comments>
      <pubDate>Mon, 12 Jan 2026 19:39:34 +0900</pubDate>
    </item>
    <item>
      <title>[딥러닝] 트랜스포머의 나침반: Positional Encoding</title>
      <link>https://skystarry.tistory.com/3</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://skystarry.tistory.com/2&quot;&gt;지난 글&lt;/a&gt;에서 우리는 트랜스포머가 RNN의 재귀(Recurrence)를 과감히 버리고 병렬화라는 무기를 얻었다고 이야기했습니다.&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;p data-ke-size=&quot;size16&quot;&gt;모델 입장에서는 &quot;아버지가 방에 들어가신다&quot;와 &quot;아버지 가방에 들어가신다&quot;가 그저 똑같은 단어 카드 뭉치로 보입니다. 모든 토큰을 한꺼번에 병렬로 처리하기 때문입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 우리는 단어 벡터에 &lt;b&gt;&quot;네 위치는 여기야!&quot;&lt;/b&gt;라는 이정표를 심어줘야 합니다. 이것이 바로 &lt;b&gt;Positional Encoding(PE)&lt;/b&gt;입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;왜 결합(Concat)이 아니라 더하기(Add)인가?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PE를 처음 접할 때 아주 가끔, 하지만 꽤나 근본적인 질문을 던지는 분들이 있습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;정보 오염을 막으려면 단어 벡터 옆에 위치 벡터를 붙여버리면(Concatenate) 되잖아요? 왜 굳이 더해서(Add) 정보를 섞어버리나요?&quot;&lt;/p&gt;
&lt;/blockquote&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;1. 텐서 규격의 파괴 (Dimension Mismatch)&lt;/b&gt;&lt;br /&gt;트랜스포머의 모든 가중치 행렬($W^Q, W^K, W^V$)은 $d_{model}$(예: 512)이라는 고정된 차원에 맞춰 정교하게 설계되어 있습니다. 만약 위치 정보를 보존하겠다고 옆에 512차원을 더 붙여 1024차원을 만드는 순간, 모든 레이어 설계를 통째로 갈아엎어야 합니다. 배보다 배꼽이 더 커지는 꼴이죠.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. 위치 정보가 '입혀지지' 않는다&lt;/b&gt;&lt;br /&gt;사실 이게 더 본질적인 문제입니다. 결합(Concat)은 위치 정보를 단어 옆에 '나열'할 뿐, 단어 벡터에 위치 속성을 &lt;b&gt;'주입'&lt;/b&gt;하지 못합니다. 연구적 관점에서 보자면, 우리는 단어 자체가 특정 위치에 있을 때의 &lt;b&gt;'상태(State)'&lt;/b&gt;를 모델링하고 싶은 것이지, 단어 옆에 위치 번호표를 따로 붙여두고 모델이 알아서 매칭하길 기다리는 게 아니기 때문입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3. 계산 비용의 낭비 (Parameter Inefficiency)&lt;/b&gt;&lt;br /&gt;만약 512차원 단어 벡터 옆에 512차원 위치 벡터를 붙인다면(Concat), 입력 차원은 1024가 됩니다. 이렇게 되면 모델 내부의 연산량은 제곱비례로 늘어나게 됩니다. 학습 속도는 느려지고 메모리는 더 많이 잡아먹겠죠. 연구진은 차원을 늘리지 않고도($d_{model}$ 유지), 덧셈만으로 위치 정보를 충분히 표현할 수 있음을 증명해 낸 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;그래서 '더하기(Add)'를 선택했습니다.&lt;/b&gt;&lt;br /&gt;연구진은 &lt;b&gt;고차원 공간의 희소성(Sparsity)&lt;/b&gt;을 믿었습니다. 512차원이라는 공간은 생각보다 광활합니다. 단어의 의미 정보를 담은 벡터 위에 위치 정보를 담은 벡터를 더해도($Element-wise Sum$), 두 정보는 충분히 분리될 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마치 흰색 도화지(단어 의미)에 아주 연한 색연필(위치 정보)로 숫자를 적는 것과 같습니다. 본질을 망치지 않으면서도 순서는 알 수 있게 하는, 일종의 &lt;b&gt;'착색(Coloring)'&lt;/b&gt; 기법인 셈입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;사인/코사인(Sinusoidal)의 설계 철학&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구글 연구진은 이 착색 작업을 위해 삼각함수를 가져왔습니다. 단순히 수식이 멋있어 보여서 쓴 게 아닙니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$PE_{(pos, 2i)} = \sin(pos / 10000^{2i/d_{model}})$$&lt;br /&gt;$$PE_{(pos, 2i+1)} = \cos(pos / 10000^{2i/d_{model}})$$&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;b&gt;1. 유일성 (Uniqueness)&lt;/b&gt;&lt;br /&gt;각 차원($i$)마다 주파수(Frequency)가 다릅니다. 이들이 조합되면 &lt;b&gt;모든 위치($pos$)는 겹치지 않는 고유한 벡터 값&lt;/b&gt;을 가지게 됩니다. 마치 시계의 시침, 분침, 초침이 매 순간 다른 각도로 조합되어 시간을 가리키는 것과 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. 상대적 위치 보존 (Relative Positioning)&lt;/b&gt;&lt;br /&gt;이 부분이 가장 중요합니다. 논문 저자들은 모델이 절대적인 위치보다 &lt;b&gt;상대적인 거리(예: &quot;이 단어는 저 단어보다 $k$칸 뒤에 있다&quot;)&lt;/b&gt;를 학습하길 원했습니다. 사인/코사인의 덧셈 정리에 의해, $pos+k$ 위치의 인코딩 벡터는 $pos$ 위치의 벡터에 &lt;b&gt;선형 변환(Linear Transformation)&lt;/b&gt;을 가한 형태로 표현될 수 있기 때문입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모델은 $QK^T$ 내적 연산을 통해 &lt;b&gt;&quot;아, 얘네 둘은 각도가 이만큼 차이 나니 거리가 이 정도겠구나&quot;&lt;/b&gt;라는 거리감을 수학적으로 자연스럽게 익히게 됩니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&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;가장 큰 문제는 &lt;b&gt;외삽(Extrapolation)&lt;/b&gt;입니다. Sinusoidal PE는 고정된 함수입니다. 만약 학습 때 최대 길이가 512토큰이었다면, 추론 때 1000번째 토큰이 들어왔을 때 모델은 당황합니다. 주기 함수니까 값은 나오겠지만, 모델이 학습하지 못한 '거리감'이기 때문입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최근의 LLaMA 같은 거대 모델들은 이 한계를 극복하기 위해 벡터를 더하는 대신 공간 자체를 회전시켜버리는 &lt;b&gt;RoPE(Rotary Positional Embedding)&lt;/b&gt; 같은 방식을 사용합니다. 이런 현대적인 기법들은 추후 연재될 &lt;b&gt;&lt;code&gt;[고급] 현대 트랜스포머 시리즈&lt;/code&gt;&lt;/b&gt;에서 아주 깊게 파헤쳐 보겠습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;마치며&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Positional Encoding은 단순한 덧셈이 아닙니다. &lt;b&gt;단어라는 본질에 '순서'라는 상태(State)를 부여하는 과정&lt;/b&gt;입니다. 이로써 트랜스포머는 병렬 처리의 속도와 순차 정보의 디테일을 모두 잡을 수 있었습니다.&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;a href=&quot;https://skystarry.tistory.com/4&quot;&gt;&lt;b&gt;Multi-Head Attention&lt;/b&gt;&lt;/a&gt;의 진짜 얼굴을 파헤쳐 보겠습니다.&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;i&gt;해당 게시글은 Gemini 3 Flash를 이용해 작업하였음을 알립니다.&lt;/i&gt;&lt;/p&gt;</description>
      <category>트랜스포머 시리즈</category>
      <category>nlp</category>
      <category>positional encoding</category>
      <category>Transformer</category>
      <category>트랜스포머</category>
      <author>민성 Skystarry</author>
      <guid isPermaLink="true">https://skystarry.tistory.com/3</guid>
      <comments>https://skystarry.tistory.com/3#entry3comment</comments>
      <pubDate>Mon, 12 Jan 2026 19:39:16 +0900</pubDate>
    </item>
    <item>
      <title>[딥러닝] 트랜스포머 Attention 완벽 이해: Q, K, V의 역할과 원리</title>
      <link>https://skystarry.tistory.com/2</link>
      <description>&lt;p&gt;&amp;quot;Attention을 안다&amp;quot;고 말하는 사람들이 많습니다. Transformer 논문도 읽었고, 코드도 돌려봤고, 심지어 직접 구현까지 해봤다고 합니다.&lt;/p&gt;
&lt;p&gt;하지만 막상 물어보면 대답이 막힙니다.&lt;/p&gt;
&lt;p&gt;$Q, K, V$가 왜 세 개로 나뉘는지, $K$를 전치하는 이유가 뭔지, Attention Score만으로는 왜 안 되는지. &lt;strong&gt;정확히 설명할 수 있는 사람은 드뭅니다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;이 글은 어텐션 메커니즘의 설계 철학을 다룹니다. &lt;strong&gt;수식과 코드는 최소한으로, 원리 자체에 집중합니다.&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;왜 이런 설계가 필요했는가&lt;/h2&gt;
&lt;p&gt;Attention을 이해하려면 역사적 맥락을 알아야 합니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;RNN의 한계&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Transformer 이전, 시퀀스 처리는 RNN과 LSTM의 영역이었습니다. 하지만 치명적인 문제가 있었습니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;재귀(recurrence) 구조&lt;/strong&gt;입니다. $h_t$를 계산하려면 $h_{t-1}$이 필요하고, $h_{t-1}$을 계산하려면 $h_{t-2}$가 필요합니다. 순차적으로 계산할 수밖에 없는 구조죠.&lt;/p&gt;
&lt;p&gt;이는 두 가지 문제를 일으켰습니다:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;GPU 병렬화 불가능&lt;/strong&gt; → 학습 느림&lt;/li&gt;
&lt;li&gt;먼 거리의 토큰 관계 학습 어려움 → gradient vanishing&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;이에 대한 Attention의 해답&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Attention은 재귀를 제거했습니다.&lt;/strong&gt; 모든 토큰 쌍의 관계를 한 번에 계산합니다.&lt;/p&gt;
&lt;p&gt;$QK^T$ 연산 하나로 $(\text{seq\_len} \times \text{seq\_len})$ 개의 관계를 동시에 구합니다. &lt;strong&gt;완벽한 병렬화가 가능해진 거죠.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;거리도 무의미해졌습니다. 첫 토큰과 마지막 토큰도 직접 연결됩니다. Long-range dependency 문제가 해결됩니다.&lt;/p&gt;
&lt;p&gt;대신 순차성 정보는 사라졌습니다. 토큰의 위치 정보를 별도로 주입해야 하는데, 이 부분은 다음 글에서 다룰 &lt;strong&gt;Positional Encoding&lt;/strong&gt;이 담당합니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Q, K, V의 역할 분담&lt;/h2&gt;
&lt;p&gt;어텐션을 이해하는 첫 단계는 Query, Key, Value의 역할을 구분하는 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;내적으로 유사도를 측정한다&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;$QK^T$ 연산은 토큰 간 유사도를 계산합니다. 여기서 전치($T$)가 붙는 이유를 정확히 알아야 합니다.&lt;/p&gt;
&lt;p&gt;행렬 곱셈 규칙을 떠올려보겠습니다. $(A, B, C) \times (A, C, D) = (A, B, D)$&lt;/p&gt;
&lt;p&gt;$QK^T$ 계산:&lt;/p&gt;
&lt;p&gt;$$ \begin{aligned} &amp;amp;[\text{batch}, \text{seq\_len}, d_{model}] \times [\text{batch}, d_{model}, \text{seq\_len}] \\ &amp;amp;= [\text{batch}, \text{seq\_len}, \text{seq\_len}] \end{aligned} $$&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;가운데 차원인 $d_{model}$끼리 곱해집니다.&lt;/strong&gt; 행렬 곱셈 법칙상 가운데 차원이 소거되면서 내적이 일어나는 구조죠.&lt;/p&gt;
&lt;p&gt;각 토큰 쌍마다 $d_{model}$ 차원의 벡터끼리 내적합니다. 임베딩 정보를 비교하는 방식입니다. &lt;strong&gt;$K$를 전치하지 않으면 차원이 맞지 않아 계산 자체가 불가능합니다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Attention Score의 한계&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;이렇게 계산된 결과만으로 충분할까요?&lt;/p&gt;
&lt;p&gt;계산 결과는 $(\text{batch}, \text{seq\_len}, \text{seq\_len})$ 크기의 Attention Score입니다. Softmax를 거치면 각 토큰이 다른 토큰들에 얼마나 집중할지 확률 분포로 나타납니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;&lt;em&gt;참고: 흔히 어텐션의 연산 비용을 $O(n^2)$ 혹은 $O(\text{seq\_len}^2)$이라고 부르는데, 바로 이 과정에서 만들어지는 행렬의 크기가 시퀀스 길이의 제곱으로 늘어나기 때문입니다.&lt;/em&gt;&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;그런데 여기까지만으로는 부족합니다. &lt;strong&gt;Attention Score는 &amp;quot;누가 중요한지&amp;quot;만 알려줄 뿐입니다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;V가 담당하는 것&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;$(\text{seq\_len}, \text{seq\_len})$ 형태의 스코어에는 관련성만 있습니다. 각 토큰이 원래 어떤 의미였는지는 사라졌습니다.&lt;/p&gt;
&lt;p&gt;$V$는 입력($X$)이 가중치 행렬 $W^V$와 곱해져($V = X W^V$) 생성됩니다.&lt;/p&gt;
&lt;p&gt;즉, $W^V$를 통해 &lt;strong&gt;학습 가능한 변환&lt;/strong&gt;을 거친 정보를 담고 있습니다. 단순히 원본을 보관하는 게 아니라, &lt;strong&gt;현재 문맥에서 이 토큰이 전달할 정보&lt;/strong&gt;로 재구성된 것입니다.&lt;/p&gt;
&lt;p&gt;Attention Score와 $V$를 가중합하면 중요도가 부여된 토큰들의 정보를 얻게 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Scaled Dot-Product: 학습을 가능하게 만드는 장치&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;그런데 여기서 한 가지 빠뜨린 게 있습니다. 실제 어텐션 수식을 보면 이렇게 생겼습니다:&lt;/p&gt;
&lt;p&gt;$$\text{Attention}(Q, K, V) = \text{softmax}\left(\frac{QK^T}{\sqrt{d_k}}\right)V$$&lt;/p&gt;
&lt;p&gt;분모에 $\sqrt{d_k}$가 있습니다. 이게 뭘까요?&lt;/p&gt;
&lt;p&gt;$QK^T$를 계산하면 $d_{model}$ 차원의 벡터끼리 내적합니다. 문제는 $d_{model}$이 커질수록 내적 값도 기하급수적으로 커진다는 점입니다.&lt;/p&gt;
&lt;p&gt;값이 너무 크면 어떻게 될까요? Softmax를 통과했을 때 확률이 한쪽으로만 쏠립니다. $0.99, 0.01, 0.00...$ 이런 식으로요.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;이렇게 되면 기울기가 거의 0에 수렴해버립니다. 학습이 멈춰버리는 거죠.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;$\sqrt{d_k}$로 나눠주는 이유가 바로 이겁니다. 이 값을 &lt;strong&gt;스케일링 팩터(Scaling Factor)&lt;/strong&gt;라고 부릅니다. 내적 값의 스케일을 조정해서 Softmax가 적절한 분포를 유지하도록 만듭니다. 이 작은 나눗셈 하나가 Transformer를 깊게 쌓을 수 있게 만든 핵심입니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;학습으로 획득하는 언어 패턴&lt;/h2&gt;
&lt;p&gt;그런데 자기 자신과 어텐션해서 중요도를 매긴다고 했는데, 실제 쿼리할 때는 이게 어떻게 적용되는 걸까요?&lt;/p&gt;
&lt;p&gt;처음 학습을 시작할 때 $Q, K, V$를 만드는 투영 레이어의 가중치는 랜덤입니다. 어디에 집중해야 할지 모릅니다.&lt;/p&gt;
&lt;p&gt;이때, 바로 Cross Entropy Loss를 통한 역전파가 상황을 바꿉니다.&lt;/p&gt;
&lt;p&gt;틀린 예측을 할 때마다 기울기가 역전파되면서 &amp;quot;어떤 토큰에 집중했어야 했는가&amp;quot;를 배웁니다. 중요한 건, &lt;strong&gt;특정 문장을 외우는 게 아니라는 점입니다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;$Q, K, V$ projection weights는 &lt;strong&gt;일반적인 언어 패턴&lt;/strong&gt;을 학습합니다:&lt;/p&gt;
&lt;ul&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;/ul&gt;
&lt;p&gt;새로운 문장이 들어와도 작동하는 이유입니다. 학습된 가중치로 $Q, K, V$를 만들면 의미있는 attention이 가능합니다. 메커니즘은 &amp;quot;나 자신과의 유사도&amp;quot;로 동일하지만, 학습된 패턴이 새로운 입력을 처리합니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;마치며&lt;/h2&gt;
&lt;p&gt;어텐션의 설계는 명확한 철학을 담고 있습니다. $Q$와 $K$로 유사도를 측정하고, $V$로 원본 정보를 보존하며, 학습을 통해 언어의 일반 패턴을 익힙니다.&lt;/p&gt;
&lt;p&gt;이 구조를 이해하면 어텐션이 왜 강력한지 보입니다. 동시에 왜 대체하기 어려운지도 알 수 있습니다.&lt;/p&gt;
&lt;p&gt;$(\text{sen\_len}, \text{seq\_len})$의 메모리 문제, Long context 처리의 한계. 이런 제약들도 결국 이 설계에서 비롯됩니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;어텐션 하면 빠질 수 없는 &lt;a href=&quot;https://skystarry.tistory.com/3&quot;&gt;Positional Encoding&lt;/a&gt;을 다음 글에서 다룹니다. 그럼 다음 글에서 뵙도록 하겠습니다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;해당 게시글은 Claude Sonnet 4.5와 Gemini 3 Pro를 이용해 작업하였음을 알립니다.&lt;/em&gt;&lt;br&gt;&lt;del&gt;&lt;em&gt;이 글은 기존 벨로그에서 &lt;a href=&quot;https://velog.io/@skystarry/why-attention-needs-qkv&quot;&gt;작성한 글&lt;/a&gt;을 보완하여 재발행한 것입니다.&lt;/em&gt;&lt;/del&gt;(SEO 최적화를 위해 삭제하였습니다.)&lt;/p&gt;</description>
      <category>트랜스포머 시리즈</category>
      <category>deeplearning</category>
      <category>nlp</category>
      <category>Transformer</category>
      <category>면접질문</category>
      <category>인공지능</category>
      <author>민성 Skystarry</author>
      <guid isPermaLink="true">https://skystarry.tistory.com/2</guid>
      <comments>https://skystarry.tistory.com/2#entry2comment</comments>
      <pubDate>Thu, 8 Jan 2026 17:30:14 +0900</pubDate>
    </item>
  </channel>
</rss>