모두의 AI
머신러닝AI논문
로딩 중…

배우기

🏅내 업적

Ch.02

트랜스포머: 위치 인코딩과 피드포워드

트랜스포머의 셀프 어텐션은 단어들 끼리의 관계를 파악하는 데는 탁월하지만, 단어가 문장 속 어느 위치에 있는지 스스로 알지 못하는 치명적인 단점이 있습니다. 이를 해결하기 위해 단어의 임베딩에 '몇 번째 단어인지' 알려주는 위치 인코딩(Positional Encoding) 이름표를 붙여줍니다. 또한, 어텐션으로 모인 정보를 각 단어별로 깊이 있게 재가공하는 피드포워드(Feed Forward, FFN) 층이 이어집니다. 이 챕터에서는 파동의 원리를 이용한 사인·코사인 위치 인코딩의 직관적인 이해와, 토큰들의 개별 심층 면접관 역할을 하는 FFN의 작동 원리를 초보자의 눈높이에서 쉽고 재미있게 알아봅니다.

수식 쉽게 이해하기

ht(0)=xt+PE(t)h_t^{(0)} = x_t + PE(t)ht(0)​=xt​+PE(t)에서 xtx_txt​는 토큰 임베딩, PE(t)PE(t)PE(t)는 위치 ttt에 대응하는 벡터입니다. "내용"과 "순서(몇 번째인지를 숫자로 만든 정보)"를 더해 모델 입력을 만듭니다.사인·코사인 PE에서 PE(t,2i)=sin⁡(t/100002i/d)PE(t,2i)=\sin(t/10000^{2i/d})PE(t,2i)=sin(t/100002i/d), PE(t,2i+1)=cos⁡(t/100002i/d)PE(t,2i+1)=\cos(t/10000^{2i/d})PE(t,2i+1)=cos(t/100002i/d) 꼴은 여러 주파수 iii로 위치를 부호화합니다. ddd는 dmodeld_{model}dmodel​, ttt는 토큰 인덱스입니다.대표적으로 FFN(x)=max⁡(0,xW1+b1)W2+b2\mathrm{FFN}(x)=\max(0,xW_1+b_1)W_2+b_2FFN(x)=max(0,xW1​+b1​)W2​+b2​ 꼴이며, max⁡(0,⋅)\max(0,\cdot)max(0,⋅)는 ReLU입니다. 일반화하면 FFN(h)=W2 σ(W1h+b1)+b2\mathrm{FFN}(h)=W_2\,\sigma(W_1 h+b_1)+b_2FFN(h)=W2​σ(W1​h+b1​)+b2​에서 σ\sigmaσ는 GELU 등 비선형, W1W_1W1​은 dmodel→dffd_{model}\to d_{ff}dmodel​→dff​, W2W_2W2​는 dff→dmodeld_{ff}\to d_{model}dff​→dmodel​ 선형층입니다.같은 FFN 가중치를 모든 위치에 적용하는 파라미터 공유는 데이터가 적은 위치에서도 일반화를 돕고, 구현을 단순화합니다.
위
왼쪽부터 읽는 방향으로, 각 칸마다 말의 뜻과 몇 번째인지를 숫자로 만든 정보(PE) 를 한데 더해요.
아래
줄끼리는 서로 섞지 않고, 똑같은 계산 블록(같은 가중치로 하는 같은 연산)을 네 줄이 각각 한 번씩 거쳐요.
논문에서는 이 계산 블록을 FFN이라고 부릅니다.

① 입력 만들기 → (중간 단계 생략) → ② 줄마다 같은 FFN

① 다음에
②가 같은 블록 안에서 차례로 진행돼요.
한 블록 안 순서
①→②
① 먼저 뜻 + 순서(PE) 를 더해 입력을 만듭니다. (가운데 어텐션 등은 그림에서 생략)
② 그다음 같은 FFN으로 줄마다 한 번씩 다듬어요. 줄끼리는 서로 안 섞어요.
① 말의 뜻 + 순서 숫자(PE)를 한데 모으기
문장 안에서 몇 번째 단어인지 숫자로 적어 두는 것과 비슷해요.
A→B→C→D0번호뜻+순서값A 합친뜻+순서값1번호뜻+순서값B 합친뜻+순서값2번호뜻+순서값C 합친뜻+순서값3번호뜻+순서값D 합친뜻+순서값
↓② 같은 계산 블록으로 줄마다 한 번씩 다듬기 (FFN)네 줄은 서로 안 섞고, 똑같은 계산 블록만 통과줄 1입력넓힌 층비선형출력줄 2입력넓힌 층비선형출력줄 3입력넓힌 층비선형출력줄 4입력넓힌 층비선형출력
네 칸 모두 같은 계산 블록 (가중치 W₁, W₂ 공유)
① 말의 뜻 + 순서 숫자(PE)를 한데 모으기
문장 안에서 몇 번째 단어인지 숫자로 적어 두는 것과 비슷해요.
A→B0번호뜻+순서값A 합친뜻+순서값1번호뜻+순서값B 합친뜻+순서값C→D2번호뜻+순서값C 합친뜻+순서값3번호뜻+순서값D 합친뜻+순서값
↓② 같은 계산 블록으로 줄마다 한 번씩 다듬기 (FFN)네 줄은 서로 안 섞고, 똑같은 계산 블록만 통과줄 1입력넓힌 층비선형출력줄 2입력넓힌 층비선형출력줄 3입력넓힌 층비선형출력줄 4입력넓힌 층비선형출력
네 칸 모두 같은 계산 블록 (가중치 W₁, W₂ 공유)
번호뜻순서값계산 블록(FFN)

트랜스포머: 위치 인코딩과 피드포워드

1. 개념: 왜 위치 인코딩이 필요한가? (영화관 좌석표)
셀프 어텐션은 문장을 한 번에 뭉텅이로 처리하기 때문에 단어의 '순서'를 모릅니다. 즉, "아빠가 방에 들어간다"와 "가방에 아빠가 들어간다"를 똑같이 인식할 위험이 있습니다. 위치 인코딩(Positional Encoding)은 각 단어 벡터에 위치 정보가 담긴 벡터 PE(p)PE(p)PE(p)를 더해주는(Add) 과정입니다.
직관적 이해: 영화관표에 "어벤져스(단어 의미)"만 적혀 있고 "좌석 번호(위치)"가 없다면 대혼란이 오겠죠? PE는 각 단어의 목에 "나는 1번 단어다", "나는 2번 단어다"라는 고유한 좌석 번호 띠를 걸어주는 것과 같습니다.
2. 개념: 사인·코사인 위치 인코딩 (시계 비유로 이해하기)
먼저 직관만: 벽시계를 떠올려 보세요. 초침은 빨리 돌고, 분침은 중간, 시침은 아주 천천히 돕니다. 세 바늘이 가리키는 방향이 만드는 모양만 보면 "지금 몇 시 몇 분"처럼 이 단어가 문장에서 몇 번째인지를 짚는 데 도움이 됩니다. 바늘마다 도는 속도가 다르기 때문에 두 시각이 가까운지 먼지(상대 거리)도 같이 읽기 쉬워집니다. 사인·코사인 PE도 느린 파동·빠른 파동을 여러 겹 포개서, 위치마다 서로 다른 숫자 패턴을 만드는 느낌과 비슷합니다.
한 단계만 더: 옛 트랜스포머 논문은 위치마다 벡터를 만들 때, 차원을 나누어 어떤 칸에는 sin⁡\sinsin처럼 돌아가며 반복되는 값, 짝을 이루는 칸에는 cos⁡\coscos 형태를 넣습니다. 주파수를 여러 단계로 두면 가까운 칸과 먼 칸을 모델이 구분하기 쉬워집니다.
수식 (외울 필요 없음, 참고용): 짝수 차원 2i2i2i에는 PE(t,2i)=sin⁡(t/100002i/dmodel)PE(t, 2i) = \sin(t / 10000^{2i/d_{model}})PE(t,2i)=sin(t/100002i/dmodel​), 홀수 차원 2i+12i+12i+1에는 같은 지수를 쓴 cos⁡(⋯ )\cos(\cdots)cos(⋯)가 전형적입니다. ttt는 몇 번째 토큰인지, iii는 벡터의 몇 번째 차원인지, dmodeld_{model}dmodel​은 벡터 길이입니다.
쉬운 풀이: 위 식은 "몇 번째 자리(ttt)마다 숫자로 된 위치 지문을 하나 만든다"고 보면 됩니다. 벡터는 길이 dmodeld_{model}dmodel​인 여러 칸이고, 칸을 둘씩 짝지어 한 바퀴 도는 속도가 다른 파동을 넣습니다. ttt는 "문장에서 몇 번째 토큰인가"이고, iii는 "그 긴 벡터에서 몇 번째 주파수(느린 파~빠른 파)를 쓰는가"에 가깝습니다. dmodeld_{model}dmodel​은 전체 길이라서, 지수 안에서 파동이 너무 빠르거나 느리지 않게 스케일을 맞출 때 등장합니다. 바로 옆 자리(ttt가 1만큼 차이)면 파동 값이 조금씩만 바뀌고, 멀리 떨어진 자리는 패턴이 더 달라지기 쉬워 "누가 앞·뒤인지" 같은 상대 거리를 읽는 데 도움이 됩니다. sin⁡\sinsin과 cos⁡\coscos를 짝으로 두면, 마치 바늘이 도는 각도를 두 숫자로 적는 것처럼 한 위치를 더 안정적으로 표현할 수 있습니다(세부는 외울 필요 없음).
활용: 긴 문맥 인코더 등; 이후 모델은 학습형 위치 임베딩·RoPE 등 다른 방식으로도 발전했습니다.
3. 개념: 피드포워드(FFN) — 단어 하나씩 ‘깊은 상담’
한 줄 요약: 어텐션은 단어들이 서로 섞이며 문맥을 나누는 단계이고, FFN은 그다음에 각 단어 줄을 따로 유지한 채 같은 계산을 줄마다 한 번씩 하는 단계입니다(위쪽 그림의 초록 계산 블록이 이 역할에 가깝습니다).
비유: 회의에서 다 같이 이야기(어텐션)한 뒤, 참가자 한 명씩 상담실에 들어가 개인 면담(FFN)을 받는 것과 비슷합니다. 숫자 벡터의 길이(dmodeld_{model}dmodel​)를 잠깐 넓혔다가(중간을 크게) 다시 원래 길이로 줄이는 모래시계 모양이 흔합니다.
왜 필요할까요? 어텐션만으로는 ‘곱·더하기’ 위주라 표현이 한계에 걸릴 수 있습니다. FFN 안에 ReLU(max⁡(0,⋅)\max(0,\cdot)max(0,⋅))처럼 0보다 작은 값을 잘라 내는 비틀기를 넣어, 직선만으로는 못 그리는 굴곡진 패턴을 배우게 합니다.
수식(참고): FFN(x)=max⁡(0,xW1+b1)W2+b2\mathrm{FFN}(x) = \max(0, xW_1 + b_1)W_2 + b_2FFN(x)=max(0,xW1​+b1​)W2​+b2​. 보통 모든 위치가 같은 W1,W2W_1,W_2W1​,W2​를 씁니다.
4. 개념: 한 블록 안 흐름 — 컨베이어 한 칸
한 줄 요약: 인코더 블록 하나는 공장 작업 라인 한 칸처럼, 항상 같은 순서로만 돌아갑니다.
쉬운 순서:
1. 출발 준비: 단어 임베딩에 위치(PE)를 더해 ‘몇 번째 단어인지’가 붙은 상태로 만듭니다.
2. 같이 섞기: 어텐션으로 단어들이 서로 문맥을 주고받습니다.
3. 안 잊게 잇기: Add & Norm — 아까 값을 조금 더해 받침(잔차)을 두고, 숫자 크기를 가지런히(층 정규화) 맞춥니다.
4. 각자 손보기: FFN으로 각 단어 줄을 비선형으로 다듬습니다.
5. 다시 Add & Norm으로 한 번 더 정리합니다.
수식(참고): 먼저 h′=LayerNorm(h+Attn(h))h' = \mathrm{LayerNorm}(h + \mathrm{Attn}(h))h′=LayerNorm(h+Attn(h)), 이어서 h′′=LayerNorm(h′+FFN(h′))h'' = \mathrm{LayerNorm}(h' + \mathrm{FFN}(h'))h′′=LayerNorm(h′+FFN(h′)). 이 한 덩어리를 여러 겹 쌓으면 표현이 점점 풍부해집니다.

왜 중요한지

순서가 곧 문법이고 의미다
자연어 처리에서 순서 정보는 생명입니다. "내가 너를 이겼다"와 "너를 내가 이겼다"는 같은 단어들로 이루어졌지만, 주어와 목적어의 위치가 결론을 완전히 바꿉니다. 위치 인코딩이 없다면 챗봇은 말의 앞뒤 문맥을 전혀 파악하지 못하는 바보가 됩니다. 금융 사기 탐지 모델에서도 "비밀번호 변경 →\rightarrow→ 큰 금액 이체"라는 사건의 순서가 사기 여부를 결정짓는 핵심 키입니다.
FFN, 복잡성을 돌파하는 비선형의 마법
어텐션 연산은 사실 행렬들의 단순한 곱셈과 덧셈(선형 연산) 위주로 굴러갑니다. 선형 연산만으로는 복잡한 현실 세계의 데이터를 다 담을 수 없습니다. FFN은 중간에 ReLU\mathrm{ReLU}ReLU나 GELU\mathrm{GELU}GELU 같은 비선형 활성화 함수를 개입시켜, 모델이 "A면 B다"라는 단순한 규칙을 넘어 "A와 B가 동시에 등장하고 C가 없다면 D다" 같은 고차원적인 복잡한 규칙을 학습할 수 있게 만듭니다.
성능과 비용의 저울질 (트레이드오프)
FFN의 중간 차원 크기(dffd_{ff}dff​)를 한없이 키우면 모델은 더 똑똑해지겠지만, 반대로 GPU 연산량과 메모리 소모가 극심해집니다. 실제 AI 서비스를 운영할 때 사용자에게 빠르게 답변을 주려면(지연 시간 단축), 이 FFN의 덩치를 얼마나 적절하게 조절하느냐가 머신러닝 엔지니어의 핵심 역량입니다.
최신 LLM으로 이어지는 불변의 진리
초기 트랜스포머의 사인·코사인 위치 인코딩은 이후 BERT의 '학습형 위치 임베딩', LLaMA 모델에서 쓰는 'RoPE(회전 위치 인코딩)' 등으로 발전해 왔습니다. 하지만 "순서 정보를 텐서(숫자)로 만들어서 주입한다"는 철학 자체는 변하지 않았으며, 어텐션과 FFN의 결합 구조 역시 현존하는 모든 최고 수준 AI 모델들의 뼈대 역할을 하고 있습니다.

어떻게 쓰이는지

실무 파이프라인: 밑작업의 정석 (토큰화 →\rightarrow→ 임베딩 →\rightarrow→ 위치 추가)
텍스트 데이터가 들어오면 개발자는 토크나이저로 문장을 잘게 쪼갭니다. 이를 임베딩 차원(예: 512차원)의 숫자로 바꾼 직후, 정확히 똑같은 512차원 크기의 위치 인코딩 벡터를 단순히 더해줍니다(+). (이어 붙이는 Concat이 아니라 Add입니다!) 실무에서 허깅페이스(Hugging Face) 라이브러리를 쓸 때는 `max_position_embeddings`라는 파라미터로 모델이 한 번에 읽을 수 있는 최대 문장 길이를 설정합니다.
FFN 하이퍼파라미터 튜닝의 예술
실무 코드를 보면 `intermediate_size`라는 변수가 바로 FFN의 확장 차원(dffd_{ff}dff​)입니다. 대체로 입력 차원(dmodeld_{model}dmodel​)의 4배 크기를 주는 것이 국룰처럼 쓰입니다(예: 768→3072768 \rightarrow 3072768→3072). 만약 여러분이 복잡한 프로그래밍 코드를 생성하는 AI를 만든다면, 이 중간 차원을 더 넓히고 깊게 만들어서 코드의 깐깐한 문법과 스타일을 모델이 더 세밀하게 외우도록 유도할 수 있습니다.
디코더(생성형 AI)에서의 위치 정보 주의점
GPT 같은 디코더 전용 모델은 문장을 생성할 때 미래의 단어를 미리 커닝하지 못하도록 마스크(Masking)를 씌웁니다. 하지만 위치 인코딩은 여전히 왼쪽에서 오른쪽으로 흐르는 절대적인 순서 번호를 충실히 매겨줍니다. 이 컨텍스트 길이(문맥을 기억하는 길이)와 위치 처리 능력이 결국 챗봇이 과거 대화를 얼마나 잘 기억하는지(성능)를 결정합니다.
디버깅 꿀팁 (어디를 고쳐야 할까?)
내가 만든 모델이 긴 문서를 읽다가 앞부분 내용을 자꾸 까먹거나 순서를 헷갈린다면? 가장 먼저 위치 인코딩 방식(학습형인지, RoPE인지)과 설정된 최대 길이를 점검해야 합니다. 반대로 모델이 문맥은 아는데 말투가 너무 단조롭거나 미묘한 뉘앙스를 못 잡는다면? FFN의 층수나 폭(dffd_{ff}dff​), 혹은 활성화 함수를 변경해 모델의 '표현력(Capacity)' 자체를 끌어올려야 합니다.

요약

트랜스포머가 문맥을 잘 보는 이유의 절반은 셀프 어텐션에 있지만, 문장이 가진 순서와 어느 칸의 토큰인지를 안정적으로 모델에 전달하려면 위치 정보를 별도로 실어 보내는 과정이 필요합니다. 전통적인 사인·코사인 위치 인코딩은 여러 주파수의 파동을 겹쳐 위치마다 서로 다른 패턴의 벡터를 만들고, 이를 토큰 임베딩에 더해 초기 표현을 완성합니다. 이후 블록에서는 어텐션이 토큰들 사이의 관계를 조정하고, 피드포워드 층은 그 결과를 토큰별로 동일한 비선형 변환을 반복 적용해 표현을 깊게 다듬습니다. 이때 중간 차원을 넓혔다가 다시 줄이는 구조는 연산 비용과 표현력 사이의 현실적인 타협점이 되며, 번역·요약·분류·생성 같은 응용 전반에서 공통으로 등장하는 설계입니다.

문제 풀이를 위한 설명

정리 — 위치 인코딩은 토큰 임베딩에 순서 정보를 더해, 셀프 어텐션이 놓치기 쉬운 "몇 번째인지"를 보강합니다. 사인·코사인 PE는 여러 주파수의 sin⁡/cos⁡\sin/\cossin/cos로 위치 벡터를 만들고, FFN은 각 위치에서 동일한 MLP로 비선형 표현을 쌓습니다. 실무에서는 dffd_{ff}dff​·층 수·컨텍스트 길이가 성능·비용과 함께 움직입니다.
  • 유형위치 인코딩 목적
  • 풀이·예시 (키워드 → 정답)순서·절대/상대 위치 정보 주입 → 개념 선택 시 "임베딩+PE"
  • 유형사인·코사인 PE
  • 풀이·예시 (키워드 → 정답)짝수 차원 sin⁡\sinsin, 홀수 차원 cos⁡\coscos 대응(전형적 구성) → 기호 확인
  • 유형가산형 PE
  • 풀이·예시 (키워드 → 정답)h=x+PE(pos)h = x + PE(pos)h=x+PE(pos) 형태로 더함 → 합성 벡터
  • 유형FFN 역할
  • 풀이·예시 (키워드 → 정답)토큰별 MLP, 보통 dff>dmodeld_{ff} > d_{model}dff​>dmodel​ → 비선형 확장
  • 유형파라미터 공유
  • 풀이·예시 (키워드 → 정답)위치마다 다른 FFN이 아니라 동일 가중치 공유가 일반적 → 구조 이해
  • 유형트레이드오프
  • 풀이·예시 (키워드 → 정답)dffd_{ff}dff​·깊이 증가 ↔ 연산량·지연 증가 → 서비스 튜닝
유형풀이·예시 (키워드 → 정답)
위치 인코딩 목적순서·절대/상대 위치 정보 주입 → 개념 선택 시 "임베딩+PE"
사인·코사인 PE짝수 차원 sin⁡\sinsin, 홀수 차원 cos⁡\coscos 대응(전형적 구성) → 기호 확인
가산형 PEh=x+PE(pos)h = x + PE(pos)h=x+PE(pos) 형태로 더함 → 합성 벡터
FFN 역할토큰별 MLP, 보통 dff>dmodeld_{ff} > d_{model}dff​>dmodel​ → 비선형 확장
파라미터 공유위치마다 다른 FFN이 아니라 동일 가중치 공유가 일반적 → 구조 이해
트레이드오프dffd_{ff}dff​·깊이 증가 ↔ 연산량·지연 증가 → 서비스 튜닝
예시 (개념 이해 문제)
"셀프 어텐션만으로 순서가 항상 완벽히 드러난다.
① 참
② 부분적으로만
③ 순서는 중요 없다"
완전하진 않아 PE 등으로 보강합니다. → 정답 선택지 2

"전형적인 사인·코사인 PE에서 짝수 차원 2i2i2i에 쓰는 함수는?
① 코사인만
② 사인
③ 항등"
PE(t,2i)=sin⁡(⋯ )PE(t,2i)=\sin(\cdots)PE(t,2i)=sin(⋯) 꼴이 흔합니다. → 정답 2

"토큰 임베딩 xxx와 위치 벡터 PEPEPE를 합칠 때 가장 흔한 방식은?
① 더하기 x+PE(pos)x+PE(pos)x+PE(pos)
② 이어붙이기만
③ 원소별 곱만"
가산형(add) PE가 일반적입니다. → 정답 1

"FFN(피드포워드) 블록의 역할에 가장 가까운 것은?
① 토큰 간 관계를 만드는 어텐션
② 각 토큰 표현을 토큰별로 비선형 변환
③ 드롭아웃만 적용"
토큰별 MLP에 가깝습니다. → 정답 2

"같은 레이어에서 각 위치의 FFN을 쓰는 일반적인 방식은?
① 위치마다 다른 W1,W2W_1,W_2W1​,W2​
② 모든 위치가 같은 FFN 가중치 공유
③ PE 행렬만 공유"
파라미터 공유가 일반적입니다. → 정답 2

"중간 차원 dffd_{ff}dff​나 블록 깊이를 늘리면 흔히 동시에 고려해야 하는 비용은?
① 항상 속도 향상만
② 연산량·메모리·지연
③ 데이터 라벨 수"
표현력과 비용의 트레이드오프입니다. → 정답 2

예시 (O/X 문제)
"FFN은 토큰마다 다른 가중치를 반드시 써야 한다. 맞으면 1, 틀리면 0."
보통은 공유 가중치입니다. → 정답 0

예시 (시나리오 기반 문제)
"의료 기록에서 투약 전후 순서가 중요하다. 우선 보강할 입력은?
① 임베딩+PE
② 픽셀만
③ 파일명만"
순서 신호가 필요합니다. → 정답 1

예시 (투표 결과 계산 문제)
"표시 벡터 [1,1,0,1,0]에서 1의 개수는?"
1+1+0+1+0=31+1+0+1+0=31+1+0+1+0=3. → 정답 3

예시 (모델 예측 집계 문제)
"세 블록 점수 [2,1,2]의 합은?"
2+1+2=52+1+2=52+1+2=5. → 정답 5

예시 (모델 구성 계산 문제)
"토큰 수 10일 때 self-attention 점수 행렬 원소 수는?"
10×10=10010\times10=10010×10=100. → 정답 100

예시 (앙상블 원리 이해 문제)
"여러 층을 쌓는 목적에 가장 가까운 것은?
① 단계적 추상화
② 데이터 삭제
③ 입력 금지"
층을 쌓아 표현을 단계적으로 만듭니다. → 정답 1