본 콘텐츠의 이미지 및 내용은 AI로 생성되었습니다.
본 콘텐츠의 이미지 및 내용을 무단으로 복제, 배포, 수정하여 사용할 경우 저작권법에 의해 법적 제재를 받을 수 있습니다.
이미지 로딩 중...
AI Generated
2025. 12. 4. · 15 Views
트랜스포머와 LLM 완벽 가이드
딥러닝의 혁명이라 불리는 트랜스포머 아키텍처부터 GPT, BERT까지. 초급 개발자도 이해할 수 있도록 Seq2Seq에서 시작해 어텐션 메커니즘의 원리를 파헤치고, 대규모 언어 모델의 세계로 안내합니다.
목차
1. Seq2Seq 모델 이해
김개발 씨는 요즘 번역 서비스를 만들어보고 싶다는 생각에 빠져 있습니다. "영어 문장을 넣으면 한국어로 바꿔주는 모델, 어떻게 만들지?" 선배 박시니어 씨에게 물어보니 "일단 Seq2Seq부터 알아야 해"라는 답이 돌아왔습니다.
Seq2Seq(Sequence-to-Sequence)는 한 시퀀스를 다른 시퀀스로 변환하는 모델입니다. 마치 통역사가 영어 문장을 듣고 한국어로 말해주는 것처럼, 입력 문장 전체를 이해한 뒤 새로운 문장을 생성합니다.
이 구조를 이해하면 번역, 요약, 챗봇 등 다양한 자연어 처리 태스크의 기반을 파악할 수 있습니다.
다음 코드를 살펴봅시다.
import torch
import torch.nn as nn
# 인코더: 입력 시퀀스를 고정 길이 벡터로 압축
class Encoder(nn.Module):
def __init__(self, input_dim, emb_dim, hidden_dim):
super().__init__()
self.embedding = nn.Embedding(input_dim, emb_dim)
self.rnn = nn.LSTM(emb_dim, hidden_dim, batch_first=True)
def forward(self, src):
# src: [batch, src_len]
embedded = self.embedding(src) # [batch, src_len, emb_dim]
outputs, (hidden, cell) = self.rnn(embedded)
# hidden이 바로 "문맥 벡터"입니다
return hidden, cell
김개발 씨는 입사 6개월 차 주니어 개발자입니다. 최근 회사에서 간단한 번역 기능을 검토해보라는 업무를 받았습니다.
구글 번역이 어떻게 동작하는지 궁금해진 김개발 씨는 자연어 처리 공부를 시작했습니다. 선배 박시니어 씨가 커피를 건네며 말했습니다.
"번역 모델을 이해하려면 Seq2Seq부터 알아야 해. 2014년에 나온 개념인데, 지금 나온 모든 언어 모델의 조상 격이라고 보면 돼." 그렇다면 Seq2Seq란 정확히 무엇일까요?
쉽게 비유하자면, Seq2Seq는 마치 동시통역사와 같습니다. 동시통역사는 연사의 말을 끝까지 듣고 내용을 머릿속에 정리한 뒤, 다른 언어로 옮겨 말합니다.
이처럼 Seq2Seq도 입력 문장 전체를 하나의 벡터로 압축한 뒤, 그 벡터를 기반으로 새로운 문장을 생성합니다. Seq2Seq 이전에는 어떻게 번역을 했을까요?
규칙 기반 번역 시스템에서는 문법 규칙과 사전을 일일이 정의해야 했습니다. "I love you"를 "나는 너를 사랑해"로 바꾸려면 주어, 목적어, 동사의 위치를 바꾸는 규칙을 수작업으로 만들어야 했습니다.
언어마다 문법이 다르니 규칙도 천차만별이었고, 예외 상황을 처리하기가 거의 불가능했습니다. 바로 이런 문제를 해결하기 위해 Seq2Seq가 등장했습니다.
Seq2Seq는 인코더와 디코더라는 두 개의 신경망으로 구성됩니다. 인코더는 입력 문장을 읽어서 문맥 벡터(context vector)라는 고정 길이 벡터로 압축합니다.
디코더는 이 문맥 벡터를 받아서 한 단어씩 출력을 생성합니다. 위의 코드를 살펴보겠습니다.
먼저 Encoder 클래스를 보면 Embedding 레이어와 LSTM 레이어가 있습니다. Embedding은 단어를 숫자 벡터로 바꿔주는 역할을 합니다.
LSTM은 순서대로 단어를 읽으면서 정보를 축적합니다. 마지막에 반환되는 hidden이 바로 문맥 벡터입니다.
이 벡터 하나에 입력 문장의 모든 의미가 담겨 있습니다. 실제 현업에서는 어떻게 활용할까요?
Seq2Seq는 번역뿐 아니라 텍스트 요약, 대화 시스템, 코드 생성 등 다양한 분야에 적용됩니다. 예를 들어 긴 뉴스 기사를 한 줄 요약으로 바꾸거나, 사용자의 질문에 적절한 답변을 생성하는 챗봇을 만들 때 이 구조가 기반이 됩니다.
하지만 주의할 점도 있습니다. Seq2Seq의 가장 큰 약점은 병목 현상입니다.
아무리 긴 문장이라도 고정된 크기의 벡터 하나로 압축해야 합니다. 마치 백과사전 내용을 포스트잇 한 장에 요약하라는 것과 같습니다.
문장이 길어질수록 정보 손실이 심해지고, 번역 품질이 떨어집니다. 다시 김개발 씨의 이야기로 돌아가 봅시다.
박시니어 씨의 설명을 들은 김개발 씨는 고개를 끄덕이며 물었습니다. "그럼 긴 문장은 어떻게 처리해요?" 박시니어 씨가 웃으며 답했습니다.
"그래서 어텐션이 나온 거야. 다음에 알려줄게."
실전 팁
💡 - 인코더와 디코더는 서로 다른 언어의 문법을 각각 학습합니다
- 문맥 벡터의 크기가 너무 작으면 정보 손실이 발생하므로 적절한 hidden_dim 설정이 중요합니다
2. 어텐션 메커니즘 원리
김개발 씨는 Seq2Seq로 간단한 번역 모델을 만들어봤습니다. 그런데 짧은 문장은 괜찮은데 긴 문장은 엉망이었습니다.
"왜 긴 문장만 이상하게 번역되지?" 박시니어 씨가 답했습니다. "문맥 벡터 하나에 모든 걸 담으려니 그렇지.
어텐션을 써봐."
어텐션(Attention)은 디코더가 출력을 생성할 때 입력의 어떤 부분에 집중할지 결정하는 메커니즘입니다. 마치 시험 문제를 풀 때 관련된 교과서 부분만 찾아보는 것처럼, 필요한 정보가 있는 곳을 선택적으로 참조합니다.
이를 통해 긴 문장도 정확하게 처리할 수 있게 됩니다.
다음 코드를 살펴봅시다.
import torch
import torch.nn as nn
import torch.nn.functional as F
class Attention(nn.Module):
def __init__(self, hidden_dim):
super().__init__()
# 어텐션 스코어 계산용 레이어
self.attn = nn.Linear(hidden_dim * 2, hidden_dim)
self.v = nn.Linear(hidden_dim, 1, bias=False)
def forward(self, hidden, encoder_outputs):
# hidden: [batch, hidden_dim] - 디코더의 현재 상태
# encoder_outputs: [batch, src_len, hidden_dim]
src_len = encoder_outputs.shape[1]
hidden = hidden.unsqueeze(1).repeat(1, src_len, 1)
# 어텐션 스코어 계산
energy = torch.tanh(self.attn(torch.cat((hidden, encoder_outputs), dim=2)))
attention = self.v(energy).squeeze(2) # [batch, src_len]
# 소프트맥스로 확률 분포 생성
return F.softmax(attention, dim=1)
김개발 씨는 Seq2Seq 모델의 한계를 직접 경험했습니다. "The weather is nice today so I decided to go for a walk in the park"라는 문장을 번역하니 앞부분은 잘 나오는데 뒷부분이 이상했습니다.
문맥 벡터 하나에 모든 정보를 담기엔 문장이 너무 길었던 것입니다. 박시니어 씨가 화이트보드에 그림을 그리며 설명을 시작했습니다.
"사람이 번역할 때 어떻게 해? 전체 문장을 외운 다음에 번역하는 게 아니라, 번역하면서 원문을 계속 참조하잖아.
어텐션도 똑같아." 그렇다면 어텐션이란 정확히 무엇일까요? 쉽게 비유하자면, 어텐션은 마치 형광펜과 같습니다.
시험 공부를 할 때 교과서 전체를 외우려 하지 않습니다. 문제를 읽고, 관련된 부분에 형광펜으로 표시한 곳을 찾아봅니다.
어텐션도 마찬가지로 현재 번역하려는 단어와 관련된 입력 단어들을 찾아서 가중치를 부여합니다. 어텐션의 핵심 아이디어는 선택적 참조입니다.
디코더가 "날씨"라는 단어를 출력할 때는 입력의 "weather"에 높은 가중치를 부여합니다. "공원"을 출력할 때는 "park"에 집중합니다.
이렇게 출력 단어마다 입력의 다른 부분을 참조하니, 아무리 긴 문장이라도 필요한 정보를 놓치지 않습니다. 위의 코드를 살펴보겠습니다.
forward 함수에서 hidden은 디코더의 현재 상태입니다. "지금 어떤 단어를 출력하려고 해"라는 정보가 담겨 있습니다.
encoder_outputs는 인코더가 입력을 처리하면서 생성한 모든 상태입니다. 이 둘을 결합해서 어텐션 스코어를 계산합니다.
마지막에 softmax를 적용하면 모든 입력 단어에 대한 확률 분포가 나옵니다. 어텐션 스코어가 높은 단어일수록 현재 출력과 관련이 깊다는 뜻입니다.
예를 들어 "I love coffee"를 "나는 커피를 좋아한다"로 번역할 때, "좋아한다"를 생성하는 시점에서는 "love"의 어텐션 스코어가 가장 높게 나옵니다. 이 스코어를 가중치로 사용해서 encoder_outputs를 가중 합산하면, "love"의 정보가 많이 반영된 문맥 벡터가 만들어집니다.
실제 현업에서 어텐션은 필수 요소가 되었습니다. 구글 번역, 파파고 등 현대의 모든 번역 서비스는 어텐션을 사용합니다.
단순히 번역뿐 아니라 이미지 캡셔닝, 음성 인식, 질의응답 시스템 등 입력과 출력 사이의 정렬이 필요한 모든 태스크에 적용됩니다. 하지만 초기 어텐션에도 한계가 있었습니다.
디코더가 출력을 생성할 때만 어텐션을 사용했습니다. 인코더에서 입력을 처리할 때는 여전히 순차적으로 처리했습니다.
LSTM이 단어를 하나씩 읽어야 하니 병렬 처리가 불가능했고, 학습 속도가 느렸습니다. 김개발 씨가 어텐션을 적용하니 번역 품질이 확실히 좋아졌습니다.
"그런데 선배, 인코더에서도 어텐션을 쓸 수는 없나요?" 박시니어 씨가 환하게 웃었습니다. "그게 바로 셀프 어텐션이야.
트랜스포머의 핵심이지."
실전 팁
💡 - 어텐션 가중치를 시각화하면 모델이 어떤 단어에 집중하는지 확인할 수 있습니다
- 어텐션은 모델의 해석 가능성을 높여주는 부가 효과도 있습니다
3. 셀프 어텐션 상세
김개발 씨는 어텐션의 원리를 이해하고 나니 욕심이 생겼습니다. "입력을 처리할 때도 어텐션을 쓰면 더 좋지 않을까?" 박시니어 씨가 대답했습니다.
"바로 그 아이디어가 셀프 어텐션이야. 트랜스포머의 심장 같은 존재지."
셀프 어텐션(Self-Attention)은 하나의 시퀀스 내에서 각 단어가 다른 모든 단어와의 관계를 파악하는 메커니즘입니다. 마치 소설을 읽을 때 앞에 나온 인물이 뒤에서 다시 언급되면 자연스럽게 연결 짓는 것처럼, 문장 내 단어들 사이의 관계를 학습합니다.
이를 통해 문맥을 더 풍부하게 이해할 수 있습니다.
다음 코드를 살펴봅시다.
import torch
import torch.nn as nn
import math
class SelfAttention(nn.Module):
def __init__(self, embed_dim):
super().__init__()
self.embed_dim = embed_dim
# Query, Key, Value를 생성하는 선형 변환
self.W_q = nn.Linear(embed_dim, embed_dim)
self.W_k = nn.Linear(embed_dim, embed_dim)
self.W_v = nn.Linear(embed_dim, embed_dim)
def forward(self, x):
# x: [batch, seq_len, embed_dim]
Q = self.W_q(x) # Query: "나는 무엇을 찾고 있는가"
K = self.W_k(x) # Key: "나는 어떤 정보를 가지고 있는가"
V = self.W_v(x) # Value: "실제로 전달할 정보"
# 어텐션 스코어 계산 (스케일링 적용)
scores = torch.matmul(Q, K.transpose(-2, -1)) / math.sqrt(self.embed_dim)
attn_weights = torch.softmax(scores, dim=-1)
# 가중 합산으로 출력 생성
return torch.matmul(attn_weights, V)
김개발 씨는 "The cat sat on the mat because it was tired"라는 문장을 보고 있었습니다. 여기서 "it"이 "cat"을 가리킨다는 것을 사람은 바로 알 수 있습니다.
하지만 컴퓨터는 어떻게 알 수 있을까요? 박시니어 씨가 설명을 시작했습니다.
"기존 RNN은 단어를 순서대로 처리하잖아. 'it'을 볼 때 'cat'은 이미 한참 전에 처리됐으니 연결하기 어려워.
셀프 어텐션은 모든 단어를 동시에 보면서 관계를 파악해." 그렇다면 셀프 어텐션이란 정확히 무엇일까요? 쉽게 비유하자면, 셀프 어텐션은 마치 독서 토론회와 같습니다.
모든 참가자가 같은 책을 읽고 모였습니다. 각자 자기가 맡은 챕터에 대해 질문을 던지고, 다른 참가자들은 관련된 정보를 제공합니다.
이 과정에서 책 전체의 맥락이 풍부하게 이해됩니다. 셀프 어텐션의 핵심은 Query, Key, Value 세 가지 개념입니다.
Query는 "나는 어떤 정보가 필요한가"입니다. Key는 "나는 어떤 정보를 가지고 있는가"입니다.
Value는 "실제로 전달할 내용"입니다. 도서관에서 책을 찾는 상황으로 비유하면, Query는 찾고 싶은 주제, Key는 책의 제목과 분류, Value는 책의 실제 내용입니다.
위의 코드를 단계별로 살펴보겠습니다. 먼저 입력 x가 세 개의 선형 변환을 거쳐 Q, K, V로 변환됩니다.
같은 입력에서 세 가지 역할을 하는 벡터가 만들어지는 것입니다. 그 다음 Q와 K의 내적으로 어텐션 스코어를 계산합니다.
내적 값이 크면 두 단어가 관련이 깊다는 뜻입니다. 스케일링이 중요합니다.
코드에서 math.sqrt(self.embed_dim)으로 나누는 부분이 있습니다. 차원이 커지면 내적 값도 커지는데, 이러면 softmax가 극단적인 값을 내놓습니다.
스케일링으로 이 문제를 방지합니다. 이것이 바로 Scaled Dot-Product Attention이라고 불리는 이유입니다.
다시 "it"과 "cat"의 관계로 돌아가 봅시다. "it"의 Query와 "cat"의 Key가 내적되면 높은 스코어가 나옵니다.
왜냐하면 둘 다 비슷한 의미 공간에 있기 때문입니다. 반면 "it"과 "mat"의 스코어는 낮습니다.
이렇게 셀프 어텐션은 문맥상 관련된 단어들을 자동으로 연결합니다. 셀프 어텐션의 가장 큰 장점은 병렬 처리입니다.
RNN은 단어를 하나씩 순서대로 처리해야 합니다. 10개 단어를 처리하려면 10번의 연산이 필요합니다.
반면 셀프 어텐션은 모든 단어 쌍의 관계를 한 번에 계산합니다. 행렬 연산으로 표현되니 GPU에서 효율적으로 처리됩니다.
하지만 셀프 어텐션에도 주의할 점이 있습니다. 모든 단어 쌍을 계산하므로 시퀀스 길이가 n이면 연산량이 n제곱에 비례합니다.
긴 문서를 처리할 때는 메모리와 시간이 많이 필요합니다. 이 문제를 해결하기 위해 Sparse Attention, Linear Attention 등 다양한 변형이 연구되고 있습니다.
김개발 씨는 셀프 어텐션의 원리를 이해하고 나니 궁금해졌습니다. "이걸로 모델 전체를 만들 수 있나요?" 박시니어 씨가 대답했습니다.
"그게 바로 트랜스포머야. RNN 없이 어텐션만으로 만든 아키텍처지."
실전 팁
💡 - Q, K, V는 같은 입력에서 나오지만 서로 다른 역할을 합니다
- 멀티 헤드 어텐션은 여러 개의 셀프 어텐션을 병렬로 수행하여 다양한 관계를 포착합니다
4. 트랜스포머 아키텍처
김개발 씨는 드디어 핵심에 도달했습니다. "Attention Is All You Need라는 논문 제목을 봤는데, 진짜 어텐션만으로 모든 걸 해결한다고요?" 박시니어 씨가 고개를 끄덕였습니다.
"2017년에 나온 그 논문이 딥러닝의 역사를 바꿨지. 트랜스포머 아키텍처를 알아보자."
트랜스포머(Transformer)는 셀프 어텐션을 핵심으로 하는 신경망 아키텍처입니다. RNN이나 CNN 없이 오직 어텐션과 피드포워드 네트워크만으로 구성됩니다.
마치 잘 설계된 공장 라인처럼 입력을 체계적으로 처리하여 놀라운 성능을 달성합니다. 현대 자연어 처리의 표준이 된 구조입니다.
다음 코드를 살펴봅시다.
import torch
import torch.nn as nn
class TransformerBlock(nn.Module):
def __init__(self, embed_dim, num_heads, ff_dim, dropout=0.1):
super().__init__()
# 멀티 헤드 셀프 어텐션
self.attention = nn.MultiheadAttention(embed_dim, num_heads, batch_first=True)
# 피드포워드 네트워크
self.ffn = nn.Sequential(
nn.Linear(embed_dim, ff_dim),
nn.ReLU(),
nn.Linear(ff_dim, embed_dim)
)
# 레이어 정규화와 드롭아웃
self.norm1 = nn.LayerNorm(embed_dim)
self.norm2 = nn.LayerNorm(embed_dim)
self.dropout = nn.Dropout(dropout)
def forward(self, x):
# 잔차 연결 + 레이어 정규화
attn_out, _ = self.attention(x, x, x)
x = self.norm1(x + self.dropout(attn_out))
ffn_out = self.ffn(x)
return self.norm2(x + self.dropout(ffn_out))
김개발 씨는 트랜스포머 아키텍처 그림을 보고 당황했습니다. 박스와 화살표가 복잡하게 얽혀 있었기 때문입니다.
박시니어 씨가 차근차근 설명을 시작했습니다. "겁먹지 마.
하나씩 뜯어보면 별거 아니야." 트랜스포머의 구조를 이해하려면 먼저 큰 그림을 봐야 합니다. 트랜스포머는 인코더와 디코더로 구성됩니다.
인코더는 입력을 이해하는 역할, 디코더는 출력을 생성하는 역할입니다. 각각은 동일한 구조의 블록이 여러 개 쌓인 형태입니다.
원래 논문에서는 6개의 블록을 사용했습니다. 트랜스포머 블록 하나를 자세히 살펴보겠습니다.
블록은 크게 두 부분으로 나뉩니다. 첫째는 멀티 헤드 셀프 어텐션입니다.
앞서 배운 셀프 어텐션을 여러 개 병렬로 수행합니다. 둘째는 피드포워드 네트워크(FFN)입니다.
간단한 두 개의 선형 레이어로 구성됩니다. 왜 멀티 헤드일까요?
쉽게 비유하면, 여러 명의 전문가가 같은 문장을 다른 관점에서 분석하는 것과 같습니다. 한 헤드는 문법적 관계에 집중하고, 다른 헤드는 의미적 관계에 집중할 수 있습니다.
이렇게 다양한 관점의 분석 결과를 합치면 더 풍부한 표현이 가능합니다. 위의 코드에서 중요한 부분은 잔차 연결(Residual Connection)입니다.
x + self.dropout(attn_out) 부분을 보세요. 어텐션의 출력에 원래 입력을 더합니다.
이것이 잔차 연결입니다. 마치 고속도로의 지름길처럼, 그라디언트가 깊은 네트워크를 통해 원활하게 흐를 수 있게 해줍니다.
레이어가 아무리 많아져도 학습이 안정적으로 됩니다. 레이어 정규화(Layer Normalization)도 핵심입니다.
각 레이어의 출력을 정규화하여 학습을 안정화합니다. 배치 정규화와 달리 배치 크기에 의존하지 않아서 시퀀스 데이터에 더 적합합니다.
트랜스포머에는 한 가지 더 중요한 요소가 있습니다. 위치 인코딩(Positional Encoding)입니다.
셀프 어텐션은 순서 정보를 알지 못합니다. "I love you"나 "you love I"나 어텐션 계산 결과가 같습니다.
이 문제를 해결하기 위해 각 위치에 고유한 벡터를 더해줍니다. 사인과 코사인 함수를 사용하거나, 학습 가능한 벡터를 사용합니다.
실제 트랜스포머는 이 블록을 여러 개 쌓습니다. 층이 깊어질수록 더 추상적이고 고차원적인 특징을 학습합니다.
초기 층에서는 단어의 형태나 품사 같은 저수준 정보를, 후기 층에서는 문장의 의미나 맥락 같은 고수준 정보를 파악합니다. 디코더에는 한 가지 추가 요소가 있습니다.
디코더의 셀프 어텐션은 마스킹을 사용합니다. 현재 위치 이후의 단어들을 보지 못하게 가립니다.
번역할 때 아직 생성하지 않은 단어를 미리 볼 수는 없으니까요. 이를 인과적 마스킹(Causal Masking) 또는 미래 마스킹이라고 부릅니다.
김개발 씨는 트랜스포머의 구조를 이해하고 나니 감탄했습니다. "정말 우아한 설계네요." 박시니어 씨가 덧붙였습니다.
"그래서 GPT도, BERT도, 모든 현대 언어 모델이 이 구조를 기반으로 해."
실전 팁
💡 - 레이어 수, 헤드 수, 임베딩 차원은 모델 크기를 결정하는 주요 하이퍼파라미터입니다
- 위치 인코딩 없이는 트랜스포머가 단어 순서를 구분할 수 없습니다
5. 트랜스포머가 빛나는 이유
김개발 씨는 궁금했습니다. "RNN도 있고 CNN도 있는데, 왜 하필 트랜스포머가 대세가 됐나요?" 박시니어 씨가 웃으며 대답했습니다.
"좋은 질문이야. 트랜스포머가 왜 특별한지 비교해보면 바로 알 수 있어."
트랜스포머가 자연어 처리의 표준이 된 데는 분명한 이유가 있습니다. 병렬 처리, 장거리 의존성 학습, 확장성이라는 세 가지 강점이 기존 모델들의 한계를 완벽하게 극복했기 때문입니다.
마치 증기기관이 수작업을 대체한 것처럼, 트랜스포머는 딥러닝의 패러다임을 바꿨습니다.
다음 코드를 살펴봅시다.
# RNN vs Transformer 시간 복잡도 비교
# 시퀀스 길이: n, 임베딩 차원: d
# RNN: 순차 처리 필요
# - 시간 복잡도: O(n) 순차 연산
# - 장거리 의존성: 그라디언트 소실 문제
# Transformer: 병렬 처리 가능
# - 시간 복잡도: O(1) 병렬 연산 (GPU 기준)
# - 장거리 의존성: 모든 위치 직접 연결
# 실험 예시: 1000 토큰 시퀀스 처리 시간
import time
import torch
def simulate_processing(seq_len, model_type):
if model_type == "RNN":
# RNN: 각 스텝이 이전 스텝에 의존
total_time = seq_len * 0.001 # 순차적
else:
# Transformer: 모든 토큰 동시 처리
total_time = 0.01 # 상수 시간 (병렬화)
return total_time
# RNN: 1초, Transformer: 0.01초 (100배 차이)
김개발 씨는 회사에서 자연어 처리 프로젝트를 맡게 되었습니다. 선배들이 예전에 만든 LSTM 기반 모델이 있었는데, 학습에 일주일이 걸린다고 했습니다.
"트랜스포머로 바꾸면 이틀이면 돼"라는 박시니어 씨의 말에 김개발 씨는 놀랐습니다. 트랜스포머의 첫 번째 강점은 병렬 처리입니다.
RNN은 태생적으로 순차적입니다. "나는 밥을 먹었다"라는 문장을 처리할 때 "나는"을 처리한 결과가 있어야 "밥을"을 처리할 수 있습니다.
100개 단어 문장이면 100번의 순차 연산이 필요합니다. GPU가 아무리 좋아도 병렬화가 불가능합니다.
반면 트랜스포머의 셀프 어텐션은 모든 단어를 동시에 처리합니다. 행렬 곱셈 한 번으로 모든 단어 쌍의 관계를 계산합니다.
GPU는 행렬 연산에 최적화되어 있으니, 수천 개의 코어가 동시에 일합니다. 마치 1000명이 한 줄로 서서 한 명씩 일하는 것과 1000명이 동시에 일하는 것의 차이입니다.
두 번째 강점은 장거리 의존성 학습입니다. RNN에서 첫 번째 단어의 정보가 열 번째 단어에 전달되려면 아홉 번의 연산을 거쳐야 합니다.
이 과정에서 정보가 희미해지거나 그라디언트가 사라집니다. 이것이 유명한 그라디언트 소실 문제입니다.
LSTM과 GRU가 이를 완화했지만 완전히 해결하지는 못했습니다. 트랜스포머에서는 모든 단어가 직접 연결됩니다.
첫 번째 단어와 백 번째 단어의 거리가 어텐션에서는 한 칸입니다. 정보가 직통으로 전달되니 장거리 의존성을 쉽게 학습합니다.
소설에서 처음에 나온 복선이 마지막에 회수되는 것을 이해하는 것과 같습니다. 세 번째 강점은 확장성입니다.
트랜스포머는 크기를 키우면 성능이 계속 좋아집니다. 레이어를 더 쌓고, 헤드를 더 추가하고, 임베딩 차원을 늘리면 됩니다.
구조가 규칙적이라 확장이 쉽습니다. GPT-3가 1750억 개의 파라미터를 가질 수 있었던 것도 이런 확장성 덕분입니다.
물론 트랜스포머에도 약점이 있습니다. 셀프 어텐션은 시퀀스 길이의 제곱에 비례하는 메모리를 사용합니다.
1000 토큰 문장은 100만 개의 어텐션 스코어를 계산해야 합니다. 아주 긴 문서를 처리하기 어려운 이유입니다.
이를 해결하기 위해 Longformer, BigBird 같은 효율적인 변형들이 연구되고 있습니다. 실제로 구글은 2017년 논문 발표 직후 번역 시스템을 트랜스포머로 교체했습니다.
훈련 시간은 8분의 1로 줄고, 번역 품질은 더 좋아졌습니다. 이후 모든 빅테크 기업이 트랜스포머를 채택했고, 자연어 처리를 넘어 컴퓨터 비전, 음성, 단백질 구조 예측까지 영역을 확장했습니다.
김개발 씨는 LSTM 모델을 트랜스포머로 교체했습니다. 학습 시간이 일주일에서 이틀로 줄었고, 정확도도 올랐습니다.
"이제 왜 다들 트랜스포머를 쓰는지 알겠어요." 박시니어 씨가 고개를 끄덕였습니다. "이제 이걸 기반으로 만들어진 LLM을 알아볼 차례야."
실전 팁
💡 - 트랜스포머의 병렬성은 GPU가 많을수록 더 큰 효과를 발휘합니다
- 시퀀스 길이가 매우 길다면 효율적인 어텐션 변형을 고려해보세요
6. 대규모 언어 모델 LLM
김개발 씨는 요즘 ChatGPT를 자주 사용합니다. "이게 어떻게 이렇게 똑똑하지?" 박시니어 씨가 설명했습니다.
"GPT는 트랜스포머를 기반으로 한 대규모 언어 모델이야. 엄청난 양의 데이터로 학습하면 저런 능력이 나오는 거지."
LLM(Large Language Model)은 수십억에서 수천억 개의 파라미터를 가진 거대한 언어 모델입니다. 트랜스포머 아키텍처를 기반으로 인터넷의 방대한 텍스트 데이터를 학습합니다.
마치 수백만 권의 책을 읽은 것처럼 언어에 대한 깊은 이해를 가지게 됩니다. GPT, BERT, LLaMA 등이 대표적인 LLM입니다.
다음 코드를 살펴봅시다.
# LLM의 규모 비교
llm_models = {
"GPT-2": {
"parameters": "1.5B", # 15억 파라미터
"training_data": "40GB", # 웹텍스트
"year": 2019
},
"GPT-3": {
"parameters": "175B", # 1750억 파라미터
"training_data": "570GB", # 책, 위키, 웹
"year": 2020
},
"GPT-4": {
"parameters": "~1.8T", # 추정 1조 8천억
"training_data": "Unknown", # 비공개
"year": 2023
}
}
# 스케일링 법칙: 모델이 커질수록 성능 향상
# Loss ∝ 1 / (Parameters^0.076)
# 파라미터 10배 증가 → 성능 약 17% 향상
김개발 씨는 GPT-3 논문을 읽다가 놀랐습니다. 파라미터가 1750억 개라니요.
자신이 만든 모델은 100만 개도 안 되는데 말입니다. "어떻게 이렇게 큰 모델을 학습시키죠?" 박시니어 씨가 차근차근 설명을 시작했습니다.
"LLM의 비밀은 크게 세 가지야. 규모, 데이터, 그리고 사전 학습이지." 첫 번째 비밀은 규모입니다.
연구자들은 신기한 현상을 발견했습니다. 모델을 키우면 단순히 성능이 좋아지는 것이 아니라, 어느 순간 새로운 능력이 창발(emergence)합니다.
작은 모델은 못하던 것을 큰 모델은 갑자기 할 수 있게 됩니다. 산술 추론, 논리적 사고, 코드 작성 같은 능력이 그렇습니다.
이것을 스케일링 법칙(Scaling Law)이라고 부릅니다. 파라미터 수, 학습 데이터 양, 연산량을 늘리면 성능이 예측 가능하게 향상됩니다.
OpenAI, DeepMind, Google 모두 이 법칙을 실험적으로 확인했습니다. 그래서 기업들이 경쟁적으로 더 큰 모델을 만드는 것입니다.
두 번째 비밀은 데이터입니다. LLM은 인터넷에 있는 거의 모든 텍스트를 학습합니다.
위키피디아, 책, 논문, 뉴스, 소셜 미디어, 코드 저장소까지. GPT-3는 약 5000억 개의 토큰을 학습했습니다.
책으로 치면 수백만 권에 해당하는 양입니다. 세 번째 비밀은 사전 학습(Pre-training)입니다.
LLM은 특정 작업을 위해 학습하지 않습니다. 대신 "다음 단어 예측"이라는 범용적인 작업으로 학습합니다.
"나는 오늘 _"이 주어지면 "아침에"가 올 확률, "학교에"가 올 확률 등을 계산합니다. 이 단순한 작업을 수천억 번 반복하면서 언어의 패턴을 익힙니다.
놀라운 것은 이 단순한 학습이 복잡한 능력으로 이어진다는 것입니다. 다음 단어를 잘 예측하려면 문법, 상식, 논리, 심지어 수학까지 이해해야 합니다.
"2+3="이 나오면 다음에 "5"가 올 확률이 가장 높아야 합니다. 이렇게 LLM은 명시적으로 가르치지 않은 것도 알아서 배웁니다.
사전 학습 후에는 미세 조정(Fine-tuning)을 합니다. 범용적인 언어 능력을 갖춘 모델을 특정 작업에 맞게 조정합니다.
대화에 특화시키면 ChatGPT가 되고, 코드에 특화시키면 GitHub Copilot이 됩니다. 적은 데이터로도 좋은 성능을 낼 수 있습니다.
이것을 전이 학습(Transfer Learning)이라고 합니다. 최근에는 RLHF(Reinforcement Learning from Human Feedback)라는 기법도 사용합니다.
인간이 모델의 답변을 평가하고, 이 피드백으로 모델을 더 학습시킵니다. 이렇게 하면 더 도움이 되고, 안전하고, 정직한 답변을 하도록 만들 수 있습니다.
ChatGPT가 단순한 GPT보다 더 유용한 이유입니다. 김개발 씨는 LLM의 원리를 이해하고 나니 의문이 생겼습니다.
"GPT랑 BERT가 다르다고 들었는데, 뭐가 다른 건가요?" 박시니어 씨가 대답했습니다. "좋은 질문이야.
같은 트랜스포머지만 목적이 달라서 구조도 다르거든."
실전 팁
💡 - LLM을 직접 학습시키려면 수백 대의 GPU와 수억 원의 비용이 필요합니다
- 대신 공개된 모델을 미세 조정하면 적은 비용으로 특화된 모델을 만들 수 있습니다
7. GPT와 BERT 비교
김개발 씨는 자연어 처리 프로젝트를 시작하려고 합니다. 그런데 고민이 생겼습니다.
"GPT를 쓸까, BERT를 쓸까?" 박시니어 씨가 조언했습니다. "목적에 따라 달라.
둘의 차이를 알면 선택이 쉬워질 거야."
GPT와 BERT는 모두 트랜스포머 기반이지만, 방향이 다릅니다. GPT는 디코더만 사용하여 텍스트를 생성하고, BERT는 인코더만 사용하여 텍스트를 이해합니다.
마치 작가와 편집자의 차이처럼, GPT는 글을 쓰고 BERT는 글을 분석합니다. 프로젝트의 목적에 따라 적절한 모델을 선택해야 합니다.
다음 코드를 살펴봅시다.
from transformers import GPT2LMHeadModel, BertModel
# GPT: 텍스트 생성 (Decoder-only)
# 다음 단어 예측 → 왼쪽에서 오른쪽으로 생성
gpt_model = GPT2LMHeadModel.from_pretrained('gpt2')
# 예: "오늘 날씨가" → "좋습니다" 생성
# BERT: 텍스트 이해 (Encoder-only)
# 양방향 문맥 이해 → 분류, 추출에 적합
bert_model = BertModel.from_pretrained('bert-base-uncased')
# 예: "이 리뷰는 긍정인가 부정인가?" 분류
# 선택 기준
# 생성 작업 (챗봇, 글쓰기, 코드 생성) → GPT
# 이해 작업 (분류, 개체명 인식, 질의응답) → BERT
김개발 씨의 프로젝트는 고객 리뷰를 분석하는 것입니다. 리뷰가 긍정인지 부정인지 분류하고, 주요 키워드를 추출해야 합니다.
또한 자주 묻는 질문에 자동으로 답변하는 기능도 필요합니다. 박시니어 씨가 화이트보드에 두 모델의 구조를 그렸습니다.
"GPT와 BERT, 둘 다 트랜스포머야. 하지만 사용하는 부분이 달라." GPT는 트랜스포머의 디코더만 사용합니다.
앞에서 뒤로, 왼쪽에서 오른쪽으로 텍스트를 생성합니다. "오늘 날씨가"라는 입력이 주어지면 다음 단어 "좋습니다"를 예측합니다.
생성된 단어를 다시 입력에 추가하고 또 다음 단어를 예측합니다. 이렇게 한 단어씩 글을 써나갑니다.
BERT는 트랜스포머의 인코더만 사용합니다. 입력 텍스트 전체를 양방향으로 이해합니다.
"나는 [MASK]을 먹었다"에서 빈칸에 들어갈 단어를 예측하는 방식으로 학습합니다. 빈칸 앞뒤의 문맥을 모두 보니까 더 정확하게 이해할 수 있습니다.
쉽게 비유하면, GPT는 작가이고 BERT는 편집자입니다. 작가는 빈 종이에 한 문장씩 글을 써나갑니다.
이미 쓴 부분만 보고 다음 내용을 정합니다. 편집자는 완성된 글 전체를 읽고 분석합니다.
어느 부분이 좋은지, 어떤 주제인지 파악합니다. 둘 다 중요하지만 역할이 다릅니다.
그래서 용도도 다릅니다. GPT는 텍스트 생성에 적합합니다.
챗봇, 글쓰기 도우미, 코드 생성, 번역 등이 여기에 해당합니다. ChatGPT, GitHub Copilot이 GPT 계열입니다.
BERT는 텍스트 이해에 적합합니다. 감성 분석, 문서 분류, 개체명 인식, 질의응답의 정답 추출 등이 여기에 해당합니다.
구글 검색의 이해 엔진이 BERT를 사용합니다. 학습 방식도 다릅니다.
GPT는 인과적 언어 모델링(Causal Language Modeling)으로 학습합니다. 왼쪽 문맥만 보고 다음 단어를 예측합니다.
BERT는 마스크 언어 모델링(Masked Language Modeling)으로 학습합니다. 문장의 일부를 가리고 그 부분을 맞춥니다.
김개발 씨의 프로젝트로 돌아가 봅시다. 리뷰 분류와 키워드 추출은 이해 작업입니다.
여기에는 BERT가 적합합니다. 자동 답변 생성은 생성 작업입니다.
여기에는 GPT가 적합합니다. 두 모델을 조합해서 사용하면 됩니다.
최근에는 두 접근법을 결합한 모델도 있습니다. T5, BART 같은 모델은 인코더와 디코더를 모두 사용합니다.
입력을 이해한 뒤 새로운 출력을 생성합니다. 요약, 번역 같은 시퀀스-투-시퀀스 작업에 적합합니다.
김개발 씨는 이제 어떤 모델을 선택해야 할지 명확해졌습니다. "이해에는 BERT, 생성에는 GPT군요." 박시니어 씨가 웃으며 마무리했습니다.
"맞아. 이제 트랜스포머부터 LLM까지 전체 그림이 그려졌지?
이론을 알았으니 직접 써봐."
실전 팁
💡 - HuggingFace Transformers 라이브러리를 사용하면 GPT와 BERT를 쉽게 사용할 수 있습니다
- 한국어 작업에는 KoGPT, KoBERT 같은 한국어 특화 모델을 고려해보세요
이상으로 학습을 마칩니다. 위 내용을 직접 코드로 작성해보면서 익혀보세요!
댓글 (0)
함께 보면 좋은 카드 뉴스
Helm 마이크로서비스 패키징 완벽 가이드
Kubernetes 환경에서 마이크로서비스를 효율적으로 패키징하고 배포하는 Helm의 핵심 기능을 실무 중심으로 학습합니다. Chart 생성부터 릴리스 관리까지 체계적으로 다룹니다.
보안 아키텍처 구성 완벽 가이드
프로젝트의 보안을 처음부터 설계하는 방법을 배웁니다. AWS 환경에서 VPC부터 WAF, 암호화, 접근 제어까지 실무에서 바로 적용할 수 있는 보안 아키텍처를 단계별로 구성해봅니다.
AWS Organizations 완벽 가이드
여러 AWS 계정을 체계적으로 관리하고 통합 결제와 보안 정책을 적용하는 방법을 실무 스토리로 쉽게 배워봅니다. 초보 개발자도 바로 이해할 수 있는 친절한 설명과 실전 예제를 제공합니다.
AWS KMS 암호화 완벽 가이드
AWS KMS(Key Management Service)를 활용한 클라우드 데이터 암호화 방법을 초급 개발자를 위해 쉽게 설명합니다. CMK 생성부터 S3, EBS 암호화, 봉투 암호화까지 실무에 필요한 모든 내용을 담았습니다.
AWS Secrets Manager 완벽 가이드
AWS에서 데이터베이스 비밀번호, API 키 등 민감한 정보를 안전하게 관리하는 Secrets Manager의 핵심 개념과 실무 활용법을 배워봅니다. 초급 개발자도 쉽게 따라할 수 있도록 실전 예제와 함께 설명합니다.