자연어처리 (NLP)¶
면접에서 자주 나오는 NLP 질문들
질문 목록¶
텍스트 전처리¶
- [ ] One Hot 인코딩에 대해 설명해주세요
- [ ] POS 태깅은 무엇인가요?
- [ ] 가장 간단하게 POS tagger를 만드는 방법은?
- [ ] Stop Words는 무엇일까요?
- [ ] 이것을 왜 제거해야 하나요?
텍스트 표현¶
- [ ] TF-IDF 점수는 무엇이며 어떤 경우 유용한가요?
- [ ] n-gram은 무엇일까요?
- [ ] Word2Vec의 원리는?
- [ ] 그 그림에서 왼쪽 파라메터들을 임베딩으로 쓰는 이유는?
- [ ] 그 그림에서 오른쪽 파라메터들의 의미는?
- [ ] 남자와 여자가 가까울까? 남자와 자동차가 가까울까?
분류/분석¶
- [ ] 문장에서 "Apple"이란 단어가 과일인지 회사인지 식별하는 모델?
- [ ] 영화 리뷰가 긍정적인지 부정적인지 예측하는 모델?
- [ ] 뉴스 기사를 주제별로 자동 분류하는 시스템?
정보 추출¶
- [ ] 뉴스 기사에 인용된 텍스트의 모든 항목을 어떻게 찾을까요?
- [ ] depedency parsing란 무엇인가요?
언어 모델¶
- [ ] Regular grammar는 무엇인가요?
- [ ] regular expression과 무슨 차이가 있나요?
- [ ] 잠재론적, 의미론적 색인은 무엇이고 어떻게 적용할까요?
- [ ] 한국어에서 많이 사용되는 사전은 무엇인가요?
시퀀스 모델¶
- [ ] RNN에 대해 설명해주세요
- [ ] LSTM은 왜 유용한가요?
번역/생성¶
- [ ] Translate 과정 Flow에 대해 설명해주세요
- [ ] 영어 텍스트를 다른 언어로 번역할 시스템?
- [ ] 번역을 Unsupervised로 할 수 있을까?
- [ ] 음성 인식 시스템에서 생성된 텍스트를 자동으로 수정하는 시스템?
랭킹/검색¶
- [ ] PageRank 알고리즘은 어떻게 작동하나요?
답변¶
One Hot 인코딩에 대해 설명해주세요¶
한 줄 정의¶
범주형 데이터를 해당 위치만 1, 나머지 0인 벡터로 표현하는 방식
동작 방식¶
단어 사전: [사과, 바나나, 오렌지, 포도]
크기: 4
사과: [1, 0, 0, 0]
바나나: [0, 1, 0, 0]
오렌지: [0, 0, 1, 0]
포도: [0, 0, 0, 1]
장점과 단점¶
| 장점 | 단점 |
|---|---|
| 구현 간단 | 고차원 희소 벡터 (어휘 10만 → 10만 차원) |
| 범주 간 동등 취급 | 단어 간 의미적 관계 표현 불가 |
| 머신러닝 입력 가능 | 메모리 비효율 |
의미 관계 문제¶
cos(사과, 바나나) = 0
cos(사과, 오렌지) = 0
→ 모든 단어가 동일하게 "무관계"
→ "사과-오렌지"가 "사과-자동차"보다 가깝다는 정보 없음
해결: Word Embedding (Word2Vec, BERT 등)
코드¶
from sklearn.preprocessing import OneHotEncoder
import numpy as np
# sklearn
encoder = OneHotEncoder(sparse=False)
words = np.array([['사과'], ['바나나'], ['사과']])
encoded = encoder.fit_transform(words)
# PyTorch
import torch
import torch.nn.functional as F
labels = torch.tensor([0, 1, 2])
one_hot = F.one_hot(labels, num_classes=4)
면접 포인트¶
- 희소 벡터: 해당 위치만 1
- 의미 관계 없음: 모든 단어가 동일 거리
- 해결책: Word Embedding
POS 태깅은 무엇인가요?¶
한 줄 정의¶
문장의 각 단어에 품사(Part-of-Speech)를 부착하는 작업
품사 태그 예시¶
"나는 맛있는 사과를 먹었다"
나/NP 는/JX 맛있는/VA 사과/NNG 를/JKO 먹었다/VV
NP: 대명사
JX: 보조사
VA: 형용사
NNG: 일반명사
JKO: 목적격조사
VV: 동사
영어 품사 태그¶
| 태그 | 품사 | 예시 |
|---|---|---|
| NN | 명사 | dog, cat |
| VB | 동사 | run, eat |
| JJ | 형용사 | beautiful |
| RB | 부사 | quickly |
| DT | 관사 | the, a |
| IN | 전치사 | in, on |
왜 필요한가?¶
코드¶
# KoNLPy (한국어)
from konlpy.tag import Okt
okt = Okt()
print(okt.pos("나는 맛있는 사과를 먹었다"))
# [('나', 'Noun'), ('는', 'Josa'), ...]
# NLTK (영어)
import nltk
text = nltk.word_tokenize("I love natural language processing")
print(nltk.pos_tag(text))
# [('I', 'PRP'), ('love', 'VBP'), ...]
면접 포인트¶
- 각 단어에 품사 부착
- 구문 분석, NER, 번역의 기초 작업
- 한국어: KoNLPy (Okt, Mecab 등)
가장 간단하게 POS tagger를 만드는 방법은?¶
한 줄 답변¶
룩업 테이블: 단어별 가장 빈번한 품사를 매핑
방법 1: 룩업 테이블 (가장 간단)¶
# 빈도 기반 품사 사전
pos_dict = {
"사과": "NNG", # 명사가 가장 흔함
"먹다": "VV",
"예쁜": "VA",
...
}
def simple_tagger(word):
return pos_dict.get(word, "NNG") # 기본값: 명사
방법 2: 규칙 기반¶
def rule_based_tagger(word):
if word.endswith("다"):
return "VV" # 동사
elif word.endswith("적"):
return "XSN" # 접미사
elif word.endswith("은") or word.endswith("는"):
return "JX" # 조사
else:
return "NNG" # 기본값: 명사
방법 3: HMM (Hidden Markov Model)¶
현대적 방법¶
면접 포인트¶
- 가장 간단: 룩업 테이블 (빈도 기반)
- 문맥 고려: HMM, CRF
- 현대: BERT 기반, 라이브러리 활용
Stop Words는 무엇일까요?¶
한 줄 정의¶
분석에 큰 의미가 없는 고빈도 단어 (조사, 관사, 전치사 등)
예시¶
영어 Stop Words:
the, a, an, is, are, was, were, in, on, at, to, for, with, of...
한국어 Stop Words:
은, 는, 이, 가, 을, 를, 에, 의, 와, 과, 도, 만...
예문: "The quick brown fox jumps over the lazy dog"
제거 후: "quick brown fox jumps lazy dog"
왜 제거하나?¶
1. 의미 없음
- "the"가 있다고 문서 주제를 알 수 없음
2. 노이즈 제거
- 불필요한 패턴 학습 방지
3. 차원 축소
- 어휘 크기 감소 → 계산 효율
4. TF-IDF 왜곡 방지
- 모든 문서에 등장 → IDF 낮음 → 어차피 의미 없음
주의사항¶
무조건 제거하면 안 되는 경우:
1. 감성 분석
"not good" → "good" (의미 반전!)
2. 질의응답
"To be or not to be" → 의미 상실
3. 기계 번역
문법 정보 필요
4. 언어 모델 (LLM)
모든 토큰 필요
→ 태스크에 따라 판단 필요
코드¶
# NLTK
from nltk.corpus import stopwords
stop_words = set(stopwords.words('english'))
# 직접 정의
korean_stopwords = ['은', '는', '이', '가', '을', '를']
# 필터링
words = ["나", "는", "사과", "를", "먹었다"]
filtered = [w for w in words if w not in korean_stopwords]
# ['나', '사과', '먹었다']
면접 포인트¶
- 고빈도 저의미 단어: 조사, 관사, 전치사
- 제거 이유: 노이즈, 차원 축소
- 주의: 감성 분석, 번역에서는 제거 신중
TF-IDF 점수는 무엇이며 어떤 경우 유용한가요?¶
한 줄 정의¶
TF(단어 빈도) × IDF(역문서 빈도): 특정 문서에서 중요한 단어에 높은 점수 부여
수식¶
직관적 이해¶
"Python" in 프로그래밍 문서:
- TF 높음: 자주 등장
- IDF 중간: 일부 문서에만 등장
→ TF-IDF 높음 (중요 키워드!)
"the" in 모든 문서:
- TF 높음: 자주 등장
- IDF 매우 낮음: 모든 문서에 등장
→ TF-IDF 낮음 (불용어)
"quantum" in 물리 문서 1개:
- TF 낮음: 드물게 등장
- IDF 높음: 특정 문서에만
→ TF-IDF 중간 (특수 키워드)
유용한 경우¶
| 용도 | 설명 |
|---|---|
| 문서 검색 | 쿼리와 문서 매칭 |
| 키워드 추출 | 문서별 중요 단어 |
| 문서 분류 | 피처로 활용 |
| 유사도 계산 | 코사인 유사도 |
코드¶
from sklearn.feature_extraction.text import TfidfVectorizer
docs = [
"I love machine learning",
"Machine learning is great",
"Deep learning is a subset of machine learning"
]
vectorizer = TfidfVectorizer()
tfidf_matrix = vectorizer.fit_transform(docs)
# 단어별 TF-IDF 점수
feature_names = vectorizer.get_feature_names_out()
한계¶
1. 단어 순서 무시 (Bag of Words)
2. 의미적 유사도 반영 못함
- "car"와 "automobile" 다른 단어 취급
3. 희소 행렬
- 차원이 어휘 크기
→ 현대: Word Embedding, BERT 사용
면접 포인트¶
- TF × IDF: 빈도 높고, 다른 문서에 없으면 중요
- 용도: 검색, 키워드 추출, 분류
- 한계: 의미 관계 무시 → Embedding으로 대체
n-gram은 무엇일까요?¶
한 줄 정의¶
연속된 n개의 토큰(단어/문자) 시퀀스
예시¶
문장: "I love natural language processing"
Unigram (n=1): [I, love, natural, language, processing]
Bigram (n=2): [I love, love natural, natural language, language processing]
Trigram (n=3): [I love natural, love natural language, natural language processing]
문자 n-gram¶
단어: "hello"
Character bigram: [he, el, ll, lo]
Character trigram: [hel, ell, llo]
→ OOV(미등록어) 처리에 유용
→ 오타에 로버스트
용도¶
| 용도 | 설명 |
|---|---|
| 언어 모델 | P(다음 단어 | 이전 n-1개 단어) |
| 텍스트 분류 | n-gram을 피처로 |
| 철자 교정 | 문자 n-gram 유사도 |
| 표절 검사 | n-gram 중복 비율 |
언어 모델에서의 활용¶
Bigram 언어 모델:
P(processing | language) = count("language processing") / count("language")
문장 확률:
P("I love NLP") = P(I) × P(love|I) × P(NLP|love)
코드¶
from nltk import ngrams
sentence = "I love natural language processing".split()
bigrams = list(ngrams(sentence, 2))
# [('I', 'love'), ('love', 'natural'), ...]
# sklearn
from sklearn.feature_extraction.text import CountVectorizer
vectorizer = CountVectorizer(ngram_range=(1, 2)) # unigram + bigram
n의 선택¶
n 작음 (1-2): 데이터 희소성 ↓, 문맥 ↓
n 큼 (4+): 문맥 ↑, 데이터 희소성 ↑
실무: 보통 1-3 조합 사용
현대: RNN, Transformer로 대체 (무한 문맥)
면접 포인트¶
- 연속 n개 토큰: bigram(2), trigram(3)
- 용도: 언어 모델, 분류, 철자 교정
- 한계: n 커지면 희소성 → 딥러닝으로 대체
Word2Vec의 원리는?¶
한 줄 정의¶
주변 단어(문맥)를 이용해 단어를 밀집 벡터(Dense Vector)로 표현하는 신경망 기반 임베딩
핵심 아이디어¶
두 가지 아키텍처¶
1. CBOW (Continuous Bag of Words)
문맥 → 중심 단어 예측
[나는] [맛있는] [?] [를] [먹었다] → 사과
2. Skip-gram
중심 단어 → 문맥 예측
사과 → [나는], [맛있는], [를], [먹었다]
Skip-gram이 더 널리 사용됨 (희귀 단어에 효과적)
Skip-gram 구조¶
입력: 중심 단어 (one-hot)
↓
[Embedding Layer] (W: V×D)
↓
임베딩 벡터 (D차원)
↓
[Output Layer] (W': D×V)
↓
Softmax → 문맥 단어 확률
학습 목표¶
주어진 중심 단어 w_c에 대해
문맥 단어 w_o가 나타날 확률 최대화
max Σ log P(w_o | w_c)
Negative Sampling:
- Softmax 대신 이진 분류로 단순화
- 실제 문맥 단어 vs 랜덤 단어 구분
흥미로운 속성¶
코드¶
from gensim.models import Word2Vec
sentences = [
["나는", "사과를", "먹었다"],
["그는", "바나나를", "먹었다"]
]
model = Word2Vec(
sentences,
vector_size=100, # 임베딩 차원
window=5, # 문맥 윈도우
min_count=1,
sg=1 # 1: Skip-gram, 0: CBOW
)
# 유사 단어
model.wv.most_similar("사과")
면접 포인트¶
- 문맥 기반: 주변 단어로 의미 학습
- Skip-gram: 중심 → 문맥 예측
- 벡터 연산: king - man + woman = queen
Word2Vec에서 왼쪽 파라미터들을 임베딩으로 쓰는 이유는?¶
한 줄 답변¶
입력층-은닉층 가중치(W)가 단어의 의미 표현을 직접 학습하기 때문
Word2Vec 구조¶
입력 (V차원) 은닉 (D차원) 출력 (V차원)
One-hot ─────────────→ Embedding ─────────────→ Softmax
W (V×D) W' (D×V)
"왼쪽" "오른쪽"
왜 W (왼쪽)를 쓰는가?¶
W의 각 행 = 해당 단어의 임베딩 벡터
예: 어휘 크기 10000, 임베딩 차원 300
W: (10000 × 300)
단어 "사과"의 인덱스가 42라면:
임베딩("사과") = W[42, :] = 300차원 벡터
→ W는 단어를 저차원 공간으로 매핑하는 "룩업 테이블"
W' (오른쪽)는?¶
W': 출력 가중치
- 임베딩 → 문맥 단어 예측에 사용
- 학습 과정의 일부지만 최종 임베딩은 아님
실제로:
- W만 사용 (일반적)
- W + W' 평균 사용 (GloVe에서)
시각적 이해¶
면접 포인트¶
- W 행렬의 행 = 단어 임베딩
- One-hot × W = 룩업 테이블 역할
- W'는 출력용, 보통 W만 임베딩으로 사용
문장에서 "Apple"이란 단어가 과일인지 회사인지 식별하는 모델?¶
한 줄 답변¶
Word Sense Disambiguation (WSD) 문제 - 문맥 기반 분류 모델 사용
접근 방법¶
| 방법 | 설명 |
|---|---|
| 규칙 기반 | 주변 단어 키워드로 판단 |
| 전통 ML | 문맥 피처 + SVM/NB |
| 딥러닝 | BERT 등 문맥 임베딩 활용 |
1. 규칙 기반 (가장 간단)¶
def classify_apple(sentence):
fruit_keywords = ['eat', 'fruit', 'red', 'delicious', 'tree']
company_keywords = ['iPhone', 'Mac', 'CEO', 'stock', 'company']
sentence_lower = sentence.lower()
fruit_score = sum(1 for w in fruit_keywords if w in sentence_lower)
company_score = sum(1 for w in company_keywords if w in sentence_lower)
return 'fruit' if fruit_score > company_score else 'company'
2. 문맥 임베딩 (BERT)¶
from transformers import BertTokenizer, BertModel
import torch
tokenizer = BertTokenizer.from_pretrained('bert-base-uncased')
model = BertModel.from_pretrained('bert-base-uncased')
def get_apple_embedding(sentence):
inputs = tokenizer(sentence, return_tensors='pt')
outputs = model(**inputs)
# "Apple" 토큰의 위치 찾기
apple_idx = (inputs['input_ids'] == tokenizer.convert_tokens_to_ids('apple')).nonzero()
# 해당 위치의 임베딩 (문맥 반영됨!)
apple_embedding = outputs.last_hidden_state[0, apple_idx]
return apple_embedding
# 다른 문맥에서 다른 임베딩!
emb1 = get_apple_embedding("I ate a delicious apple") # 과일
emb2 = get_apple_embedding("Apple released a new iPhone") # 회사
BERT의 장점¶
Word2Vec: "Apple"은 항상 같은 벡터
BERT: 문맥에 따라 다른 벡터
"I ate an apple" → 과일 의미에 가까운 벡터
"Apple stock rose" → 회사 의미에 가까운 벡터
→ 문맥화된 임베딩 (Contextualized Embedding)
면접 포인트¶
- WSD 문제: 동음이의어 해결
- BERT: 문맥마다 다른 임베딩
- 과일 vs 회사는 주변 문맥으로 구분
영화 리뷰가 긍정적인지 부정적인지 예측하는 모델?¶
한 줄 답변¶
감성 분석(Sentiment Analysis) - 텍스트 분류 모델로 해결
접근 방법¶
| 방법 | 설명 | 성능 |
|---|---|---|
| 사전 기반 | 긍/부정 단어 수 카운트 | 낮음 |
| ML | TF-IDF + SVM/NB | 중간 |
| 딥러닝 | LSTM, CNN | 높음 |
| Transformer | BERT Fine-tuning | 최고 |
1. 간단한 방법 (사전 기반)¶
positive_words = ['good', 'great', 'excellent', 'amazing', 'love']
negative_words = ['bad', 'terrible', 'awful', 'hate', 'boring']
def simple_sentiment(text):
text = text.lower()
pos_count = sum(1 for w in positive_words if w in text)
neg_count = sum(1 for w in negative_words if w in text)
return 'positive' if pos_count > neg_count else 'negative'
2. 전통 ML¶
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.svm import SVC
from sklearn.pipeline import Pipeline
pipeline = Pipeline([
('tfidf', TfidfVectorizer(max_features=5000)),
('clf', SVC(kernel='linear'))
])
pipeline.fit(train_texts, train_labels)
predictions = pipeline.predict(test_texts)
3. BERT Fine-tuning (최고 성능)¶
from transformers import BertForSequenceClassification, Trainer
model = BertForSequenceClassification.from_pretrained(
'bert-base-uncased',
num_labels=2 # 긍정/부정
)
trainer = Trainer(
model=model,
train_dataset=train_dataset,
eval_dataset=eval_dataset
)
trainer.train()
주의사항¶
어려운 케이스:
- "Not bad at all" → 긍정 (부정 단어지만)
- "The plot was predictable but acting was great" → 혼합
- 아이러니, 비꼼 감지
→ 문맥 이해 필수 (BERT 유리)
면접 포인트¶
- 감성 분석 = 텍스트 이진 분류
- 사전 < TF-IDF+ML < BERT
- "not bad" 같은 부정 표현 처리 중요
뉴스 기사를 주제별로 자동 분류하는 시스템?¶
한 줄 답변¶
다중 클래스 텍스트 분류 - TF-IDF/BERT + 분류기
전체 파이프라인¶
1. 데이터 수집 → 2. 전처리 → 3. 피처 추출 → 4. 분류기 학습 → 5. 예측
뉴스 기사 → 토큰화, 정제 → TF-IDF/BERT → Softmax → 스포츠/정치/경제/...
카테고리 예시¶
방법 1: TF-IDF + ML¶
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.naive_bayes import MultinomialNB
from sklearn.pipeline import Pipeline
pipeline = Pipeline([
('tfidf', TfidfVectorizer(max_features=10000)),
('clf', MultinomialNB())
])
pipeline.fit(train_texts, train_categories)
predicted = pipeline.predict(new_articles)
방법 2: BERT Fine-tuning¶
from transformers import BertForSequenceClassification
model = BertForSequenceClassification.from_pretrained(
'klue/bert-base', # 한국어 BERT
num_labels=7 # 7개 카테고리
)
# Fine-tuning...
핵심 고려사항¶
1. 불균형 데이터
- 카테고리별 기사 수 다름
- Oversampling, Class weights
2. 라벨 노이즈
- 복합 주제 기사: "IT기업 CEO 정치 발언"
- Multi-label 고려
3. 실시간 분류
- 새 기사 빠르게 분류
- 경량 모델 or 캐싱
4. 시간에 따른 변화
- 신조어, 새 토픽 등장
- 주기적 재학습
면접 포인트¶
- 다중 클래스 분류 문제
- TF-IDF+Naive Bayes (빠름) vs BERT (정확)
- 불균형 데이터, Multi-label 고려 필요
Dependency Parsing이란 무엇인가요?¶
한 줄 정의¶
문장 내 단어들 간의 의존(수식) 관계를 분석하여 트리 구조로 표현
예시¶
"나는 맛있는 사과를 먹었다"
먹었다
/ | \
나는 사과를
|
맛있는
관계:
- "나는" → "먹었다" (주어)
- "사과를" → "먹었다" (목적어)
- "맛있는" → "사과를" (수식어)
의존 관계 유형¶
| 관계 | 설명 | 예시 |
|---|---|---|
| nsubj | 주어 | 나 → 먹었다 |
| dobj | 직접 목적어 | 사과 → 먹었다 |
| amod | 형용사 수식어 | 맛있는 → 사과 |
| prep | 전치사 | in → go |
| det | 관사 | the → dog |
왜 중요한가?¶
1. 문장 구조 이해
- 주어, 목적어 식별
- 수식 관계 파악
2. 정보 추출
- "누가 무엇을 했는가"
- 관계 추출
3. 기계 번역
- 어순이 다른 언어 변환
4. 질의응답
- 질문 의도 파악
코드¶
# spaCy (영어)
import spacy
nlp = spacy.load("en_core_web_sm")
doc = nlp("Apple is looking at buying UK startup")
for token in doc:
print(f"{token.text} --{token.dep_}--> {token.head.text}")
# Apple --nsubj--> looking
# is --aux--> looking
# looking --ROOT--> looking
# at --prep--> looking
# buying --pcomp--> at
# UK --compound--> startup
# startup --dobj--> buying
면접 포인트¶
- 단어 간 의존 관계 → 트리 구조
- 용도: 정보 추출, 번역, QA
- spaCy, Stanford Parser 등 도구 활용
RNN에 대해 설명해주세요¶
한 줄 정의¶
이전 시점의 출력을 현재 입력에 반영하여 순서 정보를 학습하는 신경망
기본 구조¶
x₁ → [RNN] → h₁ → [RNN] → h₂ → [RNN] → h₃
↑ ↑ ↑
└──────────←┴──────────←┘
(이전 hidden state 전달)
hₜ = tanh(W_xh × xₜ + W_hh × hₜ₋₁ + b)
시각적 이해¶
입력: "나는 사과를 먹었다"
시점1: "나는" → h₁ = f(x₁, h₀)
시점2: "사과를" → h₂ = f(x₂, h₁) ← h₁의 정보 포함
시점3: "먹었다" → h₃ = f(x₃, h₂) ← h₁, h₂ 정보 포함
→ h₃에 전체 문맥 정보 압축!
용도¶
| 용도 | 설명 |
|---|---|
| 언어 모델 | 다음 단어 예측 |
| 감성 분석 | 문장 → 긍/부정 |
| 기계 번역 | Seq2Seq |
| 음성 인식 | 음성 → 텍스트 |
문제점: Vanishing Gradient¶
긴 시퀀스:
h₁ → h₂ → h₃ → ... → h₁₀₀
역전파 시:
∂L/∂h₁ = ∂L/∂h₁₀₀ × ∂h₁₀₀/∂h₉₉ × ... × ∂h₂/∂h₁
→ 연쇄 곱셈 → gradient 소실!
→ 먼 과거 정보 학습 불가
코드¶
import torch.nn as nn
# 기본 RNN
rnn = nn.RNN(
input_size=100, # 입력 차원
hidden_size=256, # 은닉 상태 차원
num_layers=2, # 층 수
batch_first=True
)
# 입력: (batch, seq_len, input_size)
output, h_n = rnn(x, h_0)
면접 포인트¶
- 순환 구조: 이전 출력 → 현재 입력
- 순서 정보 학습: 시퀀스 데이터에 적합
- 문제: Vanishing Gradient → LSTM/GRU로 해결
LSTM은 왜 유용한가요?¶
한 줄 답변¶
Gate 메커니즘으로 장기 의존성을 학습하여 RNN의 Vanishing Gradient 문제 해결
RNN vs LSTM¶
RNN 문제:
"나는 10년 전 파리에서 프랑스어를 배웠다. ... 나는 ___어를 할 수 있다."
↓
100단어 전 "프랑스어" 정보가 소실됨
LSTM 해결:
Cell State로 중요 정보 장기 보존!
LSTM 구조¶
┌────────────────────────────────┐
│ Cell State │
│ cₜ = fₜ⊙cₜ₋₁ + iₜ⊙c̃ₜ │
└────────────────────────────────┘
↑ ↑
┌────────────┼────────┼────────────┐
│ Forget │ Input │ Output │
│ Gate │ Gate │ Gate │
│ fₜ │ iₜ │ oₜ │
└────────────┴────────┴────────────┘
↑
[xₜ, hₜ₋₁]
3개의 Gate¶
| Gate | 역할 | 수식 |
|---|---|---|
| Forget | 이전 정보 얼마나 버릴지 | fₜ = σ(W_f·[hₜ₋₁, xₜ]) |
| Input | 새 정보 얼마나 저장할지 | iₜ = σ(W_i·[hₜ₋₁, xₜ]) |
| Output | 출력에 얼마나 반영할지 | oₜ = σ(W_o·[hₜ₋₁, xₜ]) |
왜 Gradient가 소실되지 않나?¶
Cell State 업데이트:
cₜ = fₜ ⊙ cₜ₋₁ + iₜ ⊙ c̃ₜ
역전파 시:
∂cₜ/∂cₜ₋₁ = fₜ (forget gate)
fₜ ≈ 1이면 gradient가 거의 그대로 전달!
→ 정보 고속도로 (Highway)
→ 장기 의존성 학습 가능
GRU (간소화 버전)¶
LSTM: 3개 gate, 2개 상태 (h, c)
GRU: 2개 gate, 1개 상태 (h)
GRU = Reset Gate + Update Gate
→ 파라미터 적음, 비슷한 성능
코드¶
import torch.nn as nn
lstm = nn.LSTM(
input_size=100,
hidden_size=256,
num_layers=2,
batch_first=True,
bidirectional=True # 양방향
)
gru = nn.GRU(
input_size=100,
hidden_size=256,
num_layers=2
)
면접 포인트¶
- Gate 메커니즘: 정보 흐름 제어
- Cell State: 장기 정보 고속도로
- Vanishing Gradient 해결: fₜ≈1이면 gradient 유지
Translate 과정 Flow에 대해 설명해주세요¶
한 줄 답변¶
Encoder-Decoder 구조: 입력 문장 인코딩 → Context Vector → 출력 문장 디코딩
Seq2Seq 구조¶
입력: "I love you" 출력: "나는 너를 사랑해"
I → love → you
↓
[ Encoder ]
↓
Context Vector
↓
[ Decoder ]
↓
나는 → 너를 → 사랑해
단계별 설명¶
1. Encoder (입력 처리)
- 입력 문장을 순차적으로 읽음
- 마지막 hidden state = Context Vector
- 전체 입력의 의미 압축
2. Context Vector
- 고정 크기 벡터
- 입력 문장 전체 정보 담음
3. Decoder (출력 생성)
- Context Vector로 시작
- 이전 출력을 다음 입력으로
- <EOS> 토큰까지 생성
Attention 메커니즘¶
문제: 긴 문장 → Context Vector에 모든 정보 담기 어려움
해결: Attention
- 디코딩 시 Encoder의 모든 hidden state 참조
- 관련 있는 부분에 더 집중
"I love you" → "나는 너를 사랑해"
"나는" 생성 시 "I"에 집중
"사랑해" 생성 시 "love"에 집중
Transformer 방식 (현대)¶
기존 Seq2Seq: RNN 기반, 순차 처리
Transformer: Self-Attention, 병렬 처리
장점:
- 빠른 학습 (병렬화)
- 장거리 의존성 더 잘 학습
- 현재 번역 표준 (Google, DeepL 등)
코드 (PyTorch Seq2Seq)¶
class Encoder(nn.Module):
def __init__(self, vocab_size, embed_dim, hidden_dim):
super().__init__()
self.embedding = nn.Embedding(vocab_size, embed_dim)
self.rnn = nn.LSTM(embed_dim, hidden_dim, batch_first=True)
def forward(self, x):
embedded = self.embedding(x)
outputs, (hidden, cell) = self.rnn(embedded)
return hidden, cell # Context
class Decoder(nn.Module):
def __init__(self, vocab_size, embed_dim, hidden_dim):
super().__init__()
self.embedding = nn.Embedding(vocab_size, embed_dim)
self.rnn = nn.LSTM(embed_dim, hidden_dim, batch_first=True)
self.fc = nn.Linear(hidden_dim, vocab_size)
def forward(self, x, hidden, cell):
embedded = self.embedding(x)
output, (hidden, cell) = self.rnn(embedded, (hidden, cell))
prediction = self.fc(output)
return prediction, hidden, cell
면접 포인트¶
- Encoder → Context → Decoder
- Attention: 긴 문장 처리 개선
- 현대: Transformer (Self-Attention)
번역을 Unsupervised로 할 수 있을까?¶
한 줄 답변¶
가능함! 병렬 코퍼스 없이 단일 언어 데이터만으로 번역 학습
왜 중요한가?¶
Supervised 번역:
- 영-한 병렬 코퍼스 필요
- 희귀 언어 쌍은 데이터 부족
Unsupervised 번역:
- 각 언어의 단일 코퍼스만 필요
- 영어 뉴스 + 한국어 뉴스 → 번역 모델
핵심 아이디어¶
| 기법 | 설명 |
|---|---|
| 공유 임베딩 | 다른 언어를 같은 공간에 매핑 |
| Back-translation | 번역 → 역번역으로 학습 데이터 생성 |
| Denoising | 노이즈 추가 → 복원 학습 |
1. 공유 임베딩 공간¶
영어: "cat" → [0.1, 0.5, 0.3]
한국어: "고양이" → [0.1, 0.5, 0.3] (같은 공간!)
방법:
- 교차 언어 임베딩 (Cross-lingual embedding)
- 사전으로 몇 개 단어 정렬 → 나머지 자동 정렬
2. Back-translation¶
반복 학습:
1. 초기 (노이즈 많은) 모델로 영→한 번역
2. 번역 결과로 한→영 모델 학습
3. 한→영 모델로 한국어 역번역
4. 역번역 결과로 영→한 모델 개선
5. 반복...
→ 점진적으로 품질 향상
3. Denoising Auto-Encoder¶
입력: "나는 사과를 먹었다"
노이즈: "를 사과 나는 먹었다" (어순 섞기)
출력: "나는 사과를 먹었다" (복원)
→ 각 언어의 문법/의미 학습
→ 공유 인코더로 언어 중립적 표현
대표 연구¶
- MUSE (Facebook, 2017): 교차 언어 임베딩
- Unsupervised NMT (Facebook, 2018)
- XLM (Cross-lingual LM): 다국어 사전학습
- mBART: 다국어 denoising 사전학습
한계¶
면접 포인트¶
- 가능! 단일 코퍼스만으로 학습
- 핵심: 공유 임베딩 + Back-translation
- Supervised보다 성능은 낮지만 데이터 부족 시 유용
PageRank 알고리즘은 어떻게 작동하나요?¶
한 줄 정의¶
웹페이지 간 링크 구조를 분석하여 각 페이지의 중요도(순위)를 계산
핵심 아이디어¶
"많은 중요한 페이지가 링크하는 페이지는 중요하다"
예:
- 페이지 A를 많은 페이지가 링크 → A 중요
- 중요한 페이지 B가 A를 링크 → A 더 중요
투표로 비유:
- 각 링크 = 1표
- 중요한 페이지의 표 = 더 가치 있음
수식¶
PR(A) = (1-d)/N + d × Σ PR(i)/L(i)
i→A
PR(A): 페이지 A의 PageRank
d: damping factor (보통 0.85)
N: 전체 페이지 수
L(i): 페이지 i의 outgoing 링크 수
해석:
- 15% 확률로 랜덤 페이지 이동
- 85% 확률로 링크 따라 이동
시각적 예시¶
[A] ←──── [B]
↑ |
│ ↓
[D] ←──── [C]
링크 수:
- A: B, D에서 받음 (in=2)
- B: 없음 (in=0)
- C: B에서 받음 (in=1)
- D: C에서 받음 (in=1)
결과: A > D ≈ C > B
계산 과정 (Power Iteration)¶
import numpy as np
# 전이 행렬 (링크 구조)
# M[i][j] = 1/out_degree(j) if j→i
M = np.array([
[0, 1, 0, 1], # A receives from B, D
[0, 0, 0, 0], # B receives none
[0, 1, 0, 0], # C receives from B
[0, 0, 1, 0] # D receives from C
]) * 0.5 # 정규화
d = 0.85
N = 4
R = np.ones(N) / N # 초기값
# Power Iteration
for _ in range(100):
R = (1-d)/N + d * M @ R
print(R) # PageRank 점수
NLP에서의 활용¶
| 용도 | 설명 |
|---|---|
| TextRank | 문장/단어 중요도 → 요약, 키워드 추출 |
| 문서 랭킹 | 문서 그래프에서 중요 문서 |
| 지식 그래프 | 엔티티 중요도 계산 |
TextRank (문장 요약)¶
면접 포인트¶
- 링크 구조 기반 중요도 계산
- 재귀적: 중요한 페이지가 링크 → 더 중요
- NLP 응용: TextRank (요약, 키워드 추출)