Level 7: Transformer & LLM 원리
🤖

Level 7

BERT 구조

Encoder-only 아키텍처, Masked Language Model, 양방향 문맥

45분
BERT 구조 강의 영상
강의 영상 보기 (새 탭에서 재생)YouTube

📓Google Colab에서 실습하기

이 레슨은 PyTorch/GPU가 필요합니다. 노트북을 다운로드 후 Google Colab에서 열어주세요.

학습 내용

BERT 구조: 양방향으로 읽는 AI

학습 목표

이 레슨을 완료하면:

  • BERT가 왜 Encoder만 사용하는지 이해한다
  • Masked Language Model(MLM)의 학습 원리를 설명할 수 있다
  • 양방향 문맥(Bidirectional Context)의 장점을 안다
  • Fine-tuning으로 다양한 태스크에 적용하는 방법을 이해한다
  • GPT와의 차이점을 명확히 구분할 수 있다

핵심 메시지

"BERT는 '빈칸 맞추기'를 통해 문장을 양쪽에서 동시에 이해하는 법을 배운다." GPT가 "다음 단어 맞추기"로 글을 쓰는 능력을 얻었다면, BERT는 "빈칸 맞추기"로 글을 깊이 이해하는 능력을 얻었습니다.


BERT란 무엇인가?

비유: 독해 시험의 달인 GPT가 "작문 시험"에 특화되었다면, BERT는 "독해 시험"에 특화되어 있습니다. 주어진 글을 읽고 의미를 파악하고, 질문에 답하고, 분류하는 것이 BERT의 강점입니다.

BERT = Bidirectional Encoder Representations from Transformers

  • Bidirectional: "양방향"으로 문맥을 참조한다
  • Encoder: Transformer의 Encoder만 사용한다
  • Representations: 단어의 "표현(벡터)"을 만들어낸다
  • Transformers: Transformer 아키텍처 기반이다

Google이 2018년에 발표했으며, 당시 11개 NLP 벤치마크에서 최고 성능을 달성했습니다.


왜 Encoder만 사용하는가?

BERT의 설계 철학

GPT의 목표: 텍스트를 "생성"한다 → 다음 단어를 예측해야 하니 미래를 못 봄

BERT의 목표: 텍스트를 "이해"한다 → 미래를 볼 수 있다! 양방향 참조 가능!

BERT는 텍스트를 생성할 필요가 없습니다. 주어진 텍스트를 깊이 이해하는 것이 목표이므로, 모든 단어가 앞뒤 모든 단어를 참조할 수 있는 Encoder가 적합합니다.

양방향 vs 단방향

문장: "나는 [???]에서 돈을 찾았다"

단방향 (GPT): "나는 ___" 만 보고 예측

  • → "은행"? "학교"? "집"? -- 뒤의 "돈을"을 볼 수 없어서 애매함

양방향 (BERT): "나는 ___ 에서 돈을 찾았다" 전체를 봄

  • → "은행"! -- "돈을 찾았다"를 보고 금융 은행임을 확신

양방향이 "이해" 태스크에서 훨씬 유리합니다!


BERT의 아키텍처

모델 크기

모델레이어 수Hidden 차원Attention 헤드파라미터 수
BERT-Base1276812110M
BERT-Large24102416340M

BERT의 입력 구성

BERT는 입력을 세 가지 임베딩의 합으로 구성합니다:

입력 문장: "나는 AI를 좋아한다" + "AI는 재미있다"

[CLS] 나는 AI를 좋아한다 [SEP] AI는 재미있다 [SEP]

Token Embedding:    E_CLS  E_나는  E_AI를  E_좋아한다  E_SEP  E_AI는  E_재미있다  E_SEP
                      +      +       +        +         +       +        +         +
Segment Embedding:  E_A    E_A     E_A      E_A        E_A     E_B      E_B       E_B
                      +      +       +        +         +       +        +         +
Position Embedding: E_0    E_1     E_2      E_3        E_4     E_5      E_6       E_7
                      =      =       =        =         =       =        =         =
                    최종 입력 벡터 (각 토큰별로 3개 임베딩의 합)
토큰설명
[CLS]문장 전체를 대표하는 특수 토큰 (분류에 사용)
[SEP]두 문장의 경계를 구분하는 특수 토큰
Segment어떤 문장에 속하는지 (A문장/B문장) 구분

Masked Language Model (MLM): BERT의 핵심 학습법

비유: 영어 빈칸 채우기 시험 "The cat sat on the ___" 에서 빈칸에 들어갈 단어를 맞추는 것과 같습니다. 하지만 BERT는 양쪽 문맥을 모두 보면서 맞추기 때문에 훨씬 정확합니다.

MLM의 작동 원리

원본 문장: "나는 학교에서 수학을 공부했다"

1단계: 입력의 15%를 무작위로 선택

  • 선택된 토큰: "학교에서", "공부했다"

2단계: 선택된 토큰에 대해 세 가지 처리

  • 80%: [MASK]로 대체 → "나는 [MASK] 수학을 [MASK]"
  • 10%: 랜덤 단어로 대체 → "나는 바나나 수학을 [MASK]"
  • 10%: 그대로 유지 → "나는 학교에서 수학을 [MASK]"

3단계: 모델이 원래 토큰을 예측

  • [MASK] 위치에서 → "학교에서", "공부했다"를 예측하도록 학습

왜 80/10/10으로 나눌까?

문제: Fine-tuning 할 때는 [MASK] 토큰이 없습니다!

  • 학습 때만 [MASK]를 보고, 실제 사용 때는 못 봄
  • 이러면 학습과 실사용 사이에 차이(gap)가 생김

해결:

  • 80% [MASK]: 빈칸 맞추기 능력을 학습
  • 10% 랜덤: "이 단어가 맞나?" 판별 능력도 학습
  • 10% 유지: 정상 단어에 대한 표현도 학습

→ 모델이 [MASK]에만 의존하지 않고, 모든 위치의 모든 토큰에 주의를 기울이게 됩니다!

실행해보기: MLM 시뮬레이션

python
import numpy as np np.random.seed(42) sentence = ["나는", "학교에서", "수학을", "열심히", "공부했다"] vocab = ["나는", "학교에서", "수학을", "열심히", "공부했다", "바나나", "노래를", "집에서", "잠을", "먹었다"] print("=== MLM (Masked Language Model) 시뮬레이션 ===") print(f"원본 문장: {' '.join(sentence)}") # 15% 마스킹 (5개 중 1개 선택) mask_idx = 1 # "학교에서"를 선택 masked_sentence = sentence.copy() # 80/10/10 비율로 처리 roll = np.random.random() if roll < 0.8: masked_sentence[mask_idx] = "[MASK]" action = "[MASK]로 대체" elif roll < 0.9: random_word = np.random.choice(vocab) masked_sentence[mask_idx] = random_word action = f"랜덤 단어 '{random_word}'로 대체" else: action = "그대로 유지" print(f"마스킹 결과: {' '.join(masked_sentence)} ({action})") print(f"정답: '{sentence[mask_idx]}'") # 모델의 예측 시뮬레이션 print() print("모델의 예측 확률:") probs = np.random.dirichlet(np.ones(len(vocab)) * 0.5) # 정답에 높은 확률 부여 (학습된 모델이라 가정) probs[1] = 0.65 # "학교에서" probs = probs / probs.sum() for word, prob in sorted(zip(vocab, probs), key=lambda x: -x[1])[:5]: marker = " <-- 정답!" if word == sentence[mask_idx] else "" print(f" P('{word}') = {prob:.3f}{marker}") print() print("양방향 문맥 덕분에:") print(f" 왼쪽: '{sentence[0]}' + 오른쪽: '{' '.join(sentence[2:])}'") print(f" => '{sentence[mask_idx]}'를 정확히 예측!")

NSP (Next Sentence Prediction)

BERT는 MLM 외에 NSP도 함께 학습합니다 (두 문장이 연속인지 판별).

유형비율문장 A문장 B라벨
IsNext50%"나는 공원에 갔다""거기서 산책을 했다"✅ 연속된 문장
NotNext50%"나는 공원에 갔다""주가가 크게 올랐다"❌ 무관한 문장

처리 흐름: [CLS] 토큰의 출력 벡터 → 이진 분류기 → IsNext / NotNext

목적: 문장 간의 관계를 이해하는 능력 학습 (QA, 자연어 추론 등에 도움)

참고: 이후 연구(RoBERTa)에서 NSP가 실제로는 큰 도움이 안 된다는 것이 밝혀졌습니다. 그래서 RoBERTa는 NSP를 제거하고 MLM만으로 학습합니다.


Fine-tuning: BERT를 실전에 투입하기

비유: 의대를 졸업한 의사 Pre-training은 의대에서 기초 의학을 배우는 것이고, Fine-tuning은 전공을 정해서 전문의가 되는 것입니다. 기초가 탄탄하면 어떤 전공이든 빠르게 적응할 수 있습니다.

Pre-training -> Fine-tuning 과정

Pre-training (사전 학습):

  • 데이터: Wikipedia + BooksCorpus (33억 단어)
  • 태스크: MLM + NSP
  • 비용: 4일 x TPU 64개 (수천만 원)
  • 결과: 범용적 언어 이해 능력

Fine-tuning (미세 조정):

  • 데이터: 태스크별 레이블 데이터 (수천~수만 개)
  • 태스크: 분류, QA, NER 등
  • 비용: 수 시간 x GPU 1개
  • 결과: 특정 태스크 전문가

핵심: Pre-trained 모델을 공유하면 누구나 적은 데이터와 비용으로 Fine-tuning 가능!

Fine-tuning 태스크별 적용 방법

태스크입력 형태사용하는 출력예시
문장 분류[CLS] 문장 [SEP][CLS] 벡터 -> 분류기감성 분석, 스팸 분류
문장 쌍 분류[CLS] 문장A [SEP] 문장B [SEP][CLS] 벡터 -> 분류기자연어 추론(NLI)
토큰 분류[CLS] 문장 [SEP]각 토큰 벡터 -> 분류기개체명 인식(NER)
질문 답변[CLS] 질문 [SEP] 지문 [SEP]지문 토큰에서 시작/끝 위치SQuAD
문장 분류 Fine-tuning 예시:

[CLS] 이 영화 정말 재미있어요 [SEP]
  |
  v
BERT Encoder (12 레이어)
  |
  v
[CLS] 토큰의 최종 벡터 (768차원)
  |
  v
Linear Layer (768 -> 2)  <- 이 부분만 새로 추가!
  |
  v
Softmax -> [긍정: 0.92, 부정: 0.08]

Fine-tuning 시:
  - BERT 전체 파라미터를 약간만 조정
  - Linear Layer는 처음부터 학습
  - 수천 개의 레이블 데이터로 충분

실행해보기: Fine-tuning 개념 시뮬레이션

python
import numpy as np np.random.seed(42) # BERT가 출력한 [CLS] 벡터 (실제로는 768차원) d_model = 8 num_classes = 3 # 긍정, 중립, 부정 # Pre-trained BERT의 [CLS] 출력 시뮬레이션 sentences = [ ("이 영화 최고에요!", "긍정"), ("그냥 그래요", "중립"), ("정말 별로였어요", "부정"), ("또 보고 싶어요!", "긍정"), ("시간 낭비예요", "부정"), ] # Fine-tuning용 Linear Layer 가중치 (학습됨) W_cls = np.random.randn(d_model, num_classes) * 0.3 b_cls = np.zeros(num_classes) print("=== BERT Fine-tuning 감성 분석 시뮬레이션 ===") labels = ["긍정", "중립", "부정"] correct = 0 for sent, true_label in sentences: # BERT의 [CLS] 벡터 (실제로는 BERT가 계산) cls_vector = np.random.randn(d_model) * 0.5 # 정답에 맞는 패턴을 살짝 심어줌 (학습된 효과) true_idx = labels.index(true_label) cls_vector[true_idx] += 1.0 # Linear Layer로 분류 logits = cls_vector @ W_cls + b_cls logits[true_idx] += 2.0 # 학습된 모델 시뮬레이션 probs = np.exp(logits) / np.exp(logits).sum() pred_idx = np.argmax(probs) pred_label = labels[pred_idx] is_correct = pred_label == true_label correct += int(is_correct) mark = "O" if is_correct else "X" print(f" [{mark}] '{sent}'") print(f" 예측: {pred_label} ({probs[pred_idx]:.2f}), 정답: {true_label}") print() print(f"정확도: {correct}/{len(sentences)} ({correct/len(sentences)*100:.0f}%)") print() print("BERT의 강점: Pre-trained 지식 덕분에 적은 데이터로도 높은 정확도!")

BERT 변형 모델들

모델특징개선 포인트
RoBERTaNSP 제거, 더 많은 데이터/시간 학습학습 방법 최적화
ALBERT파라미터 공유, 임베딩 분해모델 경량화
DistilBERT지식 증류로 60% 크기에 97% 성능속도/크기 최적화
ELECTRA대체 토큰 탐지 (생성자+판별자)학습 효율 향상
KoBERTSKT에서 개발한 한국어 BERT한국어 특화

핵심 요약

개념설명비유
Encoder-onlyTransformer Encoder만 사용독해 전문가
양방향 참조모든 토큰이 앞뒤를 모두 참조문장 전체를 한눈에 보기
MLM15% 토큰을 가리고 맞추기 학습빈칸 채우기 시험
NSP두 문장이 연속인지 판별다음 문장 맞추기
Fine-tuning소량 데이터로 특정 태스크 적응의대 졸업 후 전공 선택
[CLS] 토큰문장 전체를 대표하는 벡터문장의 요약본

학습 체크리스트

  • BERT가 Encoder만 쓰는 이유를 설명할 수 있다
  • MLM의 80/10/10 비율이 왜 필요한지 이해한다
  • 양방향 참조가 단방향보다 이해 태스크에 유리한 이유를 안다
  • Fine-tuning으로 분류, QA, NER에 적용하는 방법을 이해한다
  • [CLS] 토큰과 [SEP] 토큰의 역할을 안다
  • BERT와 GPT의 차이를 표로 정리할 수 있다

레슨 정보

레벨
Level 7: Transformer & LLM 원리
예상 소요 시간
45분
참고 영상
YouTube 링크

💡실습 환경 안내

이 레벨은 PyTorch/GPU가 필요하여 Google Colab 사용을 권장합니다.

Colab은 무료 GPU를 제공하여 PyTorch, CNN, Transformer 등을 실행할 수 있습니다.