<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>LLM &#8211; KGG Studio</title>
	<atom:link href="https://blog.kggstudio.com/tag/llm/feed/" rel="self" type="application/rss+xml" />
	<link>https://blog.kggstudio.com</link>
	<description>개발자 테크 블로그</description>
	<lastBuildDate>Fri, 08 May 2026 07:35:19 +0000</lastBuildDate>
	<language>ko-KR</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.9.4</generator>

<image>
	<url>https://blog.kggstudio.com/wp-content/uploads/2025/05/cropped-K-1-32x32.png</url>
	<title>LLM &#8211; KGG Studio</title>
	<link>https://blog.kggstudio.com</link>
	<width>32</width>
	<height>32</height>
</image> 
<site xmlns="com-wordpress:feed-additions:1">244941309</site>	<item>
		<title>Transformer (1)</title>
		<link>https://blog.kggstudio.com/transformer-1/</link>
					<comments>https://blog.kggstudio.com/transformer-1/#respond</comments>
		
		<dc:creator><![CDATA[TimTam]]></dc:creator>
		<pubDate>Fri, 08 May 2026 06:33:18 +0000</pubDate>
				<category><![CDATA[Dev]]></category>
		<category><![CDATA[LLM]]></category>
		<category><![CDATA[Transformer]]></category>
		<guid isPermaLink="false">https://blog.kggstudio.com/?p=539</guid>

					<description><![CDATA[트랜스포머 학습용 플래시카드 20장. 등장 배경부터 핵심 메커니즘, 응용까지 단계별 카드. 카드를 클릭하면 답이 보이고, 난이도 평가로 진도를 추적할 수 있습니다. 🤖 트랜스포머 마스터 카드 (20장) 카드를 클릭해 답을 확인하세요. 답을 본 후 &#8220;쉬움/보통/어려움&#8221;으로 평가하면 진도가 기록돼요. 1단계 · 등장 배경 카드 1-5 2단계 · 핵심 개념 카드 6-14 3단계 · 구조와 응용 카드 15-20 ... <a title="Transformer (1)" class="read-more" href="https://blog.kggstudio.com/transformer-1/" aria-label="Transformer (1)에 대해 더 자세히 알아보세요">더 읽기</a>]]></description>
										<content:encoded><![CDATA[
<h2 class="sr-only">트랜스포머 학습용 플래시카드 20장. 등장 배경부터 핵심 메커니즘, 응용까지 단계별 카드. 카드를 클릭하면 답이 보이고, 난이도 평가로 진도를 추적할 수 있습니다.</h2>

<div style="padding: 1rem 0;">

<div style="margin-bottom: 1.5rem;">
  <h2 style="margin: 0 0 4px;"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f916.png" alt="🤖" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 트랜스포머 마스터 카드 (20장)</h2>
  <p style="font-size: 14px; color: var(--color-text-secondary); margin: 0;">카드를 클릭해 답을 확인하세요. 답을 본 후 &#8220;쉬움/보통/어려움&#8221;으로 평가하면 진도가 기록돼요.</p>
</div>

<div style="display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; margin-bottom: 1rem;">
  <div style="background: #EEEDFE; padding: 10px; border-radius: var(--border-radius-md); text-align: center;">
    <div style="font-size: 11px; color: #3C3489; font-weight: 500;">1단계 · 등장 배경</div>
    <div style="font-size: 12px; color: #534AB7;">카드 1-5</div>
  </div>
  <div style="background: #E1F5EE; padding: 10px; border-radius: var(--border-radius-md); text-align: center;">
    <div style="font-size: 11px; color: #085041; font-weight: 500;">2단계 · 핵심 개념</div>
    <div style="font-size: 12px; color: #0F6E56;">카드 6-14</div>
  </div>
  <div style="background: #FAEEDA; padding: 10px; border-radius: var(--border-radius-md); text-align: center;">
    <div style="font-size: 11px; color: #633806; font-weight: 500;">3단계 · 구조와 응용</div>
    <div style="font-size: 12px; color: #854F0B;">카드 15-20</div>
  </div>
</div>

<div style="background: var(--color-background-secondary); border-radius: var(--border-radius-md); padding: 12px 16px; margin-bottom: 1.5rem; display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 8px;">
  <div style="font-size: 13px; color: var(--color-text-secondary);">
    <span id="current-card" style="font-weight: 500; color: var(--color-text-primary);">1</span> / 20
  </div>
  <div style="display: flex; gap: 12px; font-size: 12px;">
    <span><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f7e2.png" alt="🟢" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 쉬움 <span id="easy-count">0</span></span>
    <span><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f7e1.png" alt="🟡" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 보통 <span id="medium-count">0</span></span>
    <span><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f534.png" alt="🔴" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 어려움 <span id="hard-count">0</span></span>
  </div>
</div>

<div id="card-container" style="margin-bottom: 1.5rem;"></div>

<div style="display: flex; gap: 8px; justify-content: space-between; align-items: center; margin-bottom: 1rem;">
  <button onclick="prevCard()" id="prev-btn" style="font-size: 13px;">← 이전</button>
  <div id="rating-area" style="display: none; gap: 6px;">
    <button onclick="rate('easy')" style="font-size: 13px; background: #E1F5EE; border-color: #5DCAA5; color: #085041;"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f7e2.png" alt="🟢" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 쉬움</button>
    <button onclick="rate('medium')" style="font-size: 13px; background: #FAEEDA; border-color: #EF9F27; color: #633806;"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f7e1.png" alt="🟡" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 보통</button>
    <button onclick="rate('hard')" style="font-size: 13px; background: #FCEBEB; border-color: #F09595; color: #791F1F;"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f534.png" alt="🔴" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 어려움</button>
  </div>
  <button onclick="nextCard()" id="next-btn" style="font-size: 13px;">다음 →</button>
</div>

<div style="background: var(--color-background-secondary); border-radius: var(--border-radius-md); height: 6px; overflow: hidden; margin-bottom: 1.5rem;">
  <div id="progress-bar" style="height: 100%; background: #534AB7; width: 5%; transition: width 0.3s;"></div>
</div>

<div style="display: flex; gap: 8px; flex-wrap: wrap;">
  <button onclick="sendPrompt('Self-Attention 메커니즘을 수식과 함께 더 자세히 설명해줘')" style="font-size: 13px;">Self-Attention 깊이 보기 <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2197.png" alt="↗" class="wp-smiley" style="height: 1em; max-height: 1em;" /></button>
  <button onclick="sendPrompt('트랜스포머와 RNN/LSTM의 차이를 비교 다이어그램으로 보여줘')" style="font-size: 13px;">RNN과 비교 <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2197.png" alt="↗" class="wp-smiley" style="height: 1em; max-height: 1em;" /></button>
  <button onclick="sendPrompt('이 카드들로 어떻게 효과적으로 복습하면 좋을지 학습 계획을 짜줘')" style="font-size: 13px;">복습 계획 만들기 <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2197.png" alt="↗" class="wp-smiley" style="height: 1em; max-height: 1em;" /></button>
</div>

</div>

<script>
const cards = [
  // 1단계: 등장 배경
  {stage: 1, q: "트랜스포머 이전에 시퀀스(문장) 처리에 주로 쓰이던 모델 두 가지는?", a: "RNN (순환신경망)과 LSTM (장단기 메모리)\n\n&#x1f4a1; 둘 다 단어를 '하나씩 순서대로' 처리하는 구조"},
  {stage: 1, q: "RNN/LSTM의 가장 큰 두 가지 한계는?", a: "1. 병렬 처리 불가 — 단어를 순서대로만 처리해서 느림\n2. 장거리 의존성 약함 — 문장이 길어지면 앞쪽 정보를 잊어버림\n\n&#x1f4a1; '나는 한국에서 태어났고... [긴 문장]... 그래서 ___를 잘한다' 에서 '한국어'를 예측하기 어려움"},
  {stage: 1, q: "트랜스포머는 언제, 어떤 논문에서 처음 제안되었나?", a: "2017년, Google의 「Attention Is All You Need」 논문\n\n&#x1f4a1; 제목 그대로 '어텐션만 있으면 된다' — RNN 없이도 가능함을 증명"},
  {stage: 1, q: "트랜스포머가 RNN의 두 한계를 어떻게 해결했나?", a: "1. 병렬 처리 → 모든 단어를 한 번에 처리\n2. 장거리 의존성 → Self-Attention으로 멀리 떨어진 단어와도 직접 연결\n\n&#x1f4a1; '한 번에 모든 단어가 서로를 본다'"},
  {stage: 1, q: "트랜스포머가 등장하면서 가능해진 대표적인 모델 3가지는?", a: "BERT (2018) - 이해 특화\nGPT (2018~) - 생성 특화\nT5, ViT 등 - 다양한 변형\n\n&#x1f4a1; ChatGPT, Claude도 모두 트랜스포머 기반"},

  // 2단계: 핵심 개념
  {stage: 2, q: "Self-Attention을 한 문장으로 표현하면?", a: "문장 안의 각 단어가 다른 모든 단어와 얼마나 관련 있는지 계산하는 메커니즘\n\n&#x1f4a1; 'The animal didn't cross the street because IT was tired'에서 'it'이 'animal'을 가리킴을 파악"},
  {stage: 2, q: "Self-Attention의 핵심 3요소 Q, K, V는 각각 무엇의 약자이고 역할은?", a: "Q (Query, 질의): '내가 찾고 싶은 것'\nK (Key, 키): '내가 가진 정보의 라벨'\nV (Value, 값): '실제 정보 내용'\n\n&#x1f4a1; 도서관 비유: Q='요리책 찾고싶어', K=책 제목들, V=책 내용"},
  {stage: 2, q: "Attention 점수를 구하는 핵심 수식은?", a: "Attention(Q,K,V) = softmax(QKᵀ / √d_k) × V\n\n&#x1f4a1; 1. Q와 K의 내적 → 유사도\n2. √d_k로 나누기 → 값이 너무 커지지 않게\n3. softmax → 확률로 변환\n4. V와 곱하기 → 가중합"},
  {stage: 2, q: "왜 √d_k로 나눠주는 (scaling) 걸까?", a: "차원(d_k)이 크면 QKᵀ 값이 너무 커져서 softmax가 극단적으로 쏠림 → 학습이 어려워짐\n\n&#x1f4a1; √d_k로 나누면 분산이 안정되어 gradient가 잘 흐름"},
  {stage: 2, q: "Multi-Head Attention이란? 왜 여러 개의 head를 쓸까?", a: "Self-Attention을 여러 개(보통 8개) 병렬로 수행한 것\n\n&#x1f4a1; 각 head가 서로 다른 관점을 학습:\n- head1: 문법 관계\n- head2: 의미 관계\n- head3: 위치 관계 ...\n사람이 한 문장을 여러 각도로 보는 것과 비슷"},
  {stage: 2, q: "트랜스포머는 단어 순서를 어떻게 인식할까? (RNN처럼 순차 처리하지 않는데도)", a: "Positional Encoding (위치 인코딩)을 입력 임베딩에 더해줌\n\n&#x1f4a1; sin, cos 함수로 각 위치마다 고유한 패턴 생성\n→ '나는 너를 좋아해'와 '너는 나를 좋아해'를 구분 가능"},
  {stage: 2, q: "Feed-Forward Network (FFN)는 트랜스포머에서 어떤 역할?", a: "각 위치별로 독립적으로 적용되는 2층 신경망. 어텐션이 모은 정보를 비선형 변환해 더 풍부한 표현으로 만듦\n\n&#x1f4a1; 보통 차원을 4배로 늘렸다가(예: 512→2048→512) 다시 줄임"},
  {stage: 2, q: "Residual Connection (잔차 연결)과 Layer Normalization의 역할은?", a: "Residual: 입력을 출력에 더해줌(x + f(x)) → 깊은 신경망의 gradient 소실 방지\nLayerNorm: 각 층 출력을 정규화 → 학습 안정화\n\n&#x1f4a1; 둘 다 '깊이 쌓아도 학습이 잘 되게' 해주는 장치"},
  {stage: 2, q: "Encoder와 Decoder의 핵심 차이 한 가지는?", a: "Decoder는 Masked Self-Attention을 사용\n\n&#x1f4a1; 생성 시 미래 단어를 미리 보면 안 되니까, 자기보다 뒤에 있는 단어는 가려서(mask) 못 보게 함 → 'I love ___'에서 ___ 예측할 때 ___ 다음 단어를 못 봄"},

  // 3단계: 구조와 응용
  {stage: 3, q: "원조 트랜스포머의 전체 구조는? (Encoder/Decoder 개수)", a: "Encoder 6개 + Decoder 6개 스택\n\n&#x1f4a1; 입력(영어) → Encoder×6 → 중간 표현\n중간 표현 + 이전 출력 → Decoder×6 → 출력(한국어)\n\n원래 기계 번역용으로 설계됨"},
  {stage: 3, q: "Encoder-only, Decoder-only, Encoder-Decoder 구조는 각각 어떤 모델의 예가 있고 무엇에 강한가?", a: "Encoder-only: BERT — 이해/분류에 강함\nDecoder-only: GPT, Claude — 생성에 강함\nEncoder-Decoder: T5, BART — 번역/요약에 강함\n\n&#x1f4a1; LLM 시대엔 Decoder-only가 주류"},
  {stage: 3, q: "Self-Attention의 시간 복잡도는? 왜 긴 문장에 약점이 될까?", a: "O(n²) — 시퀀스 길이 n의 제곱\n\n&#x1f4a1; 모든 단어 쌍의 관계를 계산하니까\n- 1,000 단어 → 100만 번 계산\n- 10,000 단어 → 1억 번 계산\n→ 긴 문서 처리에 메모리/시간 부담"},
  {stage: 3, q: "트랜스포머가 NLP를 넘어 적용된 대표 분야 3가지는?", a: "1. Vision (ViT) — 이미지 분류\n2. 음성 (Whisper) — 음성 인식\n3. 멀티모달 (CLIP, GPT-4V) — 텍스트+이미지\n\n&#x1f4a1; 단백질 구조 예측(AlphaFold2)에도 사용 — 시퀀스가 있는 거의 모든 분야"},
  {stage: 3, q: "'트랜스포머를 이해했다'고 말할 수 있으려면 설명할 수 있어야 할 5가지는?", a: "1. 왜 등장했나 (RNN의 한계)\n2. Self-Attention의 Q, K, V 작동 원리\n3. Multi-Head Attention의 의도\n4. Positional Encoding의 필요성\n5. Encoder/Decoder 구조와 차이\n\n&#x1f4a1; 이 5개를 자기 말로 설명할 수 있다면 합격!"},
  {stage: 3, q: "&#x1f393; 마지막 질문: 친구에게 1분 안에 트랜스포머를 설명한다면?", a: "예시 답안:\n\n'2017년 구글이 만든 신경망 구조야. 기존 RNN은 단어를 하나씩 처리해서 느리고 긴 문장을 잘 못 다뤘는데, 트랜스포머는 Self-Attention이라는 메커니즘으로 모든 단어가 동시에 서로를 참조해. 덕분에 병렬 처리가 가능하고 멀리 있는 단어 관계도 잘 잡아내. ChatGPT, Claude 같은 LLM이 다 이 구조 기반이야.'\n\n&#x1f4a1; 자기 언어로 다시 말해보세요!"}
];

let idx = 0;
let flipped = false;
const ratings = {};
let easyCount = 0, mediumCount = 0, hardCount = 0;

function render() {
  const card = cards[idx];
  const stageColors = {
    1: {bg: '#EEEDFE', border: '#7F77DD', text: '#3C3489', label: '1단계 · 등장 배경'},
    2: {bg: '#E1F5EE', border: '#5DCAA5', text: '#085041', label: '2단계 · 핵심 개념'},
    3: {bg: '#FAEEDA', border: '#EF9F27', text: '#633806', label: '3단계 · 구조와 응용'}
  };
  const c = stageColors[card.stage];
  
  document.getElementById('card-container').innerHTML = 
    '<div onclick="flip()" style="background: var(--color-background-primary); border: 0.5px solid ' + c.border + '; border-radius: var(--border-radius-lg); padding: 1.5rem 1.25rem; cursor: pointer; min-height: 220px; display: flex; flex-direction: column;">' +
      '<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 16px;">' +
        '<span style="font-size: 11px; font-weight: 500; padding: 3px 10px; background: ' + c.bg + '; color: ' + c.text + '; border-radius: 999px;">' + c.label + '</span>' +
        '<span style="font-size: 11px; color: var(--color-text-tertiary);">카드 ' + (idx + 1) + '</span>' +
      '</div>' +
      '<div style="flex: 1; display: flex; align-items: center; justify-content: center; text-align: center; padding: 8px;">' +
        (flipped 
          ? '<div style="font-size: 14px; line-height: 1.7; white-space: pre-line; text-align: left; width: 100%;">' + card.a + '</div>'
          : '<div style="font-size: 17px; line-height: 1.5; font-weight: 500;">' + card.q + '</div>'
        ) +
      '</div>' +
      '<div style="text-align: center; margin-top: 12px; font-size: 12px; color: var(--color-text-tertiary);">' + 
        (flipped ? '↑ 다시 클릭하면 질문' : '클릭하면 답 보기 →') + 
      '</div>' +
    '</div>';
  
  document.getElementById('current-card').textContent = idx + 1;
  document.getElementById('progress-bar').style.width = ((idx + 1) / cards.length * 100) + '%';
  document.getElementById('rating-area').style.display = flipped ? 'flex' : 'none';
  document.getElementById('prev-btn').disabled = idx === 0;
  document.getElementById('next-btn').disabled = idx === cards.length - 1;
}

function flip() {
  flipped = !flipped;
  render();
}

function nextCard() {
  if (idx < cards.length - 1) {
    idx++;
    flipped = false;
    render();
  }
}

function prevCard() {
  if (idx > 0) {
    idx--;
    flipped = false;
    render();
  }
}

function rate(level) {
  if (ratings[idx] === level) return;
  if (ratings[idx]) {
    if (ratings[idx] === 'easy') easyCount--;
    if (ratings[idx] === 'medium') mediumCount--;
    if (ratings[idx] === 'hard') hardCount--;
  }
  ratings[idx] = level;
  if (level === 'easy') easyCount++;
  if (level === 'medium') mediumCount++;
  if (level === 'hard') hardCount++;
  document.getElementById('easy-count').textContent = easyCount;
  document.getElementById('medium-count').textContent = mediumCount;
  document.getElementById('hard-count').textContent = hardCount;
  setTimeout(nextCard, 300);
}

render();
</script>



<h3 class="wp-block-heading">Transformer의 등장 배경.</h3>



<h4 class="wp-block-heading">Attention으로 해결하지 못한것</h4>



<ul class="wp-block-list">
<li>장기 의존성 문제 (Long-term Dependency Problem): RNN의 순차적 정보 전달 구조, 문장이 길어질수록 앞쪽 정보가 뒤로 전달되는 과정에서 소실됨. LSTM으로 개선되었기는하나 여전히 이전 정보가 희석되고 한계가 있음. Attention은 디코더가 인코더의 모든 hidden state를 참조할수 있게 해주지만, <strong>hidden state 자체가 이미 RNN을 통해 만든 </strong>값들임.</li>



<li>병렬 처리의 어려움 (Parallelization Difficulty): RNN은 구조적으로 현재 스텝의 계산이 이전 스텝의 결과에 의존적임. Hidden state에서 h2를 계산하려면 h1이 필요하고 h3를 계산하려면 h2가 필요함. Attention 자체는 병렬계산이 가능한 알고리즘이지만, <strong>Transformer 이전의 attention은 RNN 위에서 쓰는 부가기능</strong>이었기때문에 한계가 존재했음.</li>
</ul>



<h3 class="wp-block-heading">Transformer의 핵심 개념들.</h3>



<ul class="wp-block-list">
<li>Positional Encoding</li>



<li>Attention
<ul class="wp-block-list">
<li>Scaled dot product Attention</li>



<li>Multihead Attention</li>
</ul>
</li>



<li>FFNN</li>



<li>Add&amp;Norm</li>
</ul>



<h4 class="wp-block-heading">Positional Encoding (PE): 위치기반 인코딩</h4>



<p>PE가 없다면 아래 두 문장을 구분할수 있을까?</p>



<pre class="wp-block-code"><code>나는 학생 이다 / 이다 학생 나는</code></pre>



<p>[답변] Transformer는 모든 토큰을 <strong>동시에</strong> 처리해. RNN처럼 순서대로 읽지 않기 때문에, Positional Encoding이 없으면 아래 두 문장은 <strong>완전히 동일하게</strong> 인식돼.</p>



<pre class="wp-block-code"><code>"나는 학생 이다"   →  {나는, 학생, 이다}  ← 그냥 단어 집합
"이다 학생 나는"   →  {이다, 학생, 나는}  ← 똑같은 단어 집합</code></pre>



<p>PE가 하는 일: 각 토큰의 임베딩 벡터에 <strong>위치 정보를 담은 벡터를 더해줘.</strong></p>



<pre class="wp-block-code"><code>입력 = 단어 임베딩 + 위치 임베딩</code></pre>



<p>예를 들면 이렇게 돼:</p>



<pre class="wp-block-code"><code>"나는 학생 이다"
나는  →  &#91;단어벡터] + &#91;위치1 벡터]
학생  →  &#91;단어벡터] + &#91;위치2 벡터]
이다  →  &#91;단어벡터] + &#91;위치3 벡터]

"이다 학생 나는"
이다  →  &#91;단어벡터] + &#91;위치1 벡터]  ← !!
학생  →  &#91;단어벡터] + &#91;위치2 벡터]
나는  →  &#91;단어벡터] + &#91;위치3 벡터]  ← !!</code></pre>



<h4 class="wp-block-heading">핵심 원리 한 줄 요약</h4>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p>같은 단어라도 <strong>위치가 다르면 → 더해지는 벡터가 다르고 → 최종 입력값이 달라진다</strong></p>
</blockquote>



<p>그래서 모델은 단순히 &#8220;어떤 단어가 있냐&#8221;가 아니라 <strong>&#8220;어떤 단어가 몇 번째에 있냐&#8221;</strong> 를 함께 인식할 수 있어.</p>



<h4 class="wp-block-heading">PE 설계에 반영된 전제들과 해소 방법</h4>



<h4 class="wp-block-heading">전제가 왜 필요한가?</h4>



<p>위치 벡터를 단어 임베딩에 <strong>더하는(+)</strong> 방식을 쓰기 때문에, 잘못 설계하면 위치 정보가 단어 의미 정보를 <strong>덮어쓰거나 방해</strong>할 수 있어. 그래서 PE는 아래 3가지 전제를 만족해야 해.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h4 class="wp-block-heading">전제 1. 위치마다 고유한 값이어야 한다 (Unique)</h4>



<p><strong>문제:</strong> 위치 벡터가 중복되면 모델이 두 위치를 구별할 수 없어.</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p>1번 위치 PE = 3번 위치 PE → 모델 입장에서 두 토큰이 같은 위치</p>
</blockquote>



<p><strong>해소:</strong> sin/cos 함수를 <strong>다양한 주파수</strong>로 조합해서 사용해. 각 차원마다 진동 속도가 다른 sin/cos 값을 쌓으면, 모든 위치가 고유한 벡터 패턴을 가지게 돼. 이진수에서 각 자릿수가 다른 속도로 바뀌는 것과 같은 원리야.</p>



<pre class="wp-block-code"><code>PE(pos, 2i)   = sin(pos / 10000^(2i/d_model))
PE(pos, 2i+1) = cos(pos / 10000^(2i/d_model))</code></pre>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h4 class="wp-block-heading">전제 2. 값이 너무 커지면 안 된다 (Bounded)</h4>



<p><strong>문제:</strong> 위치 벡터의 값이 단어 임베딩보다 <strong>훨씬 크면</strong>, 모델은 위치 정보만 보고 단어 의미는 무시하게 돼.</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p>단어 임베딩 값: 0.3, 0.7, -0.2 &#8230; 위치 벡터 값: 1000, 5000, 3000 &#8230; → 임베딩 정보가 묻혀버림</p>
</blockquote>



<p><strong>해소:</strong> sin/cos 함수는 모든 값이 [-1, 1] 범위 안에 있어서, 위치 신호가 단어 임베딩을 압도하지 않아. 반면 단순히 위치 인덱스(1, 2, 3&#8230;)를 그대로 더하면 문장이 길어질수록 위치 값이 무한히 커지는 문제가 생겨. <a href="https://mesuvash.github.io/blog/2026/positional-encodings/" target="_blank" rel="noreferrer noopener">Mesuvash</a></p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h4 class="wp-block-heading">전제 3. 가까운 위치는 비슷한 벡터여야 한다 (Smooth)</h4>



<p><strong>문제:</strong> 인접한 위치의 PE가 완전히 다른 값이라면, 모델이 &#8220;1번과 2번 위치는 가깝다&#8221;는 것을 학습하기 어려워.</p>



<p><strong>해소:</strong> sin/cos의 주기적 특성 덕분에 가까운 위치는 비슷한 인코딩 값을 가지게 되어, 모델이 자연스럽게 위치 간 근접성을 학습할 수 있어. 또한 삼각함수의 덧셈정리 덕분에, <strong>임의의 위치 k만큼 떨어진 관계</strong>를 선형 변환으로 표현할 수 있어서 상대적 위치 관계도 파악 가능해. <a href="https://mesuvash.github.io/blog/2026/positional-encodings/" target="_blank" rel="noreferrer noopener">Mesuvash</a></p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h4 class="wp-block-heading">정리</h4>



<figure class="wp-block-table"><table class="has-fixed-layout"><thead><tr><th>전제</th><th>문제 상황</th><th>sin/cos로 해소한 방법</th></tr></thead><tbody><tr><td><strong>Unique</strong> (고유성)</td><td>위치 중복 → 구별 불가</td><td>다양한 주파수 조합으로 모든 위치가 고유한 패턴 가짐</td></tr><tr><td><strong>Bounded</strong> (유계성)</td><td>값이 너무 큼 → 임베딩 정보 훼손</td><td>sin/cos는 항상 [-1, 1] 이내</td></tr><tr><td><strong>Smooth</strong> (연속성)</td><td>인접 위치가 너무 다름 → 근접성 학습 불가</td><td>주기함수라 인접 위치 간 값 변화가 부드러움</td></tr></tbody></table></figure>



<p>이 세 조건을 동시에 만족하는 함수로 sin/cos가 선택된 거야. 실제로 Transformer 논문에서 learnable한 PE도 시도해봤지만 성능이 거의 동일했고, 입력 길이에 대한 일반화 측면에서 sinusoidal을 최종 선택했다고 해. </p>



<h3 class="wp-block-heading">Attention</h3>



<p>트랜스포머 이전의 Dot proudct Attention의 과정.</p>



<figure class="wp-block-image size-large is-resized"><img decoding="async" data-attachment-id="545" data-permalink="https://blog.kggstudio.com/transformer-1/pre_transformer_attention_flow/#main" data-orig-file="https://blog.kggstudio.com/wp-content/uploads/2026/05/pre_transformer_attention_flow.svg" data-orig-size="100,0" data-comments-opened="1" data-image-meta="[]" data-image-title="pre_transformer_attention_flow" data-image-description="" data-image-caption="" data-large-file="https://blog.kggstudio.com/wp-content/uploads/2026/05/pre_transformer_attention_flow.svg" src="https://blog.kggstudio.com/wp-content/uploads/2026/05/pre_transformer_attention_flow.svg" alt="" class="wp-image-545" style="aspect-ratio:0.8293447591179851;width:669px;height:auto"/></figure>



<p>전체 흐름을 3단계로 나눠서 보면 이해하기 쉬워.</p>



<p><strong>① 인코더</strong> — 입력 문장의 각 토큰을 RNN이 순서대로 처리해서 hidden state(h₁, h₂, h₃)를 생성해. 각 hᵢ는 해당 토큰까지의 문맥 정보를 담고 있어.</p>



<p><strong>② Attention</strong> — 디코더의 현재 상태(s)와 인코더의 각 hᵢ를 비교해서 점수를 계산하고, Softmax로 가중치(α)를 뽑아. 그 가중치로 hᵢ들을 가중합해서 <strong>context vector(c)</strong> 를 만들어. 선 굵기가 attention 가중치를 나타내는데, h₂가 가장 관련 있다고 판단된 예시야.</p>



<p><strong>③ 디코더</strong> — context vector와 이전 decoder state를 합쳐서 RNN이 다음 출력 토큰을 생성해.</p>



<p>오른쪽 하단 점선 박스가 핵심인데, Attention을 썼어도 RNN 구조 자체는 그대로라서 순차 처리와 h₁ 손상 문제가 남아있는 게 Transformer 등장의 배경이야.</p>



<h4 class="wp-block-heading">Q, K, V가 뭔지 — 도서관 비유로 먼저 이해하기</h4>



<p>Attention을 <strong>도서관 검색 시스템</strong>으로 생각해봐.</p>



<ul class="wp-block-list">
<li><strong>Q (Query)</strong> — &#8220;내가 지금 찾고 싶은 것&#8221;. 검색창에 입력하는 검색어야. 현재 처리 중인 토큰이 &#8220;나는 무엇에 집중해야 하지?&#8221;라고 던지는 질문.</li>



<li><strong>K (Key)</strong> — &#8220;각 책의 색인 태그&#8221;. 모든 토큰이 자신을 설명하는 라벨을 달고 있어. Query가 어떤 Key와 잘 맞는지를 비교해.</li>



<li><strong>V (Value)</strong> — &#8220;책의 실제 내용&#8221;. Key가 매칭됐을 때 실제로 가져오는 정보야.</li>
</ul>



<p>그리고 <code>Q · Kᵀ</code>는 <strong>Query와 모든 Key를 내적(dot product)해서 유사도 점수를 계산하는 것</strong>이야. 두 벡터의 내적이 크다 = 방향이 비슷하다 = 관련이 높다는 뜻이야.</p>



<style>
.haha { max-width: 800px; margin: 0 auto; width: 100%; }
.tok { cursor:pointer; transition: all .2s; }
.tok:hover rect { opacity:.85; }
.score-bar { transition: width .35s ease; }
.step-label { font-size:11px; fill:var(--color-text-tertiary); }
</style>
<div class="haha">
<h2 class="sr-only">Q K V Attention 인터랙티브 도식 — 토큰을 클릭하면 해당 토큰의 Query가 다른 Key와 유사도를 계산하는 과정을 시각화합니다</h2>

<svg id="main-svg" width="100%" viewBox="0 0 680 540" role="img">
<title>Q K V Attention 인터랙티브 도식</title>
<desc>토큰을 클릭하면 Query·Key 내적으로 Attention 가중치를 계산하는 과정을 보여주는 인터랙티브 다이어그램</desc>
<defs>
  <marker id="arrow" viewBox="0 0 10 10" refX="8" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse">
    <path d="M2 1L8 5L2 9" fill="none" stroke="context-stroke" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
  </marker>
</defs>

<!-- ── 안내 텍스트 ── -->
<text id="hint" class="ts" x="340" y="28" text-anchor="middle" fill="var(--color-text-tertiary)">토큰을 클릭하면 Q · K 계산 과정이 나타납니다</text>

<!-- ── 입력 토큰 행 ── -->
<!-- 나는 x=60 w=90 -->
<g class="tok node" id="tok0" onclick="selectToken(0)">
  <rect id="tr0" x="60" y="50" width="90" height="44" rx="8" stroke-width="0.5" fill="#EEEDFE" stroke="#534AB7"/>
  <text class="th" x="105" y="72" text-anchor="middle" dominant-baseline="central" fill="#3C3489">나는</text>
</g>
<!-- 어제 -->
<g class="tok node" id="tok1" onclick="selectToken(1)">
  <rect id="tr1" x="170" y="50" width="90" height="44" rx="8" stroke-width="0.5" fill="#EEEDFE" stroke="#534AB7"/>
  <text class="th" x="215" y="72" text-anchor="middle" dominant-baseline="central" fill="#3C3489">어제</text>
</g>
<!-- 학교 -->
<g class="tok node" id="tok2" onclick="selectToken(2)">
  <rect id="tr2" x="280" y="50" width="90" height="44" rx="8" stroke-width="0.5" fill="#EEEDFE" stroke="#534AB7"/>
  <text class="th" x="325" y="72" text-anchor="middle" dominant-baseline="central" fill="#3C3489">학교</text>
</g>
<!-- 에서 -->
<g class="tok node" id="tok3" onclick="selectToken(3)">
  <rect id="tr3" x="390" y="50" width="90" height="44" rx="8" stroke-width="0.5" fill="#EEEDFE" stroke="#534AB7"/>
  <text class="th" x="435" y="72" text-anchor="middle" dominant-baseline="central" fill="#3C3489">에서</text>
</g>
<!-- 공부했다 -->
<g class="tok node" id="tok4" onclick="selectToken(4)">
  <rect id="tr4" x="500" y="50" width="120" height="44" rx="8" stroke-width="0.5" fill="#EEEDFE" stroke="#534AB7"/>
  <text class="th" x="560" y="72" text-anchor="middle" dominant-baseline="central" fill="#3C3489">공부했다</text>
</g>

<!-- ── 선택된 토큰 표시 영역 ── -->
<!-- Q 박스 -->
<g id="q-group" opacity="0">
  <rect x="60" y="130" width="160" height="56" rx="8" stroke-width="0.5" fill="#E6F1FB" stroke="#185FA5"/>
  <text class="th" x="140" y="150" text-anchor="middle" dominant-baseline="central" fill="#0C447C">Q (Query)</text>
  <text id="q-label" class="ts" x="140" y="170" text-anchor="middle" dominant-baseline="central" fill="#185FA5">&#8220;나는&#8221; 의 질문 벡터</text>
</g>

<!-- Wq Wk Wv 설명 -->
<g id="w-group" opacity="0">
  <text class="ts" x="340" y="145" text-anchor="middle" fill="var(--color-text-secondary)">각 토큰의 임베딩에 Wq, Wk, Wv 행렬을 곱해서 Q, K, V 생성</text>
</g>

<!-- K 박스들 -->
<g id="k-group" opacity="0">
  <text class="ts" x="340" y="218" text-anchor="middle" fill="var(--color-text-tertiary)">K (Keys) — 모든 토큰의 색인 벡터</text>
  <rect id="kr0" x="60"  y="228" width="90" height="36" rx="6" stroke-width="0.5" fill="#E1F5EE" stroke="#0F6E56"/>
  <rect id="kr1" x="162" y="228" width="90" height="36" rx="6" stroke-width="0.5" fill="#E1F5EE" stroke="#0F6E56"/>
  <rect id="kr2" x="264" y="228" width="90" height="36" rx="6" stroke-width="0.5" fill="#E1F5EE" stroke="#0F6E56"/>
  <rect id="kr3" x="366" y="228" width="90" height="36" rx="6" stroke-width="0.5" fill="#E1F5EE" stroke="#0F6E56"/>
  <rect id="kr4" x="468" y="228" width="120" height="36" rx="6" stroke-width="0.5" fill="#E1F5EE" stroke="#0F6E56"/>
  <text id="kt0" class="ts" x="105"  y="246" text-anchor="middle" dominant-baseline="central" fill="#085041">K: 나는</text>
  <text id="kt1" class="ts" x="207"  y="246" text-anchor="middle" dominant-baseline="central" fill="#085041">K: 어제</text>
  <text id="kt2" class="ts" x="309"  y="246" text-anchor="middle" dominant-baseline="central" fill="#085041">K: 학교</text>
  <text id="kt3" class="ts" x="411"  y="246" text-anchor="middle" dominant-baseline="central" fill="#085041">K: 에서</text>
  <text id="kt4" class="ts" x="528"  y="246" text-anchor="middle" dominant-baseline="central" fill="#085041">K: 공부했다</text>
</g>

<!-- Q·Kᵀ 내적 결과 (Score) -->
<g id="score-group" opacity="0">
  <text class="ts" x="340" y="296" text-anchor="middle" fill="var(--color-text-tertiary)">Q · Kᵀ = 유사도 점수 (내적)</text>

  <!-- 막대 그래프 배경 -->
  <rect x="60"  y="306" width="140" height="22" rx="4" fill="var(--color-background-secondary)" stroke="var(--color-border-tertiary)" stroke-width="0.5"/>
  <rect x="60"  y="338" width="140" height="22" rx="4" fill="var(--color-background-secondary)" stroke="var(--color-border-tertiary)" stroke-width="0.5"/>
  <rect x="60"  y="370" width="140" height="22" rx="4" fill="var(--color-background-secondary)" stroke="var(--color-border-tertiary)" stroke-width="0.5"/>
  <rect x="60"  y="402" width="140" height="22" rx="4" fill="var(--color-background-secondary)" stroke="var(--color-border-tertiary)" stroke-width="0.5"/>
  <rect x="60"  y="434" width="140" height="22" rx="4" fill="var(--color-background-secondary)" stroke="var(--color-border-tertiary)" stroke-width="0.5"/>

  <!-- 막대 fill -->
  <rect id="sb0" class="score-bar" x="60" y="306" width="0" height="22" rx="4" fill="#EF9F27" opacity="0.85"/>
  <rect id="sb1" class="score-bar" x="60" y="338" width="0" height="22" rx="4" fill="#EF9F27" opacity="0.85"/>
  <rect id="sb2" class="score-bar" x="60" y="370" width="0" height="22" rx="4" fill="#EF9F27" opacity="0.85"/>
  <rect id="sb3" class="score-bar" x="60" y="402" width="0" height="22" rx="4" fill="#EF9F27" opacity="0.85"/>
  <rect id="sb4" class="score-bar" x="60" y="434" width="0" height="22" rx="4" fill="#EF9F27" opacity="0.85"/>

  <!-- 레이블 -->
  <text id="sl0" class="ts" x="212" y="317" dominant-baseline="central" fill="var(--color-text-secondary)"></text>
  <text id="sl1" class="ts" x="212" y="349" dominant-baseline="central" fill="var(--color-text-secondary)"></text>
  <text id="sl2" class="ts" x="212" y="381" dominant-baseline="central" fill="var(--color-text-secondary)"></text>
  <text id="sl3" class="ts" x="212" y="413" dominant-baseline="central" fill="var(--color-text-secondary)"></text>
  <text id="sl4" class="ts" x="212" y="445" dominant-baseline="central" fill="var(--color-text-secondary)"></text>

  <text id="sn0" class="ts" x="55" y="317" text-anchor="end" dominant-baseline="central" fill="var(--color-text-tertiary)">나는</text>
  <text id="sn1" class="ts" x="55" y="349" text-anchor="end" dominant-baseline="central" fill="var(--color-text-tertiary)">어제</text>
  <text id="sn2" class="ts" x="55" y="381" text-anchor="end" dominant-baseline="central" fill="var(--color-text-tertiary)">학교</text>
  <text id="sn3" class="ts" x="55" y="413" text-anchor="end" dominant-baseline="central" fill="var(--color-text-tertiary)">에서</text>
  <text id="sn4" class="ts" x="55" y="445" text-anchor="end" dominant-baseline="central" fill="var(--color-text-tertiary)">공부했다</text>
</g>

<!-- Softmax → Attention weight 안내 -->
<g id="softmax-group" opacity="0">
  <text class="ts" x="420" y="317" fill="var(--color-text-tertiary)">Softmax 적용</text>
  <text class="ts" x="420" y="337" fill="var(--color-text-tertiary)">→ 합이 1이 되는</text>
  <text class="ts" x="420" y="357" fill="var(--color-text-tertiary)">  가중치(α) 계산</text>
  <text class="ts" x="420" y="387" fill="var(--color-text-tertiary)">α들로 V 가중합</text>
  <text class="ts" x="420" y="407" fill="var(--color-text-tertiary)">→ context vector</text>

  <rect x="400" y="305" width="220" height="116" rx="8" fill="none" stroke="var(--color-border-tertiary)" stroke-width="0.5" stroke-dasharray="3 3"/>
</g>

<!-- 공식 -->
<g id="formula-group" opacity="0">
  <rect x="60" y="470" width="560" height="50" rx="8" fill="var(--color-background-secondary)" stroke="var(--color-border-tertiary)" stroke-width="0.5"/>
  <text class="ts" x="340" y="488" text-anchor="middle" dominant-baseline="central" fill="var(--color-text-secondary)">Attention(Q, K, V) = softmax( Q · Kᵀ / √dk ) · V</text>
  <text class="ts" x="340" y="508" text-anchor="middle" dominant-baseline="central" fill="var(--color-text-tertiary)">√dk 로 나누는 이유: 차원이 커질수록 내적값이 커져서 softmax가 포화되는 걸 방지</text>
</g>
</svg>
</div>

<script>
const tokens = ["나는", "어제", "학교", "에서", "공부했다"];

const rawScores = [
  [0.9, 0.3, 0.2, 0.2, 0.4],
  [0.3, 0.9, 0.4, 0.3, 0.6],
  [0.2, 0.4, 0.9, 0.5, 0.7],
  [0.2, 0.3, 0.5, 0.9, 0.5],
  [0.4, 0.6, 0.7, 0.5, 0.9],
];

let selected = -1;

function show(id, val=1) {
  document.getElementById(id).setAttribute('opacity', val);
}

function selectToken(idx) {
  selected = idx;
  document.getElementById('hint').setAttribute('opacity', 0);

  for(let i=0;i<5;i++){
    const r = document.getElementById('tr'+i);
    if(i===idx){
      r.setAttribute('fill','#7F77DD');
      r.setAttribute('stroke','#3C3489');
      document.querySelector('#tok'+i+' text').setAttribute('fill','#EEEDFE');
    } else {
      r.setAttribute('fill','#EEEDFE');
      r.setAttribute('stroke','#534AB7');
      document.querySelector('#tok'+i+' text').setAttribute('fill','#3C3489');
    }
  }

  document.getElementById('q-label').textContent = '"' + tokens[idx] + '" 의 질문 벡터';
  show('q-group');
  show('w-group');
  show('k-group');
  show('score-group');
  show('softmax-group');
  show('formula-group');

  const scores = rawScores[idx];
  const maxS = Math.max(...scores);

  setTimeout(() => {
    for(let i=0;i<5;i++){
      const barW = Math.round((scores[i] / 1.0) * 130);
      document.getElementById('sb'+i).setAttribute('width', barW);
      document.getElementById('sl'+i).textContent = 'score: ' + scores[i].toFixed(1);

      const kr = document.getElementById('kr'+i);
      if(scores[i] === maxS){
        kr.setAttribute('fill','#9FE1CB');
        kr.setAttribute('stroke','#1D9E75');
      } else {
        kr.setAttribute('fill','#E1F5EE');
        kr.setAttribute('stroke','#0F6E56');
      }
    }
  }, 80);
}
</script>



<p>공식 맨 아래 <code>/ √dk</code>가 있는데, 이건 벡터 차원이 커질수록 내적값이 폭발적으로 커져서 Softmax가 한 값으로 쏠리는 걸 방지하기 위한 정규화야.</p>



<h3 class="wp-block-heading">Scaled dot product Attention (기존 attention과의 차이)</h3>



<p>기존 Attention은 Q, K, V가 이렇게 나옴</p>



<ul class="wp-block-list">
<li>Q = 디코더의 현재 hidden state </li>



<li>K = 인코더의 각 hidden state </li>



<li>V = 인코더의 각 hidden state (K와 동일한 값)</li>
</ul>



<p>즉 Q, K, V가 <strong>RNN이 만들어낸 hidden state 그 자체</strong>였어. 별도의 변환 없이 바로 사용한 거야.</p>



<p>Scaled Dot-Product Attention(Transformer)에서는 근본적으로 달라져. 입력 토큰의 임베딩 벡터 하나에서 <strong>Wq, Wk, Wv 세 개의 별도 가중치 행렬을 곱해서</strong> Q, K, V를 각각 따로 만들어. 같은 토큰이라도 Q로 쓸 때와 K로 쓸 때, V로 쓸 때 서로 다른 벡터가 되는 거야.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h4 class="wp-block-heading">2. 하나의 토큰이 3개로 나뉜다는 의미</h4>



<p>토큰 임베딩 벡터 <code>x</code>가 있을 때:</p>



<pre class="wp-block-code"><code>Q = x · Wq   ← "나는 무엇을 찾고 있나?"  (질문자 역할)
K = x · Wk   ← "나는 어떤 정보를 갖고 있나?" (색인 역할)
V = x · Wv   ← "나의 실제 내용은 무엇인가?" (정보 역할)</code></pre>



<p>같은 토큰 <code>x</code>에서 세 가지 <strong>역할</strong>이 분리되는 거야. Wq, Wk, Wv는 학습으로 최적화되는 파라미터이기 때문에, 모델이 "어떤 방식으로 질문하고, 어떤 방식으로 매칭하고, 어떤 정보를 전달할지"를 스스로 학습하게 돼.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h4 class="wp-block-heading">3. Self-Attention vs Encoder-Decoder Attention</h4>



<figure class="wp-block-image size-large is-resized"><img decoding="async" data-attachment-id="549" data-permalink="https://blog.kggstudio.com/transformer-1/self_vs_cross_attention/#main" data-orig-file="https://blog.kggstudio.com/wp-content/uploads/2026/05/self_vs_cross_attention.svg" data-orig-size="100,0" data-comments-opened="1" data-image-meta="[]" data-image-title="self_vs_cross_attention" data-image-description="" data-image-caption="" data-large-file="https://blog.kggstudio.com/wp-content/uploads/2026/05/self_vs_cross_attention.svg" src="https://blog.kggstudio.com/wp-content/uploads/2026/05/self_vs_cross_attention.svg" alt="" class="wp-image-549" style="aspect-ratio:1.4783061075850588;width:1354px;height:auto"/></figure>



<p>도식에서 핵심 차이가 보이지? Self-Attention은 Q, K, V가 전부 같은 시퀀스에서 나와서 "자기 문장 내부의 단어들이 서로를 참조"하는 거고, Encoder-Decoder Attention은 Q만 디코더에서, K와 V는 인코더에서 나와서 "번역 대상 문장을 보면서 출력을 생성"하는 구조야.</p>



<h4 class="wp-block-heading">4. Scaling(√dk로 나누기)의 의미</h4>



<figure class="wp-block-image size-large is-resized"><img decoding="async" data-attachment-id="552" data-permalink="https://blog.kggstudio.com/transformer-1/scaling_effect/#main" data-orig-file="https://blog.kggstudio.com/wp-content/uploads/2026/05/scaling_effect.svg" data-orig-size="100,0" data-comments-opened="1" data-image-meta="[]" data-image-title="scaling_effect" data-image-description="" data-image-caption="" data-large-file="https://blog.kggstudio.com/wp-content/uploads/2026/05/scaling_effect.svg" src="https://blog.kggstudio.com/wp-content/uploads/2026/05/scaling_effect.svg" alt="" class="wp-image-552" style="aspect-ratio:2.1937842778793417;width:1408px;height:auto"/></figure>



<p>Scaling의 원리는 이래. 벡터의 차원(dk)이 커질수록 Q와 K의 내적값이 자연히 커지는데, 이 큰 값이 그대로 Softmax에 들어가면 지수함수 특성상 가장 큰 값 쪽으로 확률이 거의 1로 쏠려버려. 이 상태에서 역전파를 하면 기울기가 거의 0이 되어서(기울기 소실) 학습이 제대로 안 돼.</p>



<p><code>√dk</code>로 나누는 건 이 폭발적인 스케일을 내적 차원에 맞게 정규화해주는 거야. 예를 들어 dk=64이면 <code>√64 = 8</code>로 나눠서 점수를 안정된 범위로 가져오는 거지.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h4 class="wp-block-heading">전체를 한 줄로 정리하면</h4>



<figure class="wp-block-table"><table class="has-fixed-layout"><thead><tr><th>항목</th><th>기존 Attention</th><th>Scaled Dot-Product</th></tr></thead><tbody><tr><td>Q, K, V 출처</td><td>RNN hidden state 그대로</td><td>임베딩에 Wq/Wk/Wv 곱해서 생성</td></tr><tr><td>토큰 → 3개 분리</td><td>없음</td><td>역할(질문/색인/정보)을 명시적 분리</td></tr><tr><td>Self vs Cross</td><td>Cross만 존재</td><td>둘 다 동일 연산, 입력만 다름</td></tr><tr><td>Scaling</td><td>없음</td><td>÷√dk 로 기울기 소실 방지</td></tr></tbody></table></figure>



<h4 class="wp-block-heading">self attention 하고, Encoder-Decoder Attention 둘다 transformer에서 사용하는거같은데, self attention 은 decoder only or encoder only 중에 하나인가?</h4>



<figure class="wp-block-image size-large is-resized"><img decoding="async" data-attachment-id="553" data-permalink="https://blog.kggstudio.com/transformer-1/transformer_attention_placement/#main" data-orig-file="https://blog.kggstudio.com/wp-content/uploads/2026/05/transformer_attention_placement.svg" data-orig-size="100,0" data-comments-opened="1" data-image-meta="[]" data-image-title="transformer_attention_placement" data-image-description="" data-image-caption="" data-large-file="https://blog.kggstudio.com/wp-content/uploads/2026/05/transformer_attention_placement.svg" src="https://blog.kggstudio.com/wp-content/uploads/2026/05/transformer_attention_placement.svg" alt="" class="wp-image-553" style="aspect-ratio:1.1724784011658167;width:1375px;height:auto"/></figure>



<h4 class="wp-block-heading">Single-Head Attention vs Multi-Head Attention</h4>



<figure class="wp-block-image size-large is-resized"><img decoding="async" data-attachment-id="554" data-permalink="https://blog.kggstudio.com/transformer-1/single_vs_multihead_attention/#main" data-orig-file="https://blog.kggstudio.com/wp-content/uploads/2026/05/single_vs_multihead_attention.svg" data-orig-size="100,0" data-comments-opened="1" data-image-meta="[]" data-image-title="single_vs_multihead_attention" data-image-description="" data-image-caption="" data-large-file="https://blog.kggstudio.com/wp-content/uploads/2026/05/single_vs_multihead_attention.svg" src="https://blog.kggstudio.com/wp-content/uploads/2026/05/single_vs_multihead_attention.svg" alt="" class="wp-image-554" style="aspect-ratio:1.0968009970918156;width:1316px;height:auto"/></figure>



<p><strong>Self-Attention은 "무엇을"</strong>, <strong>Multi-Head Attention은 "어떻게 더 잘"</strong> 하는지에 대한 개념이야.</p>



<p>Self-Attention은 Q, K, V를 사용해서 토큰 간 관계를 계산하는 <strong>연산 방식</strong> 자체고, Multi-Head Attention은 그 Self-Attention을 <code>h</code>개의 헤드로 나눠서 <strong>병렬로 여러 번</strong> 돌린 뒤 결과를 합치는 <strong>구조적 확장</strong>이야.</p>



<p>헤드를 여러 개 쓰는 이유는, 하나의 Attention만으로는 한 가지 관점밖에 못 배우기 때문이야. 예를 들어 "나는 어제 학교에서 공부했다"라는 문장에서 단 하나의 Attention은 문법 관계에 집중하거나 의미 관계에 집중하거나 둘 중 하나밖에 못 해. 헤드를 8개, 16개로 늘리면 각 헤드가 서로 다른 Wq, Wk, Wv를 학습해서 각자 다른 언어적 패턴을 담당하게 돼.</p>



<p>그래서 Transformer에서 실제로 쓰이는 건 <strong>Multi-Head Self-Attention</strong>이야. Self-Attention(연산 방식) + Multi-Head(구조)가 합쳐진 형태인 거지.</p>



<h3 class="wp-block-heading">단일 토큰 벡터를 multihead로 구성하는 방법</h3>



<h4 class="wp-block-heading">차원 분할 방식(d_model -> h개로 나누기)</h4>



<p>d</p>



<h4 class="wp-block-heading">독립적 학습 파라미터 방식 (각 head가 독립적 가중치 학습)</h4>



<p>각 헤드가 <strong>원본 d_model 전체 차원</strong>에 접근하는 독립적인 Wq, Wk, Wv를 가져. 헤드가 h개면 파라미터 행렬도 h세트가 따로 존재.</p>



<pre class="wp-block-code"><code>입력 x (d_model=512)
  ↓
Head 1: x · Wq¹(512×512), x · Wk¹(512×512), x · Wv¹(512×512)
Head 2: x · Wq²(512×512), x · Wk²(512×512), x · Wv²(512×512)
...
Head 8: x · Wq⁸(512×512), x · Wk⁸(512×512), x · Wv⁸(512×512)</code></pre>



<p>각 헤드가 전체 벡터를 보고 독립적으로 뭘 집중할지 결정해. 파라미터 수는 <code>h × 3 × d_model²</code>으로 헤드 수에 비례해서 증가해.</p>



<h4 class="wp-block-heading">차원 분할 방식 (Transformer 논문의 실제 선택)</h4>



<p>d_model을 헤드 수로 <strong>쪼개서</strong> 각 헤드에 할당해. d_model=512, h=8이면 각 헤드는 dk=64 차원만 담당해.</p>



<pre class="wp-block-code"><code>입력 x (d_model=512)
  ↓
Head 1: x · Wq¹(512×64), x · Wk¹(512×64), x · Wv¹(512×64)
Head 2: x · Wq²(512×64), x · Wk²(512×64), x · Wv²(512×64)
...
Head 8: x · Wq⁸(512×64), x · Wk⁸(512×64), x · Wv⁸(512×64)</code></pre>



<p>각 헤드가 더 작은 부분 공간에서 작동해. 파라미터 수는 <code>h × 3 × d_model × dk = 3 × d_model²</code>로 헤드 수에 무관하게 일정해.</p>



<figure class="wp-block-image size-large is-resized"><img decoding="async" data-attachment-id="555" data-permalink="https://blog.kggstudio.com/transformer-1/dimension_split_vs_independent_params/#main" data-orig-file="https://blog.kggstudio.com/wp-content/uploads/2026/05/dimension_split_vs_independent_params.svg" data-orig-size="100,0" data-comments-opened="1" data-image-meta="[]" data-image-title="dimension_split_vs_independent_params" data-image-description="" data-image-caption="" data-large-file="https://blog.kggstudio.com/wp-content/uploads/2026/05/dimension_split_vs_independent_params.svg" src="https://blog.kggstudio.com/wp-content/uploads/2026/05/dimension_split_vs_independent_params.svg" alt="" class="wp-image-555" style="aspect-ratio:1.0625456823639972;width:1146px;height:auto"/></figure>



<p>핵심 차이를 한 문장으로 요약하면, <strong>독립 파라미터 방식은 헤드마다 각자 512차원 전체를 보고, 차원 분할 방식은 512차원을 헤드 수로 나눠서 각자 64차원씩 담당하는 것</strong>이야.</p>



<p>Transformer 논문("Attention is All You Need")이 차원 분할을 선택한 이유는 파라미터 수가 헤드 수에 상관없이 일정하게 유지되기 때문이야. 독립 파라미터 방식으로 헤드를 8개 쓰면 파라미터가 8배로 늘어나지만, 차원 분할 방식은 헤드가 몇 개든 <code>3 × d_model²</code>로 고정돼. 계산량과 메모리를 효율적으로 유지하면서 다양한 관점을 동시에 학습할 수 있는 거지.</p>



<p>다만 차원 분할도 완전히 독립적인 파라미터를 가지긴 해. 각 헤드의 Wq, Wk, Wv가 서로 다른 별개의 행렬이야. 차이는 그 행렬의 크기가 <code>d_model × dk</code>(작은 부분공간)냐, <code>d_model × d_model</code>(전체 공간)이냐인 거야.</p>



<h3 class="wp-block-heading">N21, N2N, N2M 정리</h3>



<figure class="wp-block-image size-large is-resized"><img decoding="async" data-attachment-id="559" data-permalink="https://blog.kggstudio.com/transformer-1/n21_n2n_n2m_explanation/#main" data-orig-file="https://blog.kggstudio.com/wp-content/uploads/2026/05/n21_n2n_n2m_explanation.svg" data-orig-size="100,0" data-comments-opened="1" data-image-meta="[]" data-image-title="n21_n2n_n2m_explanation" data-image-description="" data-image-caption="" data-large-file="https://blog.kggstudio.com/wp-content/uploads/2026/05/n21_n2n_n2m_explanation.svg" src="https://blog.kggstudio.com/wp-content/uploads/2026/05/n21_n2n_n2m_explanation.svg" alt="" class="wp-image-559" style="aspect-ratio:1.1724784011658167;width:1310px;height:auto"/></figure>



<p><strong>N21</strong> — 문장 전체를 보고 답 하나를 내는 것. "이 리뷰가 긍정이야 부정이야?" 처럼 전체 입력을 하나의 결론으로 압축하는 태스크야.</p>



<p><strong>N2N</strong> — 토큰 하나하나에 대응하는 답을 내는 것. "각 단어가 무슨 품사야?" 처럼 입력과 출력 개수가 딱 맞아. Transformer 인코더 내부의 Self-Attention이 이 방식이야.</p>



<p><strong>N2M</strong> — 입력과 출력 길이가 서로 달라도 되는 것. 번역이 대표적인 예야. 한국어 3단어를 넣었는데 영어로는 4단어가 나올 수도 있잖아. Transformer 전체 구조(인코더 + 디코더)가 이걸 가능하게 하는 거야.</p>



<p>Transformer가 이전 RNN 기반 모델들보다 혁신적이었던 이유 중 하나가 N2M을 훨씬 잘 처리할 수 있게 된 거야. RNN은 길이가 길어질수록 앞쪽 정보가 흐려졌지만, Transformer는 Self-Attention으로 거리와 상관없이 모든 토큰을 직접 참조할 수 있으니까.</p>



<h3 class="wp-block-heading">트랜스포머 관련 링크 (3 가지)</h3>



<figure class="wp-block-embed is-type-rich"><div class="wp-block-embed__wrapper">
<a class="m-story" href="https://medium.com/@hugmanskj/transformer%EC%9D%98-%ED%81%B0-%EA%B7%B8%EB%A6%BC-%EC%9D%B4%ED%95%B4-%EA%B8%B0%EC%88%A0%EC%A0%81-%EB%B3%B5%EC%9E%A1%ED%95%A8-%EC%97%86%EC%9D%B4-%ED%95%B5%EC%8B%AC-%EC%95%84%EC%9D%B4%EB%94%94%EC%96%B4-%ED%8C%8C%EC%95%85%ED%95%98%EA%B8%B0-5e182a40459d" target="_blank" data-width="1500" data-border="1" data-collapsed="">Medium.com에서 보기</a>
</div></figure>



<p><a href="https://wikidocs.net/31379">https://wikidocs.net/31379</a></p>



<p><a href="https://cpm0722.github.io/pytorch-implementation/transformer">https://cpm0722.github.io/pytorch-implementation/transformer</a></p>



<h4 class="wp-block-heading">찾아봐야할 내용</h4>



<p>PCA, 공분산 행렬</p>



<p></p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.kggstudio.com/transformer-1/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">539</post-id>	</item>
	</channel>
</rss>
