Language Model이의 핵심 임무는 단 하나입니다.
“지금까지 나온 단어들을 보고, 다음에 올 가장 자연스러운 단어를 예측한다”
입력: "오늘 점심으로 삼겹살을"
예측: "먹었다" (80%) / "구웠다" (12%) / "시켰다" (5%) / ...
스마트폰 키보드의 자동완성, ChatGPT의 문장 생성, 번역기가 모두 이 원리로 작동합니다.
Language Model은 단어를 한 번에 하나씩 생성합니다.
이전 step의 출력이 다음 step의 입력으로 들어가는 구조입니다.
Step 0: <START> → "내일" 선택
Step 1: <START> 내일 → "회의가" 선택
Step 2: <START> 내일 회의가 → "있으니" 선택
Step 3: <START> 내일 회의가 있으니 → "준비해줘" 선택
Step 4: <START> 내일 회의가 있으니 준비해줘 → <END> → 생성 종료
최종 출력: "내일 회의가 있으니 준비해줘"
두가지 단어 선택 방식
| 방식 | 설명 | 특징 |
|---|---|---|
| Greedy Selection | 매 step마다 확률이 가장 높은 단어 선택(argmax) | 빠르고 결정적, 항상 같은 결과 |
| Sampling | 확률 분포에 따라 랜덤하게 단어 선택 | 다양한 결과, 창의적 문장 생성 가능 |
# Greedy Selection
next_word = np.argmax(output_probs) # 가장 높은 확률 단어
# Sampling
next_word = np.random.choice(len(output_probs), p=output_probs) # 확률 기반 샘플링
| 분야 | 입력 | 출력 |
|---|---|---|
| 기계번역 | 한국어 문장 | 영어 문장 |
| 질의응답 (Q&A) | 지문 + 질문 | 답변 |
| 챗봇 | 사용자 메시지 | 응답 메시지 |
| 음성인식 | 음성 파형 | 텍스트 |
| 텍스트 요약 | 긴 기사 | 핵심 요약문 |
| 이미지 캡셔닝 | 이미지 | 설명 문장 |
Seq2Seq(Sequence-to-Sequence)는 입력 시퀀스를 읽어 출력 시퀀스를 생성하는 모델입니다.
기계변역이 대표적인 사례입니다.
입력 시퀀스: "제 고양이는 생선을 좋아합니다"
↓
[Seq2Seq 모델]
↓
출력 시퀀스: "My cat likes fish"
전통적 기계번역 - 구문 단위 분리 방식
"오늘 날씨가 정말 맑다"
↓
[오늘] [날씨가] [정말] [맑다] ← 구문 단위로 쪼갬
↓
[Today] [weather] [really] [clear] ← 각 구문별 번역
↓
"Today weather really clear" ← 어색한 결과 😢
문제: 각 구문을 독립적으로 번역하므로 전체 문맥을 이해 못 함
NMT - 문장 전체를 이해하고 번역
"오늘 날씨가 정말 맑다"
↓
[Encoder가 문장 전체를 읽고 의미를 하나의 벡터로 압축]
↓
[Decoder가 그 의미를 바탕으로 번역문 생성]
↓
"The weather is really clear today" ← 자연스러운 결과 😊
장점: 문장 전체의 맥락을 이해하고 번역 → 인간의 번역 방식과 유사
Encoder-Decoder 구조는 어떤 언어든, 어떤 도메인이든 동일하게 적용할 수 있습니다.
기계번역:
입력: "桜が咲いている" (일본어)
출력: "벚꽃이 피어있다" (한국어)
Q&A 시스템:
입력: [긴 뉴스 기사] + "이 기사에서 가장 중요한 사건은?"
출력: "2024년 반도체 공급망 재편"
코드 생성:
입력: "피보나치 수열을 출력하는 파이썬 함수 작성해줘"
출력: def fibonacci(n): ...
이메일 자동 답장:
입력: "안녕하세요, 내일 미팅 시간 변경 가능한지요?"
출력: "안녕하세요, 내일 오후 3시로 변경 가능합니다."
첫 번째 단어에서 잘못된 예측을 했을 경우 시간이 지날수록 더 큰 잘못된 예측을 할 가능성이 증가합니다. Teacher Forcing 이란 오답이 나오면 손실은 계산해서 학습에 사용하되(학습 과정에서는 이미 정답을 알고 있으므로), 다음 단어 예측의 입력 값으로는 실제 값을 사용하는 기법입니다.
목표 번역: "I love you so much"
[Teacher Forcing 없이 학습]
Step 1: <START> → 예측 "I" (정답 ✅)
Step 2: "I" → 예측 "hate" (오답 ❌, 정답은 "love")
Step 3: "hate" → 예측 "this" (오답 ❌, 오류가 눈덩이처럼 커짐)
Step 4: "this" → 예측 "world" (오답 ❌)
Step 5: "world" → 예측 <END>
예측 결과: "I hate this world" 😱
첫 번째 단어에서 실수하면 이후 모든 단어가 연쇄적으로 틀리게 됩니다.
이를 Exposure Bias(노출 편향) 문제라고 합니다.
목표 번역: "I love you so much"
[Teacher Forcing 적용]
Step 1: <START> → 예측 "I" (입력에는 항상 정답 사용)
Step 2: "I"(정답) → 예측 "hate" (오답이더라도)
↑ 오답을 다음 입력으로 넣지 않고, 정답 "I"를 넣음
Step 3: "love"(정답) → 예측 "you" (정답 ✅)
Step 4: "you"(정답) → 예측 "so" (정답 ✅)
Step 5: "so"(정답) → 예측 "much" (정답 ✅)
💡 비유: 피아노 레슨에서 학생이 틀린 음을 쳤을 때,
틀린 음을 계속 이어가게 두지 않고 선생님이 "여기서 다시" 하며 올바른 음부터 다시 시작시키는 것과 같습니다.
Encoder-Decoder 모델의 핵심은 Thought Vector입니다.
입력: 제 / 강아지는 / 뼈다귀를 / 좋아해요
↓
Encoder LSTM
↓
마지막 hidden state (= **Thought Vector**)
디코더 시작:
<start> → My → dog → likes → bones → <end>
질의 응답 문제(story + question) → answer) 에 적용
ex) story + question :
뉴턴에 대한 설명이 있는 페이지 + “뉴턴의 업적 중에 대표적인 것은?”
answer :
만유인력의 법칙(페이지에서 답을 찾음)
전체 파이프라인:
입력 문장 (한국어)
↓
[Embedding Layer] 단어 → 벡터 변환
↓
[Encoder RNN/LSTM] 문장 전체를 순차적으로 읽음
↓
[Final Hidden State] = Thought Vector (문장 의미 압축)
↓
[Decoder RNN/LSTM] Thought Vector의 초기값으로 출력 생성
↓
[Dense + Softmax] 각 단어일 확률 계산
↓
출력 문장 (영어)
Encoder-Decoder 모델은 학습할 때와 실제 사용할 때 구조가 다릅니다.
학습 단계(Training Phase) - 모델이 배우는 단계
한국어 입력: "강아지가 공원에서 뛰어놀고 있어"
Encoder → Thought Vector 생성
Decoder 입력: <START> My dog is playing
Decoder 목표: My dog is playing <END>
* Teacher Forcing 기법 사용
* Encoder + Decoder가 하나의 모델로 통합되어 학습 why? backpropagtion 시 Encoder, Decoder에게 모두 전파되어야함.(Encoder, Decoder 모두 학습이 필요하기 때문!)
추론 단계(Inference Phase) - 모델이 실제로 사용하는 단계
Encoder로 입력 문장 → Thought Vector 추출
↓
Decoder에 Thought Vector 전달 (초기 상태)
↓
<START> 토큰을 시작으로 단어를 하나씩 생성
↓
<END> 토큰이 나올 때까지 반복
* Text Generation 기법 사용
* Encoder와 Decoder를 분리하여 사용
* 이전 step의 출력 단어가 다음 step의 입력으로 들어감
학습 vs 실제 사용 차이
| 구분 | 디코더 입력 |
|---|---|
| 학습 시 | 정답 단어 (Teacher Forcing) |
| 추론 시 | 이전 step의 예측 단어(Text Generation) |
Python 코드 구조 (Keras)
# ─────────── 공통: Encoder ───────────
encoder_inputs = Input(shape=(None,)) # 길이가 미정
encoder_embed = Embedding(src_vocab, embed_dim)(encoder_inputs)
_, state_h, state_c = LSTM(units, return_state=True)(encoder_embed)
encoder_states = [state_h, state_c] # = Thought Vector
# ─────────── 학습용: Decoder ───────────
decoder_inputs = Input(shape=(None,))
decoder_embed = Embedding(tgt_vocab, embed_dim)(decoder_inputs)
decoder_lstm = LSTM(units, return_sequences=True, return_state=True) # 매 시점마다 다음 단어를 예측해야함.
decoder_outputs, _, _ = decoder_lstm(decoder_embed,
initial_state=encoder_states)
decoder_dense = Dense(tgt_vocab, activation='softmax')
decoder_outputs = decoder_dense(decoder_outputs)
train_model = Model([encoder_inputs, decoder_inputs], decoder_outputs) # 인코더의 Thought Vector을 입력으로
# ─────────── 추론용: Encoder 모델 분리 ───────────
encoder_model = Model(encoder_inputs, encoder_states)
# ─────────── 추론용: Decoder 모델 분리 ───────────
dec_state_h = Input(shape=(units,))
dec_state_c = Input(shape=(units,))
dec_states_in = [dec_state_h, dec_state_c]
dec_out, h, c = decoder_lstm(decoder_embed,
initial_state=dec_states_in)
dec_out = decoder_dense(dec_out)
decoder_model = Model([decoder_inputs] + dec_states_in,
[dec_out, h, c])
BLEU(Bilingual Evaluation Understudy)는 기계번역 품질을 자동으로 측정하는 알고리즘입니다. 언어에 무관하며 이해하기 쉽고, 계산하기 쉽습니다.
뉴스 기사 번역 평가
정답 번역 (Reference):
"반도체 시장이 올해 큰 폭으로 성장했다"
모델 예측 (Prediction):
"반도체 시장이 올해 크게 성장했다"
Bigram(2-gram) 기준 BLEU 계산
정답의 bigram 목록:
(반도체, 시장이) (시장이, 올해) (올해, 큰) (큰, 폭으로) (폭으로, 성장했다)
예측의 bigram 목록:
(반도체, 시장이) (시장이, 올해) (올해, 크게) (크게, 성장했다)
일치하는 bigram:
✅ (반도체, 시장이)
✅ (시장이, 올해)
❌ (올해, 크게) ← 정답은 "올해 큰"
❌ (크게, 성장했다) ← 정답은 "폭으로 성장했다"
BLEU (bigram) = 일치 수 / 예측 bigram 수 = 2 / 4 = 0.5
BLEU 점수 해석 가이드
| BLEU 점수 | 품질 수준 |
|---|---|
| < 0.1 | 거의 이해 불가 수준 |
| 0.1 ~ 0.3 | 의미는 전달되나 어색함 |
| 0.3 ~ 0.5 | 이해 가능한 수준의 번역 |
| 0.5 ~ 0.7 | 준수한 번역 품질 |
| > 0.7 | 전문 번역가 수준에 근접 |
⚠️ BLEU의 한계
"나는 밥을 먹었다"의 완벽한 번역은 여러 가지일 수 있습니다.
"I ate rice" / "I had rice" / "I ate a meal" — 모두 정답이지만
BLEU는 정해진 Reference와만 비교하므로 창의적인 좋은 번역도 낮은 점수를 받을 수 있습니다.
Decoder가 매 step마다 단어를 선택하는 전략은 3가지가 있으며, 각각 속도, 다양성, 품질 면에서 트레이드오프가 있습니다.
매 step마다 확률이 가장 높은 단어 하나만 선택합니다.
Step 1: softmax 출력 → {"오늘": 0.6, "내일": 0.3, "어제": 0.1}
선택: "오늘" (0.6, 가장 높음)
Step 2: softmax 출력 → {"날씨가": 0.7, "회의가": 0.2, "밥이": 0.1}
선택: "날씨가" (0.7)
결과: "오늘 날씨가 ..." ← 항상 동일한 결과
next_token = np.argmax(output_probs) # 가장 높은 확률 선택
| 장점 | 단점 |
|---|---|
| 빠르고 단순 | 항상 같은 결과 (다양성 없음) |
| 구현 쉬움 | 첫 단어 실수 시 전체 문장에 영향 |
확률 분포에 따라 랜덤하게 단어를 선택합니다.
softmax 출력 → {"맛있는": 0.5, "신선한": 0.3, "저렴한": 0.2}
실행 1: "맛있는" 선택 (50% 확률)
실행 2: "신선한" 선택 (30% 확률)
실행 3: "저렴한" 선택 (20% 확률)
→ 실행할 때마다 다른 문장 생성!
next_token = np.random.choice(len(probs), p=probs) # 확률 분포로 샘플링
| 장점 | 단점 |
|---|---|
| 다양하고 창의적인 결과 | 낮은 확률의 어색한 단어가 선택될 수도 있음 |
| 챗봇, 창작 문장에 적합 | 품질이 일정하지 않음 |
매 step마다 상위 B개(Beam Width)의 후보 시퀀스를 유지하며 탐색합니다.
Greedy의 "최선만 선택"과 Sampling의 "랜덤"의 절충안입니다.
Beam Width = 3 (매 step에서 3개 후보 유지)
Step 1: 전체 단어 중 확률 상위 3개 선택
후보 A: "오늘" (확률 0.60)
후보 B: "내일" (확률 0.25)
후보 C: "어제" (확률 0.10)
Step 2: 각 후보에서 다시 상위 확률 단어 확장
A → "오늘 날씨가" (0.60 × 0.70 = 0.42) ← 누적 확률
A → "오늘 회의가" (0.60 × 0.20 = 0.12)
B → "내일 오전에" (0.25 × 0.65 = 0.16)
B → "내일 오후에" (0.25 × 0.30 = 0.075)
C → "어제 저녁에" (0.10 × 0.80 = 0.08)
...
→ 이 중 누적 확률 상위 3개만 유지
Step 3: 계속 확장하다가 <END>에 도달하면 종료
최종: 전체 누적 확률이 가장 높은 시퀀스 선택
비유로 이해하기
Greedy: 지도 없이 매 갈림길에서 "가장 넓은 길"만 선택
→ 빠르지만 막다른 골목에 걸릴 수 있음
Beam Search: 지도를 보며 가장 유망한 3개 경로를 동시에 탐색
→ 조금 느리지만 전체적으로 더 좋은 경로 발견 가능
| 항목 | Greedy | Sampling | Beam Search |
|---|---|---|---|
| 속도 | ⚡ 가장 빠름 | ⚡ 빠름 | 🐢 보통 |
| 다양성 | ❌ 없음 | ✅ 높음 | 🔶 제한적 |
| 품질 | 보통 | 들쑥날쑥 | ✅ 가장 안정적 |
| 주요 사용처 | 빠른 프로토타입 | 창작, 챗봇 | 번역, 요약 |
사람과의 (문자) 대화를 통해 질문에 알맞은 답이나 연관 정보를 제공하는
AI 기반의 대화형 소프트웨어
챗봇의 종류
| 종류 | 입력 방식 | 예시 |
|---|---|---|
| 메시지 챗봇 | 텍스트 | 카카오톡 챗봇, 고객센터 봇 |
| 음성인식 봇 | 음성 | Siri, 빅스비 |
| 개인비서 봇 | 텍스트 + 음성 + 위치 | Google Assistant |
EASY ──────────────────────────────────────── HARD
조회 기반 모델 생성 모델
(Retrieval-Based) (Generative)
미리 만들어 둔 Q&A DB에서 새로운 답변을 직접 생성
가장 유사한 답변을 찾아 반환 (Encoder-Decoder 구조)
전통적 알고리즘 기반 딥러닝 기반
단문 대화 장문 대화
Closed Domain Open Domain
(특정 주제만 가능) (어떤 주제든 가능)
입력 문장을 미리 정의된 카테고리로 분류하고, 해당 카테고리의 답변을 반환합니다.
사용자: "서울에서 부산까지 기차 얼마나 걸려요?"
↓
[분류 모델]: "교통/이동시간" 카테고리로 분류
↓
[DB 조회]: "교통/이동시간" 카테고리의 답변 반환
↓
챗봇: "KTX 기준 약 2시간 30분 소요됩니다."
데이터셋 구조
| input (사용자 질문) | category (카테고리 레이블) | target (답변) |
|---|---|---|
| "영업시간이 어떻게 돼요?" | 운영시간 | "평일 오전 9시~오후 6시입니다." |
| "환불 어떻게 하나요?" | 환불정책 | "구매 후 7일 이내 환불 가능합니다." |
| "주차 가능한가요?" | 편의시설 | "지하 2층 주차장 이용 가능합니다." |
장점과 한계
✅ 장점:
- 구현이 단순하고 안정적
- 특정 도메인(쇼핑몰, 은행 등)에서 높은 정확도
- 부적절한 답변 생성 위험 없음
❌ 한계:
- DB에 없는 질문에는 대응 불가
- 새로운 카테고리 추가 시 재학습 필요
- 문맥을 이어가는 대화 불가
Seq2Seq 구조를 이용해 새로운 답변을 직접 생성합니다.
사용자: "요즘 너무 힘들어"
↓
[Encoder]: 문장 의미를 Thought Vector로 압축
↓
[Decoder]: Thought Vector를 바탕으로 답변 생성
↓
챗봇: "많이 지치셨겠네요. 어떤 일이 있으셨나요?"
학습 데이터 구조
| 입력 (Input) | 출력 (Output) |
|---|---|
| "배고파 죽겠어" | "뭐 먹고 싶어?" |
| "오늘 발표 망했어" | "많이 긴장했구나. 그래도 열심히 했잖아." |
| "친구랑 싸웠어" | "무슨 일이 있었어? 얘기해봐." |
실습 데이터셋: 한국어 챗봇 데이터
Chatbot_data_for_Korean v1.0
출처: https://github.com/songys/Chatbot_data
규모: 챗봇 트레이닝용 문답 페어 11,876개
특징: 일상 대화 중심, 감성 대화 포함
# ──────────────── 데이터 준비 ────────────────
import pandas as pd
df = pd.read_csv('ChatbotData.csv')
# 컬럼: Q (질문), A (답변), label (감정 카테고리 0~2)
questions = df['Q'].tolist()
answers = df['A'].tolist()
# Decoder 입력/목표 시퀀스 생성
# 입력: <START> + 답변
# 목표: 답변 + <END>
decoder_inputs = ['<START> ' + a for a in answers]
decoder_targets = [a + ' <END>' for a in answers]
# ──────────────── 토크나이징 ────────────────
from tensorflow.keras.preprocessing.text import Tokenizer
from tensorflow.keras.preprocessing.sequence import pad_sequences
# 질문/답변 각각 별도 토크나이저 사용
enc_tokenizer = Tokenizer(oov_token='<OOV>')
dec_tokenizer = Tokenizer(oov_token='<OOV>')
enc_tokenizer.fit_on_texts(questions)
dec_tokenizer.fit_on_texts(decoder_inputs + decoder_targets)
enc_vocab = len(enc_tokenizer.word_index) + 1
dec_vocab = len(dec_tokenizer.word_index) + 1
# ──────────────── 시퀀스 변환 & 패딩 ────────────────
MAX_LEN_ENC = 30
MAX_LEN_DEC = 30
enc_inputs = pad_sequences(
enc_tokenizer.texts_to_sequences(questions),
maxlen=MAX_LEN_ENC, padding='post'
)
dec_inputs = pad_sequences(
dec_tokenizer.texts_to_sequences(decoder_inputs),
maxlen=MAX_LEN_DEC, padding='post'
)
dec_targets = pad_sequences(
dec_tokenizer.texts_to_sequences(decoder_targets),
maxlen=MAX_LEN_DEC, padding='post'
)
# ──────────────── 모델 구성 ────────────────
from tensorflow.keras.layers import Input, Embedding, LSTM, Dense
from tensorflow.keras.models import Model
EMBED_DIM = 64
UNITS = 128
# Encoder
enc_in = Input(shape=(MAX_LEN_ENC,))
enc_emb = Embedding(enc_vocab, EMBED_DIM)(enc_in)
_, h, c = LSTM(UNITS, return_state=True)(enc_emb)
# Decoder
dec_in = Input(shape=(MAX_LEN_DEC,))
dec_emb = Embedding(dec_vocab, EMBED_DIM)(dec_in)
dec_lstm = LSTM(UNITS, return_sequences=True, return_state=True)
dec_out, _, _ = dec_lstm(dec_emb, initial_state=[h, c])
dec_dense = Dense(dec_vocab, activation='softmax')
dec_out = dec_dense(dec_out)
train_model = Model([enc_in, dec_in], dec_out)
train_model.compile(
optimizer='adam',
loss='sparse_categorical_crossentropy',
metrics=['accuracy']
)
# ──────────────── 챗봇 대화 함수 ────────────────
def chat(input_sentence, max_len=30):
"""사용자 입력에 대한 챗봇 응답을 생성합니다."""
# 입력 인코딩
seq = enc_tokenizer.texts_to_sequences([input_sentence])
enc_input = pad_sequences(seq, maxlen=MAX_LEN_ENC, padding='post')
# Thought Vector 추출
states = encoder_model.predict(enc_input, verbose=0)
# 디코딩 시작
target = dec_tokenizer.texts_to_sequences(['<START>'])
stop = False
result = []
while not stop:
output, h, c = decoder_model.predict(
[np.array(target)] + states, verbose=0
)
# Greedy 선택
token_idx = np.argmax(output[0, -1, :])
word = dec_tokenizer.index_word.get(token_idx, '')
if word == '<END>' or len(result) >= max_len:
stop = True
else:
result.append(word)
target = [[token_idx]]
states = [h, c]
return ' '.join(result)
# 테스트
test_inputs = [
"오늘 정말 힘든 하루였어",
"취업 준비가 너무 막막해",
"좋아하는 사람한테 고백할까 말까"
]
for q in test_inputs:
print(f"사용자: {q}")
print(f"챗봇: {chat(q)}")
print()
Language Model (다음 단어 예측)
│
├─ 단어 생성 전략
│ ├─ Greedy Selection (항상 최고 확률 선택)
│ └─ Sampling (확률 분포로 랜덤 선택)
│
└─ Seq2Seq (Encoder-Decoder)
│
├─ Encoder → 입력 시퀀스를 Thought Vector로 압축
├─ Decoder → Thought Vector로 출력 시퀀스 생성
│
├─ 학습 기법: Teacher Forcing
│ └─ 오류 누적 방지, 정답을 다음 입력으로 사용
│
├─ 평가 지표: BLEU Score
│ └─ n-gram 일치율로 번역 품질 측정
│
├─ 디코딩 전략
│ ├─ Greedy (빠름, 다양성 없음)
│ ├─ Sampling (다양함, 품질 불안정)
│ └─ Beam Search (느림, 품질 안정)
│
└─ 응용
├─ 기계번역 (한국어 → 영어)
├─ Q&A (지문 + 질문 → 답변)
└─ Chatbot (질문 → 응답)
├─ Retrieval-Based (조회 기반)
└─ Generative (Seq2Seq 생성)
| 개념 | 핵심 아이디어 | 언제 사용? |
|---|---|---|
| Greedy | 매 step 최고 확률 단어 선택 | 빠른 추론, 프로토타입 |
| Sampling | 확률 분포 기반 랜덤 선택 | 다양성이 중요한 창작 |
| Beam Search | 상위 B개 후보 동시 탐색 | 번역, 요약 등 품질 중요 |
| Teacher Forcing | 학습 시 정답을 다음 입력으로 사용 | Seq2Seq 학습 안정화 |
| BLEU | n-gram 일치율로 번역 평가 | 번역 모델 성능 측정 |
| Retrieval 챗봇 | DB에서 가장 유사한 답변 검색 | 특정 도메인, 안정성 중요 |
| Generative 챗봇 | Seq2Seq로 새로운 답변 생성 | 오픈 도메인, 자연스러운 대화 |
Q1. Teacher Forcing을 사용하지 않으면 어떤 문제가 생기나요?
정답: 초기 step에서 예측이 틀리면 그 오류가 이후 step에 연쇄적으로 전파되어 전체 출력이 크게 어긋납니다 (Exposure Bias). Teacher Forcing은 학습 시 항상 정답 단어를 다음 step의 입력으로 넣어 이를 방지합니다.
Q2. 학습 단계와 추론 단계에서 Decoder의 입력이 어떻게 다른가요?
학습 시: 정답 단어 (Teacher Forcing)
추론 시: 이전 step에서 모델이 예측한 단어
Q3. Beam Search가 Greedy보다 나은 이유는?
Greedy는 매 step에서 단 하나의 최선만 선택하므로 초기 실수가 전체에 영향을 미칩니다. Beam Search는 B개의 후보를 동시에 유지하며 전체 시퀀스의 누적 확률이 가장 높은 결과를 선택하므로 더 품질이 좋습니다.
Q4. Retrieval 기반 챗봇과 Generative 챗봇의 가장 큰 차이는?
Retrieval: 미리 준비된 DB에서 답변을 검색 → 안정적이나 유연성 낮음
Generative: 새로운 답변을 직접 생성 → 유연하지만 부적절한 답변 위험 존재