딥러닝¶
면접에서 자주 나오는 딥러닝 질문들
질문 목록¶
기본 개념¶
- [ ] 딥러닝은 무엇인가요?
- [ ] 왜 갑자기 딥러닝이 부흥했을까요?
- [ ] 마지막으로 읽은 논문은 무엇인가요?
- [ ] Cost Function과 Activation Function은 무엇인가요?
- [ ] Data Normalization은 무엇이고 왜 필요한가요?
Activation Function¶
- [ ] 알고있는 Activation Function에 대해 알려주세요
- [ ] 요즘 Sigmoid 보다 ReLU를 많이 쓰는 이유는?
- [ ] Non-Linearity라는 말의 의미와 그 필요성은?
- [ ] ReLU로 어떻게 곡선 함수를 근사하나?
- [ ] ReLU의 문제점은?
- [ ] Bias는 왜 있는걸까?
학습 과정¶
- [ ] Gradient Descent에 대해서 쉽게 설명한다면?
- [ ] 왜 꼭 Gradient를 써야 할까?
- [ ] GD 중에 때때로 Loss가 증가하는 이유는?
- [ ] 중학생이 이해할 수 있게 더 쉽게 설명 한다면?
- [ ] Back Propagation에 대해서 쉽게 설명 한다면?
- [ ] Local Minima 문제에도 불구하고 딥러닝이 잘 되는 이유는?
- [ ] GD가 Local Minima 문제를 피하는 방법은?
- [ ] 찾은 해가 Global Minimum인지 아닌지 알 수 있는 방법은?
데이터 분할¶
- [ ] Training 세트와 Test 세트를 분리하는 이유는?
- [ ] Validation 세트가 따로 있는 이유는?
- [ ] Test 세트가 오염되었다는 말의 뜻은?
정규화 기법¶
- [ ] 오버피팅일 경우 어떻게 대처해야 할까요?
- [ ] Regularization이란 무엇인가?
- [ ] Batch Normalization의 효과는?
- [ ] Dropout의 효과는?
- [ ] BN 적용해서 학습 이후 실제 사용시에 주의할 점은?
- [ ] GAN에서 Generator 쪽에도 BN을 적용해도 될까?
Optimizer¶
- [ ] SGD, RMSprop, Adam에 대해서 아는대로 설명한다면?
- [ ] SGD에서 Stochastic의 의미는?
- [ ] 미니배치를 작게 할때의 장단점은?
- [ ] 모멘텀의 수식을 적어 본다면?
초기화/하이퍼파라미터¶
- [ ] 하이퍼 파라미터는 무엇인가요?
- [ ] Weight Initialization 방법에 대해 말해주세요
- [ ] 볼츠만 머신은 무엇인가요?
프레임워크/구현¶
- [ ] Tensorflow, Keras, PyTorch, Caffe, Mxnet 중 선호하는 프레임워크는?
- [ ] 간단한 MNIST 분류기를 MLP+CPU 버전으로 numpy로 만든다면?
- [ ] 어느 정도 돌아가는 녀석을 작성하기까지 몇시간 정도 걸릴까?
- [ ] Back Propagation은 몇줄인가?
- [ ] CNN으로 바꾼다면 얼마나 추가될까?
- [ ] 간단한 MNIST 분류기를 TF, Keras, PyTorch 등으로 작성하는데?
- [ ] CNN이 아닌 MLP로 해도 잘 될까?
- [ ] 마지막 레이어 부분에 대해서 설명 한다면?
- [ ] 학습은 BCE loss로 하되 상황을 MSE loss로 보고 싶다면?
- [ ] 만약 한글 (인쇄물) OCR을 만든다면 데이터 수집은?
GPU/하드웨어¶
- [ ] 딥러닝할 때 GPU를 쓰면 좋은 이유는?
- [ ] 학습 중인데 GPU를 100% 사용하지 않고 있다
- [ ] GPU를 두개 다 쓰고 싶다
- [ ] 학습시 필요한 GPU 메모리는 어떻게 계산하는가?
디버깅/실무¶
- [ ] TF, Keras, PyTorch 등을 사용할 때 디버깅 노하우는?
- [ ] 뉴럴넷의 가장 큰 단점은 무엇인가?
- [ ] One-Shot Learning은 무엇인가?
답변¶
딥러닝은 무엇인가요?¶
한 줄 정의¶
여러 층(layer)의 신경망을 통해 데이터에서 계층적 표현을 자동으로 학습하는 기법
딥러닝 vs 머신러닝¶
왜 "딥(Deep)"인가?¶
얕은 네트워크 (1-2 층):
입력 → [은닉층] → 출력
깊은 네트워크 (수십~수백 층):
입력 → [층1] → [층2] → ... → [층N] → 출력
↓ ↓ ↓
저수준 중수준 고수준
(엣지) (패턴) (의미)
계층적 표현 학습¶
딥러닝의 구성 요소¶
| 요소 | 역할 |
|---|---|
| 뉴런 | 가중치 합 + 활성화 함수 |
| 층 (Layer) | 뉴런들의 집합 |
| 활성화 함수 | 비선형성 추가 (ReLU 등) |
| 손실 함수 | 예측 오차 측정 |
| 역전파 | 기울기 계산하여 학습 |
| 옵티마이저 | 가중치 업데이트 (Adam 등) |
면접 포인트¶
- 계층적 표현 학습: 저수준 → 고수준 특징
- 자동 피처 학습: 수동 피처 엔지니어링 불필요
- 깊을수록: 더 복잡한 패턴 학습 가능
왜 갑자기 딥러닝이 부흥했을까요?¶
한 줄 답변¶
데이터 + GPU + 알고리즘 혁신의 동시 발전 (2012년 AlexNet)
세 가지 요인¶
1. 빅데이터
- ImageNet (1400만 이미지)
- 인터넷, SNS, 모바일 데이터 폭발
- 딥러닝은 데이터가 많을수록 성능 ↑
2. GPU 컴퓨팅
- NVIDIA CUDA (2007)
- 병렬 연산 → 학습 100배 가속
- 클라우드로 GPU 접근 용이
3. 알고리즘 혁신
- ReLU: Vanishing Gradient 해결
- Dropout: 과적합 방지
- Batch Normalization: 학습 안정화
역사적 타임라인¶
1960s: 퍼셉트론 발명
1969: XOR 문제 → 1차 AI 겨울
1986: 역전파 알고리즘
1990s: SVM, 앙상블 전성기 → 2차 AI 겨울
2006: Deep Belief Network (Hinton)
2012: ★ AlexNet - ImageNet 우승 (오차 15% 감소)
2014: GAN, Adam optimizer
2015: ResNet (152층)
2017: Transformer → NLP 혁명
2012년 AlexNet의 의미¶
ImageNet Challenge 2012:
기존 최고 성능: 26% 오류율 (전통 CV)
AlexNet: 15% 오류율 (딥러닝)
→ 11% 포인트 차이는 혁명적!
→ 모든 분야에서 딥러닝 연구 시작
AlexNet의 핵심 기술¶
| 기술 | 효과 |
|---|---|
| ReLU | 깊은 학습 가능 |
| Dropout | 과적합 방지 |
| GPU 학습 | 대규모 모델 학습 가능 |
| Data Augmentation | 데이터 부족 보완 |
면접 포인트¶
- 2012 AlexNet: 딥러닝 부흥의 시작점
- 삼박자: 데이터 + GPU + 알고리즘
- ReLU, Dropout: 핵심 기술 혁신
Cost Function과 Activation Function은 무엇인가요?¶
한 줄 정의¶
Cost Function: 예측 오차를 측정 | Activation Function: 비선형성을 추가
Cost Function (손실 함수)¶
| 문제 유형 | Cost Function | 수식 |
|---|---|---|
| 회귀 | MSE | Σ(y - ŷ)² / n |
| 이진 분류 | Binary Cross-Entropy | -Σ[y·log(ŷ) + (1-y)·log(1-ŷ)] |
| 다중 분류 | Categorical Cross-Entropy | -Σ y·log(ŷ) |
import torch.nn as nn
# 회귀
loss_fn = nn.MSELoss()
# 이진 분류
loss_fn = nn.BCEWithLogitsLoss()
# 다중 분류
loss_fn = nn.CrossEntropyLoss()
Activation Function (활성화 함수)¶
역할: 비선형성 추가
선형만 있으면:
f(g(x)) = W₂(W₁x) = (W₂W₁)x = Wx
→ 아무리 깊어도 선형!
비선형 추가:
f(g(x)) = σ(W₂·σ(W₁x))
→ 복잡한 패턴 학습 가능
| 활성화 함수 | 수식 | 특징 |
|---|---|---|
| Sigmoid | 1/(1+e^-x) | 출력 (0,1), Vanishing Gradient |
| Tanh | (e^x-e^-x)/(e^x+e^-x) | 출력 (-1,1) |
| ReLU | max(0, x) | 현재 표준, 빠름 |
| Leaky ReLU | max(0.01x, x) | Dead ReLU 해결 |
| Softmax | e^xᵢ/Σe^xⱼ | 다중 분류 출력층 |
import torch.nn as nn
# 은닉층
nn.ReLU()
nn.LeakyReLU(0.01)
nn.GELU() # Transformer에서 사용
# 출력층
nn.Sigmoid() # 이진 분류
nn.Softmax(dim=1) # 다중 분류
둘의 관계¶
Forward Pass:
입력 → [선형변환] → [Activation] → ... → 출력
↓
Cost Function으로 오차 계산
↓
Backward Pass: 역전파로 학습
면접 포인트¶
- Cost Function: 학습 목표 (최소화 대상)
- Activation: 비선형성 (복잡한 패턴 학습)
- ReLU: 현재 표준 활성화 함수
Data Normalization은 무엇이고 왜 필요한가요?¶
한 줄 정의¶
입력 데이터의 스케일을 통일하여 학습을 안정화하고 수렴 속도를 높이는 전처리
왜 필요한가?¶
정규화 없이:
- 피처 A: 0~1
- 피처 B: 1000~10000
→ 피처 B의 영향이 지배적
→ Gradient가 불균형
→ 학습 불안정, 수렴 느림
정규화 후:
- 피처 A: 0~1
- 피처 B: 0~1
→ 균형 잡힌 학습
시각적 이해¶
정규화 방법¶
| 방법 | 수식 | 결과 | 특징 |
|---|---|---|---|
| Min-Max | (x-min)/(max-min) | [0, 1] | 이미지에 자주 사용 |
| Z-score | (x-μ)/σ | 평균0, 분산1 | 가장 일반적 |
| /255 | x/255 | [0, 1] | 이미지 픽셀 정규화 |
# 이미지 정규화
images = images / 255.0 # [0, 255] → [0, 1]
# 또는 ImageNet 기준 정규화
normalize = transforms.Normalize(
mean=[0.485, 0.456, 0.406],
std=[0.229, 0.224, 0.225]
)
딥러닝에서의 정규화¶
1. 입력 정규화: 데이터 전처리
2. Batch Normalization: 층 사이 정규화
3. Layer Normalization: Transformer에서 사용
각각 다른 위치, 다른 목적
주의사항¶
# ❌ 잘못된 방법 (Data Leakage)
mean = X_전체.mean()
std = X_전체.std()
# ✅ 올바른 방법
mean = X_train.mean()
std = X_train.std()
X_train_norm = (X_train - mean) / std
X_test_norm = (X_test - mean) / std # train 통계 사용
면접 포인트¶
- 스케일 통일 → 학습 안정화, 수렴 속도 ↑
- Test에는 Train 통계 사용 (Data Leakage 방지)
- 이미지: /255 또는 ImageNet 정규화
알고있는 Activation Function에 대해 알려주세요¶
한 줄 정의¶
뉴런의 출력에 비선형성을 추가하여 복잡한 패턴을 학습할 수 있게 하는 함수
주요 활성화 함수¶
| 함수 | 수식 | 범위 | 특징 |
|---|---|---|---|
| Sigmoid | σ(x) = 1/(1+e^-x) | (0, 1) | 이진 분류 출력, Vanishing Gradient |
| Tanh | (e^x-e^-x)/(e^x+e^-x) | (-1, 1) | 0 중심, Vanishing Gradient |
| ReLU | max(0, x) | [0, ∞) | 현재 표준, 빠름, Dead ReLU |
| Leaky ReLU | max(αx, x) | (-∞, ∞) | Dead ReLU 해결 |
| ELU | x if x>0, α(e^x-1) else | (-α, ∞) | 음수 출력, 부드러움 |
| GELU | x·Φ(x) | - | Transformer 표준 |
| Swish | x·σ(x) | - | 자기 게이트, 부드러움 |
| Softmax | e^xᵢ/Σe^xⱼ | (0, 1), 합=1 | 다중 분류 출력 |
시각적 비교¶
용도별 선택¶
| 위치 | 추천 활성화 함수 |
|---|---|
| 은닉층 (일반) | ReLU, Leaky ReLU |
| 은닉층 (Transformer) | GELU |
| 출력층 (이진 분류) | Sigmoid |
| 출력층 (다중 분류) | Softmax |
| 출력층 (회귀) | 없음 (Linear) |
코드¶
import torch.nn as nn
# 은닉층
nn.ReLU()
nn.LeakyReLU(negative_slope=0.01)
nn.GELU()
nn.SiLU() # Swish
# 출력층
nn.Sigmoid()
nn.Softmax(dim=-1)
면접 포인트¶
- ReLU: 현재 표준, 빠름, Dead ReLU 주의
- GELU: Transformer (BERT, GPT)에서 사용
- Softmax: 다중 분류 출력층 전용
요즘 Sigmoid 보다 ReLU를 많이 쓰는 이유는?¶
한 줄 답변¶
Vanishing Gradient 문제 해결 + 계산 효율성
Sigmoid의 문제¶
Sigmoid: σ(x) = 1/(1+e^-x)
미분: σ'(x) = σ(x)(1-σ(x))
최대 미분값: 0.25 (x=0일 때)
층이 깊어지면:
0.25 × 0.25 × 0.25 × ... = 거의 0
→ Vanishing Gradient!
→ 앞쪽 층이 학습 안 됨
ReLU의 장점¶
ReLU: f(x) = max(0, x)
미분: f'(x) = 1 (x>0), 0 (x≤0)
양수 영역에서:
1 × 1 × 1 × ... = 1
→ Gradient가 사라지지 않음!
→ 깊은 네트워크 학습 가능
비교¶
| Sigmoid | ReLU | |
|---|---|---|
| 미분 최대 | 0.25 | 1 |
| 계산 | exp 연산 (느림) | max 연산 (빠름) |
| 희소성 | 없음 | 있음 (0이 많음) |
| Vanishing Gradient | 심함 | 없음 |
| 출력 범위 | (0, 1) | [0, ∞) |
계산 효율성¶
# Sigmoid: 지수 연산
def sigmoid(x):
return 1 / (1 + np.exp(-x))
# ReLU: 단순 비교
def relu(x):
return np.maximum(0, x)
# ReLU가 훨씬 빠름!
그래도 Sigmoid를 쓰는 경우¶
면접 포인트¶
- Vanishing Gradient: Sigmoid 미분 최대 0.25
- ReLU: 미분이 1, 깊은 네트워크 학습 가능
- 계산 속도: ReLU가 훨씬 빠름
Non-Linearity라는 말의 의미와 그 필요성은?¶
한 줄 정의¶
입력과 출력이 직선 관계가 아닌 것. 복잡한 패턴 학습에 필수
선형 vs 비선형¶
왜 비선형이 필요한가?¶
선형만 있으면:
층1: y = W₁x
층2: z = W₂y = W₂W₁x = (W₂W₁)x = Wx
→ 아무리 층을 쌓아도 결국 선형!
→ 100층 네트워크 = 1층 네트워크와 표현력 동일
시각적 이해¶
선형 분류: 비선형 분류:
│ ○○○ ╭──────╮
│ ○○○ │ ○○○ │
────┼────── (직선만) │ ○○○ │ (곡선 경계)
● ● ●│ │ ● ● ●│
● ● ● │ ╰──────╯
XOR 문제: 선형으로 불가!
Universal Approximation Theorem¶
코드로 이해¶
# 선형만 (의미 없음)
x = W2 @ (W1 @ x) # = (W2 @ W1) @ x
# 비선형 추가 (표현력 증가)
x = W2 @ relu(W1 @ x) # 합성 불가, 더 복잡한 함수
면접 포인트¶
- 선형만: 층 쌓아도 결국 1개 층과 동일
- 비선형: 층마다 표현력 증가
- Universal Approximation: 비선형이 있어야 어떤 함수든 근사
ReLU로 어떻게 곡선 함수를 근사하나?¶
한 줄 답변¶
여러 직선 조각을 결합하여 곡선을 구간별 선형 근사(Piecewise Linear)
직관적 이해¶
하나의 ReLU = 하나의 꺾인 직선
/
/
____/ ← ReLU(x)
여러 ReLU 조합 = 여러 번 꺾인 선 = 곡선 근사
원래 곡선: ⌢
ReLU 근사: /\
/ \
/ \___
구간별 선형 근사¶
예시: sin(x) 근사¶
시각적 증명¶
Universal Approximation¶
코드 예시¶
import numpy as np
import matplotlib.pyplot as plt
# ReLU 조합으로 sin 근사
def piecewise_linear(x, n_pieces=10):
result = np.zeros_like(x)
for i in range(n_pieces):
# 각 ReLU가 다른 지점에서 활성화
result += np.maximum(0, x - i * 0.5) * np.sin(i)
return result
면접 포인트¶
- 구간별 선형 근사: 여러 직선으로 곡선 표현
- 뉴런 많을수록 더 정밀한 근사
- Universal Approximation 이론적 근거
ReLU의 문제점은?¶
한 줄 답변¶
Dead ReLU: 음수 입력에서 항상 0 출력 → 학습 불가
Dead ReLU 문제¶
ReLU: f(x) = max(0, x)
미분: f'(x) = 0 (x ≤ 0)
만약 뉴런 입력이 항상 음수면:
- 출력: 항상 0
- Gradient: 항상 0
- 가중치 업데이트: 0
→ 영원히 죽은 상태!
언제 발생하나?¶
해결책¶
| 방법 | 설명 | 수식 |
|---|---|---|
| Leaky ReLU | 음수에서 작은 기울기 | max(0.01x, x) |
| PReLU | 기울기를 학습 | max(αx, x), α 학습 |
| ELU | 부드러운 음수 처리 | x if x>0, α(e^x-1) else |
| GELU | 가우시안 기반 | x·Φ(x) |
import torch.nn as nn
# Dead ReLU 방지
nn.LeakyReLU(negative_slope=0.01) # 가장 간단
nn.PReLU() # 기울기 학습
nn.ELU(alpha=1.0) # 부드러움
nn.GELU() # Transformer 표준
다른 문제점들¶
1. 출력이 무한대로 갈 수 있음
→ Batch Normalization으로 완화
2. 0에서 미분 불가능
→ 실제로는 큰 문제 아님 (0이나 1로 처리)
3. 출력이 0 중심이 아님
→ Batch Norm으로 해결
면접 포인트¶
- Dead ReLU: 음수 영역에서 Gradient = 0
- 해결: Leaky ReLU, PReLU, ELU
- 원인: 큰 LR, 나쁜 초기화
Bias는 왜 있는걸까?¶
한 줄 답변¶
활성화 함수를 좌우로 이동시켜 더 유연한 함수 근사 가능
Bias의 역할¶
활성화 함수 이동¶
뉴런 단위 해석¶
코드 예시¶
import torch.nn as nn
# Bias 포함 (기본값)
linear = nn.Linear(10, 5, bias=True)
# Bias 제외 (특수 경우)
linear_no_bias = nn.Linear(10, 5, bias=False)
# Batch Normalization 후에는 Bias 불필요
# (BN이 shift 역할을 하므로)
conv = nn.Conv2d(3, 64, 3, bias=False)
bn = nn.BatchNorm2d(64)
Bias 없는 경우¶
1. Batch Normalization 바로 앞
→ BN의 beta가 Bias 역할
2. Residual Connection 특정 구현
→ 생략해도 학습 가능
3. 메모리 최적화 필요 시
→ 파라미터 수 약간 감소
면접 포인트¶
- 함수 이동: 활성화 함수 위치 조절
- 활성화 임계값: 뉴런이 언제 켜지는지
- BN 후에는 불필요: BN이 대체
Gradient Descent에 대해서 쉽게 설명한다면?¶
한 줄 정의¶
손실 함수의 기울기(Gradient) 반대 방향으로 조금씩 이동하여 최솟값을 찾는 방법
비유: 눈 감고 산 내려가기¶
산 정상에서 눈 감고 가장 낮은 곳 찾기:
1. 현재 위치에서 발로 경사 느끼기 (Gradient 계산)
2. 가장 가파른 내리막 방향으로 한 걸음 (업데이트)
3. 반복
결국 골짜기(최솟값)에 도달!
수식¶
시각적 이해¶
학습률의 중요성¶
코드¶
# 직접 구현
for epoch in range(epochs):
loss = model(x)
gradient = compute_gradient(loss)
weights = weights - learning_rate * gradient
# PyTorch
optimizer = torch.optim.SGD(model.parameters(), lr=0.01)
optimizer.zero_grad()
loss.backward() # gradient 계산
optimizer.step() # 업데이트
면접 포인트¶
- 기울기 반대 방향으로 이동
- 학습률: 한 번에 얼마나 이동할지
- 비유: 눈 감고 산 내려가기
왜 꼭 Gradient를 써야 할까?¶
한 줄 답변¶
고차원 공간에서 효율적으로 최솟값 방향을 찾을 수 있는 유일한 방법
대안과 비교¶
| 방법 | 복잡도 | 문제점 |
|---|---|---|
| Grid Search | O(n^d) | 차원의 저주, 불가능 |
| Random Search | - | 수렴 보장 없음 |
| 유전 알고리즘 | - | 느림, 대규모 불가 |
| Gradient | O(n) | 미분 가능해야 함 |
고차원에서의 효율성¶
파라미터가 1억개인 신경망:
Grid Search: 각 파라미터 10개 값 → 10^100000000 조합
→ 우주가 멸망해도 못 끝남
Gradient: 1억 개 방향 중 최적 방향을 한 번에 계산
→ 효율적!
Gradient의 의미¶
Gradient = 각 파라미터가 Loss에 미치는 영향
∂L/∂w₁ = +2 → w₁을 줄이면 Loss 감소
∂L/∂w₂ = -1 → w₂를 늘리면 Loss 감소
→ 모든 파라미터의 최적 이동 방향을 동시에 알 수 있음!
미분 가능성 요구¶
면접 포인트¶
- 고차원에서 유일한 효율적 방법
- 각 파라미터별 최적 방향 동시 계산
- 미분 가능 함수 필요 → 딥러닝 구조 설계 영향
GD 중에 때때로 Loss가 증가하는 이유는?¶
한 줄 답변¶
학습률이 너무 크거나, 미니배치 노이즈, 손실 지형의 복잡성 때문
주요 원인¶
| 원인 | 설명 |
|---|---|
| 학습률 과대 | 최솟값을 뛰어넘음 |
| 미니배치 노이즈 | 배치마다 gradient 다름 |
| Saddle Point | 탈출 과정에서 일시 증가 |
| 정규화 효과 | Dropout, BN의 확률적 특성 |
학습률 문제¶
미니배치 노이즈 (SGD)¶
전체 데이터 Gradient ≠ 미니배치 Gradient
배치 A: gradient = (1, 2)
배치 B: gradient = (-1, 3)
배치 C: gradient = (2, -1)
→ 각 스텝이 약간 다른 방향
→ 지그재그, 일시적 Loss 증가 가능
→ 장기적으로는 평균이 맞음
괜찮은 증가 vs 문제 있는 증가¶
괜찮음:
- 일시적 증가 후 다시 감소
- 노이즈 범위 내 변동
문제:
- 계속 증가
- 발산 (NaN, Inf)
→ Learning Rate 줄여보기
→ Gradient Clipping 적용
해결책¶
# Learning Rate 감소
optimizer = optim.Adam(model.parameters(), lr=1e-4)
# Learning Rate Scheduler
scheduler = optim.lr_scheduler.ReduceLROnPlateau(
optimizer, patience=5
)
# Gradient Clipping
torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)
면접 포인트¶
- LR 과대: 최솟값 뛰어넘음
- SGD 노이즈: 미니배치 차이로 변동
- 일시적 증가는 정상, 지속적 증가는 문제
Back Propagation에 대해서 쉽게 설명 한다면?¶
한 줄 정의¶
Chain Rule을 이용해 출력층에서 입력층으로 역방향으로 기울기를 전파하는 알고리즘
비유: 책임 추적¶
수학적 원리: Chain Rule¶
Forward vs Backward¶
Forward Pass (순방향):
입력 x → [W1] → z1 → [ReLU] → a1 → [W2] → z2 → Loss
Backward Pass (역방향):
dL/dW2 ← dL/dz2 ← dL/da1 ← dL/dz1 ← dL/dW1
단계별 과정¶
1. Forward: 입력 → 출력 → Loss 계산
2. Output Layer: dL/dW_출력 계산
3. Hidden Layer: Chain Rule로 dL/dW 계산 (역순)
4. Update: W = W - lr × dL/dW
코드¶
# PyTorch에서는 자동으로 처리
loss = criterion(output, target)
loss.backward() # 역전파 (모든 gradient 계산)
optimizer.step() # 가중치 업데이트
핵심: 중간 결과 저장¶
면접 포인트¶
- Chain Rule: 합성함수 미분
- 역방향 전파: 출력 → 입력 방향
- Forward에서 중간값 저장 필요
Local Minima 문제에도 불구하고 딥러닝이 잘 되는 이유는?¶
한 줄 답변¶
고차원에서는 Local Minima보다 Saddle Point가 더 많고, 대부분의 Local Minima도 충분히 좋음
고차원의 특성¶
저차원 (2D/3D): Local Minima 많음
고차원 (수백만 차원): Saddle Point가 대부분
이유:
- Local Minima = 모든 방향에서 최솟값
- 고차원에서 "모든" 방향이 동시에 최소? → 확률적으로 매우 낮음
- 대부분은 어떤 방향은 최소, 다른 방향은 최대 (Saddle Point)
Saddle Point vs Local Minima¶
Local Minima도 괜찮은 이유¶
연구 결과:
- 대부분의 Local Minima는 Loss가 비슷
- Global Minima와 성능 차이 미미
- 오히려 Global Minima는 과적합 위험
"어떤 Local Minima든 찾으면 OK"
Saddle Point 탈출 방법¶
| 방법 | 설명 |
|---|---|
| Momentum | 관성으로 평평한 구간 통과 |
| SGD 노이즈 | 랜덤 방향으로 탈출 |
| Adam | 적응적 학습률로 효과적 탈출 |
# Momentum으로 Saddle Point 탈출
optimizer = optim.SGD(params, lr=0.01, momentum=0.9)
# Adam (더 효과적)
optimizer = optim.Adam(params, lr=0.001)
면접 포인트¶
- 고차원: Saddle Point >> Local Minima
- Local Minima도 OK: 대부분 성능 비슷
- Momentum, Adam으로 Saddle Point 탈출
GD가 Local Minima 문제를 피하는 방법은?¶
한 줄 답변¶
Momentum, SGD 노이즈, 학습률 조절 등으로 local minima/saddle point 탈출
주요 방법¶
| 방법 | 원리 |
|---|---|
| Momentum | 관성으로 작은 골짜기 통과 |
| SGD 노이즈 | 미니배치 변동성이 탈출 도움 |
| 학습률 스케줄링 | 큰 LR → 작은 LR |
| Random Restart | 여러 초기값에서 시작 |
| Simulated Annealing | 확률적 탈출 |
Momentum¶
SGD 노이즈¶
Learning Rate 조절¶
# Warm Restart (Cosine Annealing)
scheduler = optim.lr_scheduler.CosineAnnealingWarmRestarts(
optimizer, T_0=10, T_mult=2
)
# 주기적으로 LR을 올려서 탈출 시도
면접 포인트¶
- Momentum: 관성으로 작은 minima 통과
- SGD 노이즈: 미니배치 변동성이 탈출 도움
- LR Scheduling: 큰→작은으로 탐색+수렴
Training 세트와 Test 세트를 분리하는 이유는?¶
한 줄 답변¶
모델의 일반화 성능을 평가하기 위해 (본 적 없는 데이터에서의 성능)
분리하지 않으면?¶
올바른 평가¶
면접 포인트¶
- 일반화 성능 측정 목적
- Train에서 좋아도 Test에서 나쁘면 과적합
- Test는 마지막에 한 번만 사용
Validation 세트가 따로 있는 이유는?¶
한 줄 답변¶
하이퍼파라미터 튜닝과 Early Stopping 등 학습 중 의사결정에 사용
Train/Val/Test 역할¶
| 세트 | 역할 | 사용 시점 |
|---|---|---|
| Train | 모델 학습 | 매 epoch |
| Validation | 하이퍼파라미터 선택, Early Stop | 학습 중 |
| Test | 최종 성능 평가 | 마지막 1회 |
Validation이 필요한 이유¶
사용 예시¶
# Early Stopping
best_val_loss = float('inf')
for epoch in range(epochs):
train_loss = train(model, train_loader)
val_loss = evaluate(model, val_loader)
if val_loss < best_val_loss:
best_val_loss = val_loss
save_model(model)
else:
patience -= 1
if patience == 0:
break # Early Stop
# 최종 평가 (Test는 여기서 한 번만)
test_accuracy = evaluate(model, test_loader)
면접 포인트¶
- Validation: 하이퍼파라미터 튜닝, Early Stop
- Test: 최종 평가만 (1회)
- Test로 튜닝하면 Test에 과적합
Test 세트가 오염되었다는 말의 뜻은?¶
한 줄 답변¶
Test 데이터 정보가 학습 과정에 누출되어 일반화 성능 평가가 불가능해진 상태
오염 사례¶
| 유형 | 예시 |
|---|---|
| 직접 사용 | Test로 하이퍼파라미터 튜닝 |
| 전처리 누수 | 전체 데이터로 스케일러 fit |
| 데이터 누수 | Train/Test에 같은 데이터 |
| 시간 누수 | 미래 데이터로 과거 예측 학습 |
전처리 누수 예시¶
# ❌ 오염 (Data Leakage)
scaler.fit(전체_데이터) # Test 통계 포함!
X_train = scaler.transform(X_train)
X_test = scaler.transform(X_test)
# ✅ 올바름
scaler.fit(X_train만) # Train만!
X_train = scaler.transform(X_train)
X_test = scaler.transform(X_test)
왜 문제인가?¶
면접 포인트¶
- Test 정보가 학습에 누출 = 오염
- 전처리도 Train 데이터만으로
- 오염 시 Test 성능 = 낙관적 추정
오버피팅일 경우 어떻게 대처해야 할까요?¶
한 줄 답변¶
데이터 증강, 정규화(Dropout, L2), Early Stopping, 모델 단순화
오버피팅 진단¶
해결 방법¶
| 방법 | 설명 |
|---|---|
| 데이터 증강 | 학습 데이터 늘리기 |
| Dropout | 랜덤 뉴런 비활성화 |
| L2 정규화 | 가중치 크기 패널티 |
| Batch Norm | 내부 공변량 이동 감소 |
| Early Stopping | Val 성능 기준 조기 종료 |
| 모델 단순화 | 층/뉴런 수 감소 |
코드 예시¶
# Dropout
nn.Dropout(p=0.5)
# L2 정규화 (weight_decay)
optimizer = optim.Adam(params, lr=0.001, weight_decay=1e-4)
# Early Stopping
if val_loss > best_val_loss:
patience -= 1
if patience == 0:
break
# Data Augmentation
transforms.RandomHorizontalFlip()
transforms.RandomRotation(15)
면접 포인트¶
- 데이터 ↑: Data Augmentation
- 모델 복잡도 ↓: Dropout, L2, 단순화
- 학습 조절: Early Stopping
Regularization이란 무엇인가?¶
한 줄 정의¶
모델의 복잡도를 제한하여 과적합을 방지하는 기법
정규화 종류¶
| 종류 | 방법 |
|---|---|
| L1/L2 | 가중치에 패널티 |
| Dropout | 뉴런 랜덤 비활성화 |
| Batch Norm | 분포 정규화 |
| Data Augmentation | 데이터 다양화 |
| Early Stopping | 조기 종료 |
| Weight Decay | L2와 동일 (PyTorch) |
수식¶
면접 포인트¶
- 과적합 방지 목적
- Dropout, L2, BN이 대표적
- Bias-Variance tradeoff 조절
Batch Normalization의 효과는?¶
한 줄 정의¶
미니배치 단위로 평균 0, 분산 1로 정규화하여 학습 안정화
효과¶
| 효과 | 설명 |
|---|---|
| 학습 안정화 | Internal Covariate Shift 감소 |
| 빠른 수렴 | 더 큰 Learning Rate 사용 가능 |
| 정규화 효과 | 약간의 과적합 방지 |
| 초기화 민감도 감소 | 초기값에 덜 민감 |
수식¶
코드¶
면접 포인트¶
- 미니배치 정규화 → 학습 안정화, 수렴 속도 ↑
- γ, β: 학습 가능 (스케일, 시프트)
- Train과 Inference 모드 다름 (running mean/var)
Dropout의 효과는?¶
한 줄 정의¶
학습 시 뉴런을 랜덤하게 비활성화하여 과적합을 방지하는 정규화 기법
동작 원리¶
학습 시 (p=0.5):
[●]─[●]─[●] 랜덤하게 50% 비활성화
[●]─[○]─[●] → [○]: 비활성화 (출력=0)
[●]─[●]─[○] [●]: 활성화
추론 시:
[●]─[●]─[●] 모든 뉴런 사용
[●]─[●]─[●] → 단, 출력을 (1-p)로 스케일
[●]─[●]─[●]
왜 효과적인가?¶
| 효과 | 설명 |
|---|---|
| 앙상블 효과 | 매번 다른 서브네트워크 학습 → 여러 모델 평균 효과 |
| Co-adaptation 방지 | 특정 뉴런 조합에 의존 못함 |
| 노이즈 주입 | 학습 데이터에 노이즈 추가 효과 |
| 희소 표현 | 더 로버스트한 특징 학습 |
앙상블 해석¶
Dropout p=0.5, 뉴런 n개:
→ 가능한 서브네트워크: 2^n 개
각 미니배치마다 다른 서브네트워크 학습
→ 추론 시 모든 서브네트워크 평균!
= 매우 많은 모델의 앙상블
코드¶
import torch.nn as nn
# Dropout 적용
model = nn.Sequential(
nn.Linear(784, 256),
nn.ReLU(),
nn.Dropout(p=0.5), # 50% 비활성화
nn.Linear(256, 128),
nn.ReLU(),
nn.Dropout(p=0.5),
nn.Linear(128, 10)
)
# 학습/추론 모드 전환
model.train() # Dropout 활성화
model.eval() # Dropout 비활성화, 스케일링 적용
주의사항¶
# ⚠️ 추론 시 반드시 eval() 모드!
model.eval() # 중요!
with torch.no_grad():
output = model(x)
# eval() 안 하면:
# - Dropout이 계속 적용됨
# - 결과가 매번 달라짐
# - 성능 저하
위치별 Dropout 비율¶
면접 포인트¶
- 앙상블 효과: 2^n개 서브네트워크 학습
- Co-adaptation 방지: 특정 뉴런 의존 방지
- train/eval 모드 전환 필수
BN 적용해서 학습 이후 실제 사용시에 주의할 점은?¶
한 줄 답변¶
eval() 모드 전환 필수! Training 통계 대신 Running Statistics 사용
Train vs Inference 차이¶
Training (model.train()):
- 현재 미니배치의 mean, var 계산
- Running mean, var 업데이트 (이동 평균)
Inference (model.eval()):
- 미니배치 통계 ❌
- 저장된 Running mean, var 사용 ✓
Running Statistics¶
학습 중 누적:
running_mean = momentum × running_mean + (1-momentum) × batch_mean
running_var = momentum × running_var + (1-momentum) × batch_var
기본 momentum = 0.1
→ 전체 학습 데이터의 통계 근사
주의사항¶
# ⚠️ 반드시 eval() 모드!
model.eval() # 중요!!!
with torch.no_grad():
output = model(x)
# eval() 안 하면?
# - 추론 시 배치마다 다른 결과
# - 배치 크기에 따라 성능 변동
# - 배치 크기 1이면 정규화 불가
문제 상황과 해결¶
| 문제 | 원인 | 해결 |
|---|---|---|
| 배치마다 결과 다름 | eval() 안 함 | model.eval() 추가 |
| 배치 크기 1에서 오류 | Train 모드 BN | eval() 전환 |
| 학습-추론 성능 차이 큼 | 통계 불일치 | 충분한 학습 또는 학습률 조정 |
실제 사용 패턴¶
# 올바른 추론 코드
def predict(model, x):
model.eval() # 1. eval 모드 전환
with torch.no_grad(): # 2. gradient 계산 끄기
output = model(x)
return output
# 학습 재개 시
model.train() # train 모드로 복원
저장/불러오기¶
# 저장 시 running statistics 포함
torch.save(model.state_dict(), 'model.pth')
# 불러오기
model.load_state_dict(torch.load('model.pth'))
model.eval() # 추론 모드!
면접 포인트¶
- eval() 모드: Running mean/var 사용
- train() 모드: 미니배치 통계 사용
- 배치 크기 1에서 Train 모드 BN 불가
SGD, RMSprop, Adam에 대해서 아는대로 설명한다면?¶
한 줄 정의¶
SGD: 기본 | RMSprop: 적응적 학습률 | Adam: Momentum + RMSprop 결합
비교 표¶
| Optimizer | 특징 | 수식 핵심 |
|---|---|---|
| SGD | 기본, 단순 | θ = θ - α∇L |
| SGD+Momentum | 관성 추가 | v = βv + ∇L |
| RMSprop | 학습률 적응 | α / √(v + ε) |
| Adam | Momentum + RMSprop | m/(√v + ε) |
SGD (Stochastic Gradient Descent)¶
SGD + Momentum¶
RMSprop¶
v = β × v + (1-β) × (∇L)² (기울기 제곱의 이동평균)
θ = θ - α / √(v + ε) × ∇L
특징:
- 파라미터별 적응적 학습률
- 자주 업데이트되는 파라미터: LR 감소
- 드물게 업데이트: LR 유지
Adam (Adaptive Moment Estimation)¶
m = β₁ × m + (1-β₁) × ∇L (1st moment, 평균)
v = β₂ × v + (1-β₂) × (∇L)² (2nd moment, 분산)
m̂ = m / (1 - β₁ᵗ) (Bias 보정)
v̂ = v / (1 - β₂ᵗ)
θ = θ - α × m̂ / (√v̂ + ε)
기본값: β₁=0.9, β₂=0.999, ε=1e-8
코드¶
import torch.optim as optim
# SGD
optim.SGD(params, lr=0.01)
# SGD + Momentum
optim.SGD(params, lr=0.01, momentum=0.9)
# RMSprop
optim.RMSprop(params, lr=0.001, alpha=0.99)
# Adam (가장 많이 사용)
optim.Adam(params, lr=0.001, betas=(0.9, 0.999))
# AdamW (L2 정규화 개선)
optim.AdamW(params, lr=0.001, weight_decay=0.01)
선택 가이드¶
일반적 추천: Adam → 빠른 수렴, 튜닝 용이
대규모 모델: AdamW → 정규화 개선
세밀한 수렴: SGD+Momentum → 종종 더 좋은 최종 성능
Transformer: AdamW + warmup
면접 포인트¶
- Adam = Momentum + RMSprop
- 적응적 학습률: RMSprop, Adam
- 실무 기본: Adam/AdamW
SGD에서 Stochastic의 의미는?¶
한 줄 답변¶
미니배치를 랜덤 샘플링하여 gradient를 계산하는 것 (확률적 = Stochastic)
Full Batch vs Stochastic¶
Full Batch GD:
- 전체 데이터로 gradient 계산
- 정확하지만 느림
- 메모리 많이 사용
Stochastic GD (SGD):
- 랜덤 샘플(미니배치)로 gradient 계산
- 노이즈 있지만 빠름
- 메모리 효율적
왜 Stochastic?¶
전체 데이터 N개:
True Gradient = (1/N) × Σ∇L(xᵢ)
미니배치 B개:
SGD Gradient = (1/B) × Σ∇L(xⱼ) (j는 랜덤 샘플)
기대값:
E[SGD Gradient] = True Gradient
→ 평균적으로 맞는 방향!
→ 노이즈가 오히려 정규화 효과
시각적 이해¶
미니배치 크기별 비교¶
| 방식 | 배치 크기 | 특징 |
|---|---|---|
| Full Batch GD | 전체 | 정확, 느림, 메모리 ↑ |
| SGD | 1 | 매우 노이즈, 빠름 |
| Mini-batch SGD | 32~512 | 균형, 실용적 |
면접 포인트¶
- Stochastic = 랜덤 샘플링
- 기대값은 True Gradient와 동일
- 노이즈가 정규화 + Local minima 탈출 효과
미니배치를 작게 할때의 장단점은?¶
한 줄 답변¶
장점: 노이즈로 정규화/탈출 효과 | 단점: 학습 불안정, GPU 효율 저하
배치 크기별 특성¶
| 배치 크기 | Gradient 노이즈 | 일반화 | 학습 속도 | GPU 활용 |
|---|---|---|---|---|
| 작음 (8-32) | 높음 | 좋음 | 느림 | 낮음 |
| 중간 (64-256) | 중간 | 중간 | 중간 | 적정 |
| 큼 (512+) | 낮음 | 나쁠 수 있음 | 빠름 | 높음 |
작은 배치의 장점¶
1. 정규화 효과 (Regularization)
- Gradient 노이즈가 암묵적 정규화
- 과적합 방지
2. Local Minima 탈출
- 노이즈로 작은 골짜기 탈출
- 더 평평한(flat) minima 도달
3. 메모리 절약
- GPU 메모리 적게 사용
- 큰 모델 학습 가능
작은 배치의 단점¶
1. 학습 불안정
- Gradient 분산 큼
- Loss 진동
2. 느린 학습
- iteration당 업데이트 횟수 ↑
- 전체 epoch 시간 ↑
3. GPU 효율 저하
- 병렬 처리 활용 못함
- GPU utilization 낮음
4. BatchNorm 문제
- 통계 추정 불안정
- 배치 크기 8 미만 비추천
큰 배치의 trade-off¶
큰 배치 사용 시:
- 학습률도 비례해서 증가 필요
- Linear Scaling Rule: LR × (batch_size / base_batch)
- Warmup 필수
Sharp Minima 문제:
- 큰 배치 → Sharp minima로 수렴
- 일반화 성능 저하 가능
실무 권장¶
# 일반적인 선택
batch_size = 32 ~ 128
# 메모리 부족 시
batch_size = 8 ~ 16 + Gradient Accumulation
# Gradient Accumulation
accumulation_steps = 4
for i, (x, y) in enumerate(loader):
loss = model(x, y) / accumulation_steps
loss.backward()
if (i + 1) % accumulation_steps == 0:
optimizer.step()
optimizer.zero_grad()
면접 포인트¶
- 작은 배치: 노이즈 ↑ → 정규화 효과, 일반화 좋음
- 큰 배치: 효율적이지만 sharp minima 위험
- BN은 배치 8 이상 권장
모멘텀의 수식을 적어 본다면?¶
한 줄 정의¶
이전 gradient의 지수 이동평균을 현재 업데이트에 반영하는 방식
수식¶
Standard Momentum:
v_t = β × v_{t-1} + ∇L(θ_{t-1})
θ_t = θ_{t-1} - α × v_t
β: momentum 계수 (보통 0.9)
α: 학습률
v: 속도 (velocity)
직관적 이해¶
왜 효과적인가?¶
1. 진동 감소
지그재그 경로 → 매끄러운 경로
Without: With Momentum:
↗↘↗↘ →→→→
↘ →
2. 가속 효과
평평한 구간에서 속도 유지
3. Local minima 탈출
관성으로 작은 골짜기 넘어감
Nesterov Momentum (NAG)¶
Standard: 현재 위치에서 gradient
Nesterov: 미리 가본 위치에서 gradient
v_t = β × v_{t-1} + ∇L(θ_{t-1} - β × v_{t-1})
θ_t = θ_{t-1} - α × v_t
"앞서 보기(lookahead)" → 더 정확한 방향
코드¶
import torch.optim as optim
# Standard Momentum
optimizer = optim.SGD(params, lr=0.01, momentum=0.9)
# Nesterov Momentum
optimizer = optim.SGD(params, lr=0.01, momentum=0.9, nesterov=True)
β값에 따른 효과¶
면접 포인트¶
- v = βv + ∇L, θ = θ - αv
- β = 0.9 표준
- Nesterov: 앞서서 gradient 계산 → 더 정확
하이퍼 파라미터는 무엇인가요?¶
한 줄 정의¶
학습 전에 사람이 설정하는 값. 모델이 학습하지 않음
파라미터 vs 하이퍼파라미터¶
| 구분 | 파라미터 | 하이퍼파라미터 |
|---|---|---|
| 예시 | Weight, Bias | Learning Rate, Batch Size |
| 설정 | 학습으로 결정 | 사람이 설정 |
| 최적화 | Gradient Descent | Grid Search, Random Search |
주요 하이퍼파라미터¶
학습 관련:
- Learning Rate: 가장 중요!
- Batch Size: 32~512
- Epochs: 학습 반복 횟수
- Optimizer 종류: Adam, SGD 등
모델 구조:
- 층 수 (Depth)
- 뉴런 수 (Width)
- Dropout 비율
- Activation Function
정규화:
- Weight Decay (L2)
- Dropout Rate
- Early Stopping patience
튜닝 방법¶
| 방법 | 설명 |
|---|---|
| Grid Search | 모든 조합 시도 |
| Random Search | 랜덤 샘플링 (더 효율적) |
| Bayesian Optimization | 이전 결과로 다음 추정 |
| Learning Rate Finder | LR 자동 탐색 |
# Random Search 예시
from sklearn.model_selection import RandomizedSearchCV
param_dist = {
'lr': [1e-4, 1e-3, 1e-2],
'batch_size': [32, 64, 128],
'dropout': [0.3, 0.5, 0.7]
}
면접 포인트¶
- 학습 전 설정, 학습으로 결정 안 됨
- Learning Rate가 가장 중요
- Grid Search < Random Search (효율성)
Weight Initialization 방법에 대해 말해주세요¶
한 줄 정의¶
학습 시작 전 가중치 초기값 설정 방법. Vanishing/Exploding Gradient 방지
왜 중요한가?¶
나쁜 초기화:
- 모든 가중치 = 0: 모든 뉴런 동일 → 학습 안 됨
- 너무 큰 값: Exploding Gradient
- 너무 작은 값: Vanishing Gradient
좋은 초기화:
- 층마다 활성값 분산 유지
- 깊은 네트워크도 안정적 학습
주요 초기화 방법¶
| 방법 | 수식 | 적합한 활성화 |
|---|---|---|
| Xavier/Glorot | W ~ N(0, 2/(n_in + n_out)) | Sigmoid, Tanh |
| He (Kaiming) | W ~ N(0, 2/n_in) | ReLU, Leaky ReLU |
| LeCun | W ~ N(0, 1/n_in) | SELU |
Xavier (Glorot) Initialization¶
이론:
- 입력과 출력의 분산을 동일하게 유지
- Sigmoid, Tanh에 적합
Var(W) = 2 / (n_in + n_out)
n_in: 입력 뉴런 수
n_out: 출력 뉴런 수
He (Kaiming) Initialization¶
코드¶
import torch.nn as nn
# Xavier (Tanh, Sigmoid)
nn.init.xavier_uniform_(layer.weight)
nn.init.xavier_normal_(layer.weight)
# He/Kaiming (ReLU)
nn.init.kaiming_uniform_(layer.weight, mode='fan_in', nonlinearity='relu')
nn.init.kaiming_normal_(layer.weight, mode='fan_in', nonlinearity='relu')
# PyTorch Linear/Conv는 기본적으로 Kaiming 사용
실무 팁¶
일반적으로:
- PyTorch 기본 초기화 그대로 사용
- ReLU 계열: He (이미 기본값)
- Transformer: 특별한 초기화 (GPT-2 스타일)
문제 발생 시:
- Gradient Exploding → 초기화 분산 줄이기
- Gradient Vanishing → 초기화 분산 늘리기
면접 포인트¶
- Xavier: Sigmoid/Tanh, 분산 = 2/(n_in + n_out)
- He: ReLU, 분산 = 2/n_in
- 목표: 층간 분산 유지 → Gradient 안정화
딥러닝할 때 GPU를 쓰면 좋은 이유는?¶
한 줄 답변¶
병렬 연산에 최적화되어 행렬 연산이 CPU보다 수십~수백 배 빠름
CPU vs GPU¶
| 특성 | CPU | GPU |
|---|---|---|
| 코어 수 | 8~64 | 수천 개 |
| 코어당 성능 | 높음 | 낮음 |
| 용도 | 순차적 복잡 연산 | 단순 병렬 연산 |
| 메모리 대역폭 | 낮음 | 높음 |
왜 GPU가 딥러닝에 적합한가?¶
딥러닝 = 대부분 행렬 연산
행렬 곱: C = A × B
- 각 원소 계산이 독립적
- 동시에 병렬 처리 가능
GPU:
- 수천 코어가 각 원소 동시 계산
- 높은 메모리 대역폭으로 빠른 데이터 전송
시각적 이해¶
CPU (8코어):
████████ ████████ ... (순차 처리)
GPU (1000+ 코어):
████████████████████████████████████████ (동시 처리)
████████████████████████████████████████
████████████████████████████████████████
...
속도 비교 예시¶
GPU 활용 코드¶
import torch
# GPU 사용 설정
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
# 모델과 데이터를 GPU로
model = model.to(device)
x = x.to(device)
y = y.to(device)
# 학습
output = model(x)
loss = criterion(output, y)
주요 GPU 연산¶
병렬 처리에 적합:
- 행렬 곱셈 (Linear layer)
- Convolution
- Element-wise 연산 (ReLU, Softmax)
- Batch 처리
병렬 처리에 부적합:
- 순차적 의존성 있는 연산
- 작은 데이터 (GPU 전송 오버헤드)
면접 포인트¶
- 병렬 연산: 수천 코어, 행렬 연산 최적화
- 메모리 대역폭: 빠른 데이터 처리
- CPU는 순차, GPU는 병렬
학습시 필요한 GPU 메모리는 어떻게 계산하는가?¶
한 줄 답변¶
모델 파라미터 + 활성값 + Gradient + 옵티마이저 상태 합산
메모리 구성 요소¶
| 구성 요소 | 설명 | 크기 |
|---|---|---|
| Model Parameters | Weight, Bias | P × 4 bytes (FP32) |
| Gradients | 각 파라미터의 gradient | P × 4 bytes |
| Optimizer States | Adam: m, v | P × 8 bytes (Adam) |
| Activations | 중간 층 출력 | 가변 (배치 비례) |
대략적 계산¶
FP32 기준:
모델 파라미터: P 개
총 메모리 ≈ P × 4 × (1 + 1 + 2) + Activations
= P × 16 bytes + Activations
예: 1B 파라미터 모델
= 1B × 16 bytes = 16GB + Activations
Activations ≈ Batch × Sequence × Hidden × Layers × 4 bytes
실제 예시¶
GPT-2 Small (125M):
- Parameters: 125M × 4 = 500 MB
- Gradients: 500 MB
- Adam states: 1 GB
- Activations (batch=32): ~3 GB
→ 총 ~5 GB
BERT-Base (110M):
- 유사하게 ~4-6 GB (batch 32)
배치 크기와 메모리¶
배치 크기 ↑ → Activations ↑ → 메모리 ↑
Activations ∝ Batch Size
메모리 부족 시:
1. Batch Size 줄이기
2. Gradient Accumulation
3. Mixed Precision (FP16)
4. Gradient Checkpointing
메모리 최적화 기법¶
# 1. Mixed Precision (메모리 ~50% 절약)
from torch.cuda.amp import autocast, GradScaler
scaler = GradScaler()
with autocast():
output = model(x)
loss = criterion(output, y)
# 2. Gradient Checkpointing (메모리 ↓, 계산 ↑)
from torch.utils.checkpoint import checkpoint
output = checkpoint(layer, input)
# 3. Gradient Accumulation
accumulation_steps = 4
loss = loss / accumulation_steps
확인 방법¶
# 현재 GPU 메모리 사용량
print(torch.cuda.memory_allocated() / 1e9, "GB")
print(torch.cuda.memory_reserved() / 1e9, "GB")
# NVIDIA SMI
# nvidia-smi
면접 포인트¶
- 파라미터 + Gradient + Optimizer + Activations
- Adam은 m, v 저장 → 파라미터의 3배
- Activations은 배치 크기에 비례
뉴럴넷의 가장 큰 단점은 무엇인가?¶
한 줄 답변¶
해석 불가능성(Black Box), 데이터 의존성, 계산 비용
주요 단점¶
| 단점 | 설명 |
|---|---|
| Black Box | 왜 그런 결정을 했는지 설명 어려움 |
| 데이터 필요 | 대량의 레이블된 데이터 필요 |
| 계산 비용 | GPU, 전력, 시간 많이 소모 |
| 과적합 | 데이터 부족 시 쉽게 과적합 |
| 재현성 | 동일 결과 재현 어려움 |
1. Black Box 문제¶
전통 ML: 딥러닝:
"나이 > 60이면 고위험" "이 환자는 고위험"
↓ ↓
이해 가능 왜? 모름
규제 산업(금융, 의료)에서 문제:
- 설명 의무 (XAI 필요)
- 신뢰성 문제
2. 데이터 의존성¶
3. 계산 비용¶
4. 기타 단점¶
불확실성 추정 어려움:
- "확신도"가 실제 확률 아님
- Calibration 필요
적대적 공격 취약:
- 미세한 노이즈로 완전히 다른 예측
- 보안 문제
분포 이동에 약함:
- 학습 분포와 다른 데이터에 취약
- OOD (Out-of-Distribution) 감지 필요
대안/완화 방법¶
| 문제 | 완화 방법 |
|---|---|
| Black Box | XAI (SHAP, LIME, Attention 시각화) |
| 데이터 부족 | Transfer Learning, Data Augmentation |
| 계산 비용 | 경량화, 양자화, Pruning |
| 불확실성 | Bayesian NN, MC Dropout |
면접 포인트¶
- Black Box: 해석/설명 어려움
- 데이터/계산 비용 높음
- 완화: XAI, Transfer Learning, 경량화
One-Shot Learning은 무엇인가?¶
한 줄 정의¶
단 하나의 예시만으로 새로운 클래스를 인식하는 학습 방식
Few-Shot Learning 분류¶
왜 필요한가?¶
현실 상황:
- 희귀 질병: 사례 1건
- 새로운 사기 패턴: 첫 발생
- 얼굴 인식: 등록 사진 1장
기존 딥러닝 한계:
- 새 클래스마다 대량 데이터 수집
- 재학습 필요
- 비현실적
주요 접근법¶
| 방법 | 원리 |
|---|---|
| Siamese Network | 두 입력의 유사도 학습 |
| Prototypical Network | 클래스별 프로토타입 비교 |
| Matching Network | Attention 기반 매칭 |
| MAML | 빠르게 적응하는 초기값 학습 |
Siamese Network¶
┌──────────┐
입력 A ──→│ │──→ 임베딩 A
│ 공유 CNN │ ├→ 거리 계산 → 같은 클래스?
입력 B ──→│ │──→ 임베딩 B
└──────────┘
같은 클래스: 거리 작게
다른 클래스: 거리 크게
→ 새 클래스도 1장으로 임베딩 비교
Prototypical Network¶
Support Set (각 클래스 1개):
클래스 A: [●] → 프로토타입 A (평균)
클래스 B: [■] → 프로토타입 B (평균)
클래스 C: [▲] → 프로토타입 C (평균)
Query:
새 입력 [?] → 어느 프로토타입에 가까운가?
코드 예시 (Siamese)¶
class SiameseNetwork(nn.Module):
def __init__(self):
super().__init__()
self.encoder = nn.Sequential(
nn.Conv2d(1, 64, 3),
nn.ReLU(),
# ...
)
def forward(self, x1, x2):
# 같은 encoder로 임베딩
z1 = self.encoder(x1)
z2 = self.encoder(x2)
# 거리 계산
distance = torch.abs(z1 - z2)
return distance
# 학습: Contrastive Loss
# 같은 클래스면 거리 ↓, 다른 클래스면 거리 ↑
응용 분야¶
1. 얼굴 인식/검증
- 등록 사진 1장으로 본인 확인
2. 서명/지문 검증
- 등록 샘플과 비교
3. 약물 발견
- 새로운 분자 유사성
4. 음성 인식
- 화자 등록 (몇 초 음성)
면접 포인트¶
- 1개 예시로 새 클래스 인식
- Siamese: 유사도 학습
- Prototypical: 클래스 프로토타입 비교
- 응용: 얼굴 인식, 서명 검증