콘텐츠로 이동

자연어처리 (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

왜 필요한가?

1. 구문 분석의 기초
2. 개체명 인식 (NER) 전처리
3. 기계 번역의 품질 향상
4. 동음이의어 해결: "bank" (은행 vs 둑)

코드

# 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)

전이 확률: P(현재 품사 | 이전 품사)
방출 확률: P(단어 | 품사)

→ Viterbi 알고리즘으로 최적 품사 시퀀스 탐색

장점: 문맥 고려
단점: 복잡, 학습 데이터 필요

현대적 방법

1. BiLSTM + CRF
2. BERT 기반 시퀀스 레이블링
3. 사전 학습된 형태소 분석기 활용

→ 실무에서는 KoNLPy, spaCy 등 라이브러리 사용

면접 포인트

  • 가장 간단: 룩업 테이블 (빈도 기반)
  • 문맥 고려: 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(역문서 빈도): 특정 문서에서 중요한 단어에 높은 점수 부여

수식

TF-IDF(t, d) = TF(t, d) × IDF(t)

TF(t, d) = 문서 d에서 단어 t의 빈도
IDF(t) = log(전체 문서 수 / 단어 t를 포함한 문서 수)

직관적 이해

"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 랜덤 단어 구분

흥미로운 속성

벡터 연산으로 의미 표현:

king - man + woman ≈ queen
Paris - France + Italy ≈ Rome

→ 의미적 관계가 벡터 공간에 인코딩!

코드

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에서)

시각적 이해

단어 "고양이" (인덱스 5):
One-hot: [0, 0, 0, 0, 0, 1, 0, 0, ...]
              ↓
         W[5, :] 행 추출
              ↓
         임베딩 벡터

면접 포인트

  • 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 → 스포츠/정치/경제/...

카테고리 예시

- 정치
- 경제
- 스포츠
- 연예
- IT/과학
- 사회
- 국제

방법 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 사전학습

한계

- Supervised보다 성능 낮음
- 언어 쌍이 유사할수록 효과적
- 저자원 언어에서 주로 유용

면접 포인트

  • 가능! 단일 코퍼스만으로 학습
  • 핵심: 공유 임베딩 + 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 (문장 요약)

문장을 노드로, 유사도를 엣지로:

문장1 ─0.8─ 문장2
  │          │
 0.3        0.5
  │          │
문장3 ─0.2─ 문장4

PageRank 적용 → 중요 문장 선택 → 요약

면접 포인트

  • 링크 구조 기반 중요도 계산
  • 재귀적: 중요한 페이지가 링크 → 더 중요
  • NLP 응용: TextRank (요약, 키워드 추출)