Guide Keras에서 PyTorch로 전환하기
케라스 사용자를 위한 파이토치 완벽 비교 가이드 - 핵심 개념부터 실전 코드까지
🎯 1. 철학의 차이: 고수준 vs 저수준
Keras와 PyTorch는 근본적으로 다른 설계 철학을 가지고 있습니다.
이 차이를 이해하는 것이 전환의 첫 걸음입니다.
🔍 핵심 철학 비교
| 특징 |
Keras |
PyTorch |
| 추상화 수준 |
매우 높음 (High-level API) |
중간~낮음 (Mid-to-low level API) |
| 설계 목표 |
빠른 프로토타이핑, 사용 편의성 |
유연성, 연구 친화성, 세밀한 제어 |
| 코드 스타일 |
선언적 (Declarative) |
명령형 (Imperative) |
| 디버깅 |
제한적 (그래프 내부 접근 어려움) |
쉬움 (Python 디버거 직접 사용) |
| 동적 그래프 |
제한적 지원 |
기본 지원 (Define-by-Run) |
| 학습 곡선 |
매우 완만함 |
중간 (Python과 NumPy 지식 필요) |
| 커뮤니티 |
초보자, 엔지니어 중심 |
연구자, 논문 구현 중심 |
💡 선택 가이드:
Keras를 계속 써야 할 때: 빠른 MVP 개발, 표준적인 아키텍처 사용, 프로덕션 배포가 주 목적
PyTorch로 전환해야 할 때: 커스텀 레이어/손실함수 필요, 최신 논문 구현, 연구 프로젝트, 세밀한 제어 필요
🏗️ 2. 모델 구축 방식 비교
가장 큰 차이점 중 하나는 모델을 정의하는 방법입니다.
Keras의 Sequential/Functional API와 PyTorch의 클래스 기반 접근법을 비교해봅시다.
2.1 간단한 MLP (Multi-Layer Perceptron)
from tensorflow import keras
from tensorflow.keras import layers
# 모델 정의
model = keras.Sequential([
layers.Dense(128, activation='relu',
input_shape=(784,)),
layers.Dropout(0.2),
layers.Dense(64, activation='relu'),
layers.Dropout(0.2),
layers.Dense(10, activation='softmax')
])
# 컴파일
model.compile(
optimizer='adam',
loss='sparse_categorical_crossentropy',
metrics=['accuracy']
)
# 학습
model.fit(x_train, y_train,
epochs=10,
batch_size=32,
validation_split=0.2)
import torch
import torch.nn as nn
import torch.optim as optim
# 모델 정의
class MLP(nn.Module):
def __init__(self):
super(MLP, self).__init__()
self.fc1 = nn.Linear(784, 128)
self.dropout1 = nn.Dropout(0.2)
self.fc2 = nn.Linear(128, 64)
self.dropout2 = nn.Dropout(0.2)
self.fc3 = nn.Linear(64, 10)
def forward(self, x):
x = torch.relu(self.fc1(x))
x = self.dropout1(x)
x = torch.relu(self.fc2(x))
x = self.dropout2(x)
x = self.fc3(x)
return x
model = MLP()
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters())
# 학습 루프는 직접 작성 (다음 섹션 참조)
⚠️ 핵심 차이점:
1. 모델 정의: Keras는 레이어를 리스트로 쌓지만, PyTorch는 클래스를 정의
2. Forward Pass: PyTorch는 forward() 메서드에서 데이터 흐름을 명시적으로 정의
3. 컴파일: Keras는 compile()로 한 번에 설정, PyTorch는 개별 객체 생성
4. 학습: Keras는 fit() 한 줄, PyTorch는 학습 루프를 직접 작성해야 함
2.2 복잡한 모델 (Functional API vs nn.Module)
from tensorflow.keras import Input, Model
from tensorflow.keras.layers import (
Dense, Concatenate
)
# 다중 입력
input1 = Input(shape=(10,))
input2 = Input(shape=(20,))
# 첫 번째 브랜치
x1 = Dense(64, activation='relu')(input1)
x1 = Dense(32, activation='relu')(x1)
# 두 번째 브랜치
x2 = Dense(64, activation='relu')(input2)
x2 = Dense(32, activation='relu')(x2)
# 병합
merged = Concatenate()([x1, x2])
output = Dense(1, activation='sigmoid')(merged)
# 모델 생성
model = Model(
inputs=[input1, input2],
outputs=output
)
model.compile(optimizer='adam',
loss='binary_crossentropy')
import torch.nn as nn
class MultiInputModel(nn.Module):
def __init__(self):
super().__init__()
# 첫 번째 브랜치
self.branch1 = nn.Sequential(
nn.Linear(10, 64),
nn.ReLU(),
nn.Linear(64, 32),
nn.ReLU()
)
# 두 번째 브랜치
self.branch2 = nn.Sequential(
nn.Linear(20, 64),
nn.ReLU(),
nn.Linear(64, 32),
nn.ReLU()
)
# 출력 레이어
self.output = nn.Linear(64, 1)
def forward(self, input1, input2):
x1 = self.branch1(input1)
x2 = self.branch2(input2)
merged = torch.cat([x1, x2], dim=1)
return torch.sigmoid(self.output(merged))
model = MultiInputModel()
criterion = nn.BCELoss()
optimizer = optim.Adam(model.parameters())
🔄 3. 학습 루프: fit() vs 직접 구현
Keras의 가장 큰 장점은 model.fit() 한 줄로 학습이 끝난다는 것입니다.
PyTorch는 학습 루프를 직접 작성해야 하지만, 이것이 오히려 세밀한 제어를 가능하게 합니다.
# 한 줄로 끝!
history = model.fit(
x_train, y_train,
batch_size=32,
epochs=10,
validation_data=(x_val, y_val),
callbacks=[
keras.callbacks.EarlyStopping(
patience=3
),
keras.callbacks.ModelCheckpoint(
'best_model.h5'
)
]
)
# 평가
test_loss, test_acc = model.evaluate(
x_test, y_test
)
# 예측
predictions = model.predict(x_test)
from torch.utils.data import DataLoader, TensorDataset
# 데이터로더 준비
train_dataset = TensorDataset(x_train, y_train)
train_loader = DataLoader(
train_dataset,
batch_size=32,
shuffle=True
)
# 학습 루프
model.train()
for epoch in range(10):
running_loss = 0.0
for inputs, labels in train_loader:
# 그래디언트 초기화
optimizer.zero_grad()
# Forward pass
outputs = model(inputs)
loss = criterion(outputs, labels)
# Backward pass
loss.backward()
# 파라미터 업데이트
optimizer.step()
running_loss += loss.item()
print(f'Epoch {epoch+1}, Loss: {running_loss/len(train_loader):.4f}')
# 평가
model.eval()
with torch.no_grad():
test_outputs = model(x_test)
test_loss = criterion(test_outputs, y_test)
# 예측
predictions = model(x_test)
🔄 학습 루프 상세 비교
| 단계 |
Keras |
PyTorch |
| 배치 처리 |
자동 (fit 내부에서) |
DataLoader로 명시적 구현 |
| Forward Pass |
내부 처리 |
outputs = model(inputs) |
| 손실 계산 |
컴파일 시 지정한 함수 사용 |
loss = criterion(outputs, labels) |
| Backward Pass |
자동 |
loss.backward() 명시적 호출 |
| 그래디언트 초기화 |
자동 |
optimizer.zero_grad() 필수! |
| 파라미터 업데이트 |
자동 |
optimizer.step() |
| 평가 모드 |
자동 전환 |
model.eval() / model.train() |
🟠 PyTorch 학습 루프의 장점:
1. 완전한 제어: 각 단계에서 원하는 작업 수행 가능 (그래디언트 클리핑, 커스텀 로깅 등)
2. 디버깅 용이: 각 단계에 breakpoint 설정 가능
3. 유연성: 특수한 학습 방식(GAN, RL 등) 쉽게 구현
4. 명확성: 내부에서 무슨 일이 일어나는지 정확히 알 수 있음
📊 4. 데이터 처리 비교
데이터를 불러오고 전처리하는 방식도 크게 다릅니다.
from tensorflow.keras.preprocessing.image import (
ImageDataGenerator
)
# 데이터 증강
datagen = ImageDataGenerator(
rotation_range=20,
width_shift_range=0.2,
height_shift_range=0.2,
horizontal_flip=True,
rescale=1./255
)
# 디렉토리에서 데이터 로드
train_generator = datagen.flow_from_directory(
'data/train',
target_size=(224, 224),
batch_size=32,
class_mode='categorical'
)
# 학습
model.fit(
train_generator,
epochs=10,
steps_per_epoch=100
)
from torch.utils.data import Dataset, DataLoader
from torchvision import transforms
from PIL import Image
import os
# 커스텀 데이터셋
class CustomDataset(Dataset):
def __init__(self, root_dir, transform=None):
self.root_dir = root_dir
self.transform = transform
self.images = os.listdir(root_dir)
def __len__(self):
return len(self.images)
def __getitem__(self, idx):
img_path = os.path.join(
self.root_dir,
self.images[idx]
)
image = Image.open(img_path)
label = ... # 레이블 로직
if self.transform:
image = self.transform(image)
return image, label
# 데이터 증강
transform = transforms.Compose([
transforms.RandomRotation(20),
transforms.RandomHorizontalFlip(),
transforms.Resize((224, 224)),
transforms.ToTensor(),
transforms.Normalize(
mean=[0.485, 0.456, 0.406],
std=[0.229, 0.224, 0.225]
)
])
dataset = CustomDataset(
'data/train',
transform=transform
)
train_loader = DataLoader(
dataset,
batch_size=32,
shuffle=True,
num_workers=4
)
4.1 내장 데이터셋 사용
from tensorflow.keras.datasets import mnist
# 데이터 로드
(x_train, y_train), (x_test, y_test) = mnist.load_data()
# 전처리
x_train = x_train.reshape(-1, 784) / 255.0
x_test = x_test.reshape(-1, 784) / 255.0
# 바로 학습
model.fit(x_train, y_train, epochs=5)
from torchvision import datasets, transforms
from torch.utils.data import DataLoader
# 데이터 변환 정의
transform = transforms.Compose([
transforms.ToTensor(),
transforms.Normalize((0.1307,), (0.3081,))
])
# 데이터셋 로드
train_dataset = datasets.MNIST(
'data',
train=True,
download=True,
transform=transform
)
# 데이터로더 생성
train_loader = DataLoader(
train_dataset,
batch_size=64,
shuffle=True
)
# 학습 루프에서 사용
for images, labels in train_loader:
# 학습 코드...
🎨 5. CNN 아키텍처 비교
합성곱 신경망을 구축하는 방법을 비교해봅시다.
from tensorflow.keras import layers, models
model = models.Sequential([
layers.Conv2D(32, (3, 3), activation='relu',
input_shape=(28, 28, 1)),
layers.MaxPooling2D((2, 2)),
layers.Conv2D(64, (3, 3), activation='relu'),
layers.MaxPooling2D((2, 2)),
layers.Conv2D(64, (3, 3), activation='relu'),
layers.Flatten(),
layers.Dense(64, activation='relu'),
layers.Dropout(0.5),
layers.Dense(10, activation='softmax')
])
model.compile(
optimizer='adam',
loss='sparse_categorical_crossentropy',
metrics=['accuracy']
)
model.summary()
import torch.nn as nn
import torch.nn.functional as F
class CNN(nn.Module):
def __init__(self):
super(CNN, self).__init__()
self.conv1 = nn.Conv2d(1, 32, 3)
self.pool = nn.MaxPool2d(2, 2)
self.conv2 = nn.Conv2d(32, 64, 3)
self.conv3 = nn.Conv2d(64, 64, 3)
self.fc1 = nn.Linear(64 * 1 * 1, 64)
self.dropout = nn.Dropout(0.5)
self.fc2 = nn.Linear(64, 10)
def forward(self, x):
x = self.pool(F.relu(self.conv1(x)))
x = self.pool(F.relu(self.conv2(x)))
x = F.relu(self.conv3(x))
x = x.view(-1, 64 * 1 * 1) # Flatten
x = F.relu(self.fc1(x))
x = self.dropout(x)
x = self.fc2(x)
return x
model = CNN()
# 모델 요약 (torchsummary 사용)
from torchsummary import summary
summary(model, (1, 28, 28))
💾 6. 모델 저장 및 불러오기
# 전체 모델 저장 (아키텍처 + 가중치)
model.save('my_model.h5')
model.save('my_model.keras') # 새로운 형식
# 가중치만 저장
model.save_weights('weights.h5')
# 모델 불러오기
from tensorflow.keras.models import load_model
loaded_model = load_model('my_model.h5')
# 가중치만 불러오기
model.load_weights('weights.h5')
# TensorFlow SavedModel 형식
model.save('saved_model/')
loaded = tf.keras.models.load_model('saved_model/')
# 가중치만 저장 (일반적인 방법)
torch.save(model.state_dict(), 'model_weights.pth')
# 전체 모델 저장 (비추천)
torch.save(model, 'entire_model.pth')
# 체크포인트 저장 (최선의 방법)
torch.save({
'epoch': epoch,
'model_state_dict': model.state_dict(),
'optimizer_state_dict': optimizer.state_dict(),
'loss': loss,
}, 'checkpoint.pth')
# 가중치 불러오기
model = CNN() # 모델 클래스 정의 필요!
model.load_state_dict(
torch.load('model_weights.pth')
)
model.eval()
# 체크포인트 불러오기
checkpoint = torch.load('checkpoint.pth')
model.load_state_dict(checkpoint['model_state_dict'])
optimizer.load_state_dict(checkpoint['optimizer_state_dict'])
epoch = checkpoint['epoch']
loss = checkpoint['loss']
⚠️ PyTorch 저장/불러오기 주의사항:
1. PyTorch는 모델 아키텍처를 저장하지 않습니다. 불러올 때 모델 클래스 정의가 필요합니다.
2. state_dict()를 사용하는 것이 권장되며, 전체 모델 저장은 비추천됩니다.
3. GPU에서 학습한 모델을 CPU에서 불러올 때는 map_location='cpu' 파라미터가 필요합니다.
🔧 7. 주요 레이어 및 함수 대응표
📋 레이어 대응표
| 기능 |
Keras |
PyTorch |
| 완전연결층 |
Dense(units) |
nn.Linear(in_features, out_features) |
| 2D 합성곱 |
Conv2D(filters, kernel_size) |
nn.Conv2d(in_channels, out_channels, kernel_size) |
| 맥스 풀링 |
MaxPooling2D(pool_size) |
nn.MaxPool2d(kernel_size) |
| 배치 정규화 |
BatchNormalization() |
nn.BatchNorm2d(num_features) |
| 드롭아웃 |
Dropout(rate) |
nn.Dropout(p) |
| 평탄화 |
Flatten() |
view() 또는 flatten() |
| LSTM |
LSTM(units) |
nn.LSTM(input_size, hidden_size) |
| 임베딩 |
Embedding(input_dim, output_dim) |
nn.Embedding(num_embeddings, embedding_dim) |
🎯 활성화 함수 대응표
| 활성화 함수 |
Keras |
PyTorch |
| ReLU |
activation='relu' |
F.relu() 또는 nn.ReLU() |
| Sigmoid |
activation='sigmoid' |
torch.sigmoid() 또는 nn.Sigmoid() |
| Tanh |
activation='tanh' |
torch.tanh() 또는 nn.Tanh() |
| Softmax |
activation='softmax' |
F.softmax(dim=1) 또는 nn.Softmax(dim=1) |
| LeakyReLU |
LeakyReLU(alpha) |
F.leaky_relu() 또는 nn.LeakyReLU() |
📉 손실 함수 대응표
| 손실 함수 |
Keras |
PyTorch |
| 이진 교차 엔트로피 |
binary_crossentropy |
nn.BCELoss() 또는 nn.BCEWithLogitsLoss() |
| 범주형 교차 엔트로피 |
categorical_crossentropy |
nn.CrossEntropyLoss() (소프트맥스 포함) |
| 희소 범주형 교차 엔트로피 |
sparse_categorical_crossentropy |
nn.CrossEntropyLoss() |
| 평균 제곱 오차 |
mse |
nn.MSELoss() |
| 평균 절대 오차 |
mae |
nn.L1Loss() |
⚙️ 옵티마이저 대응표
| 옵티마이저 |
Keras |
PyTorch |
| Adam |
optimizer='adam' |
optim.Adam(params, lr=0.001) |
| SGD |
optimizer='sgd' |
optim.SGD(params, lr=0.01) |
| RMSprop |
optimizer='rmsprop' |
optim.RMSprop(params) |
| AdaGrad |
optimizer='adagrad' |
optim.Adagrad(params) |
🎮 8. GPU 사용
import tensorflow as tf
# GPU 확인
print("GPU 사용 가능:", tf.config.list_physical_devices('GPU'))
# 자동으로 GPU 사용
# 별도 코드 불필요!
# 특정 GPU 선택
gpus = tf.config.list_physical_devices('GPU')
if gpus:
tf.config.set_visible_devices(gpus[0], 'GPU')
# 메모리 증가 허용
for gpu in gpus:
tf.config.experimental.set_memory_growth(gpu, True)
import torch
# GPU 확인
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
print(f"사용 장치: {device}")
# 모델을 GPU로 이동
model = model.to(device)
# 학습 루프에서 데이터도 GPU로 이동
for inputs, labels in train_loader:
inputs = inputs.to(device)
labels = labels.to(device)
outputs = model(inputs)
loss = criterion(outputs, labels)
# ...
# 특정 GPU 선택
device = torch.device('cuda:0') # 첫 번째 GPU
model.to(device)
# 다중 GPU
if torch.cuda.device_count() > 1:
model = nn.DataParallel(model)
model.to(device)
⚠️ PyTorch GPU 사용 시 주의사항:
1. 모델, 데이터 모두 .to(device)로 명시적으로 GPU로 이동해야 합니다.
2. GPU와 CPU 간 텐서를 섞어 사용하면 에러가 발생합니다.
3. 예측 결과를 NumPy로 변환할 때: output.cpu().numpy() 필요
🔍 9. 디버깅 및 검증
# 모델 요약
model.summary()
# 레이어별 출력 확인
from tensorflow.keras import Model
layer_outputs = [layer.output for layer in model.layers]
activation_model = Model(
inputs=model.input,
outputs=layer_outputs
)
activations = activation_model.predict(x_test[0:1])
# 가중치 확인
for layer in model.layers:
weights = layer.get_weights()
print(f"{layer.name}: {len(weights)} 가중치")
# 그래디언트 확인 (복잡함)
import tensorflow as tf
with tf.GradientTape() as tape:
predictions = model(x_train[:1])
loss = loss_fn(y_train[:1], predictions)
grads = tape.gradient(loss, model.trainable_weights)
# 모델 요약 (torchsummary 사용)
from torchsummary import summary
summary(model, input_size=(1, 28, 28))
# 중간 레이어 출력 확인 (Hook 사용)
activation = {}
def get_activation(name):
def hook(model, input, output):
activation[name] = output.detach()
return hook
model.fc1.register_forward_hook(get_activation('fc1'))
output = model(x)
print(activation['fc1'])
# 가중치 확인
for name, param in model.named_parameters():
print(f"{name}: {param.shape}")
# 그래디언트 확인 (매우 쉬움!)
output = model(x)
loss = criterion(output, y)
loss.backward()
for name, param in model.named_parameters():
if param.grad is not None:
print(f"{name} gradient: {param.grad.norm()}")
🟠 PyTorch 디버깅의 강력함:
1. Python 디버거 직접 사용: pdb, VSCode 브레이크포인트 등
2. 그래디언트 접근 용이: 각 파라미터의 .grad로 즉시 확인
3. 동적 그래프: forward pass 중 print문으로 디버깅 가능
4. 명시적 제어: 각 단계를 직접 볼 수 있어 문제 파악이 쉬움
📝 10. 실전 예제: 전체 프로젝트 비교
간단한 이미지 분류 프로젝트를 두 프레임워크로 구현해봅시다.
시나리오: CIFAR-10 이미지 분류
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers
# 1. 데이터 로드
(x_train, y_train), (x_test, y_test) = keras.datasets.cifar10.load_data()
x_train, x_test = x_train / 255.0, x_test / 255.0
# 2. 모델 정의
model = keras.Sequential([
layers.Conv2D(32, 3, activation='relu', input_shape=(32, 32, 3)),
layers.MaxPooling2D(),
layers.Conv2D(64, 3, activation='relu'),
layers.MaxPooling2D(),
layers.Conv2D(64, 3, activation='relu'),
layers.Flatten(),
layers.Dense(64, activation='relu'),
layers.Dense(10)
])
# 3. 컴파일
model.compile(
optimizer='adam',
loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
metrics=['accuracy']
)
# 4. 학습
history = model.fit(
x_train, y_train,
epochs=10,
validation_data=(x_test, y_test)
)
# 5. 평가
test_loss, test_acc = model.evaluate(x_test, y_test)
print(f'Test accuracy: {test_acc}')
# 6. 예측
predictions = model.predict(x_test[:5])
# 총 코드 줄 수: ~30줄
import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import DataLoader
from torchvision import datasets, transforms
# 1. 데이터 로드
transform = transforms.Compose([
transforms.ToTensor(),
transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))
])
trainset = datasets.CIFAR10('data', train=True, download=True, transform=transform)
testset = datasets.CIFAR10('data', train=False, download=True, transform=transform)
trainloader = DataLoader(trainset, batch_size=64, shuffle=True)
testloader = DataLoader(testset, batch_size=64, shuffle=False)
# 2. 모델 정의
class CNN(nn.Module):
def __init__(self):
super().__init__()
self.conv1 = nn.Conv2d(3, 32, 3)
self.pool = nn.MaxPool2d(2, 2)
self.conv2 = nn.Conv2d(32, 64, 3)
self.conv3 = nn.Conv2d(64, 64, 3)
self.fc1 = nn.Linear(64 * 4 * 4, 64)
self.fc2 = nn.Linear(64, 10)
def forward(self, x):
x = self.pool(torch.relu(self.conv1(x)))
x = self.pool(torch.relu(self.conv2(x)))
x = torch.relu(self.conv3(x))
x = x.view(-1, 64 * 4 * 4)
x = torch.relu(self.fc1(x))
x = self.fc2(x)
return x
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
model = CNN().to(device)
# 3. 손실함수 & 옵티마이저
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters())
# 4. 학습
for epoch in range(10):
model.train()
running_loss = 0.0
for inputs, labels in trainloader:
inputs, labels = inputs.to(device), labels.to(device)
optimizer.zero_grad()
outputs = model(inputs)
loss = criterion(outputs, labels)
loss.backward()
optimizer.step()
running_loss += loss.item()
print(f'Epoch {epoch+1}, Loss: {running_loss/len(trainloader):.3f}')
# 5. 평가
model.eval()
correct = 0
total = 0
with torch.no_grad():
for inputs, labels in testloader:
inputs, labels = inputs.to(device), labels.to(device)
outputs = model(inputs)
_, predicted = torch.max(outputs.data, 1)
total += labels.size(0)
correct += (predicted == labels).sum().item()
print(f'Test accuracy: {100 * correct / total}%')
# 총 코드 줄 수: ~70줄
🚀 11. 전환 전략 및 팁
🎯 단계별 전환 가이드
-
1단계: PyTorch 기초 익히기
- 텐서 연산 (
torch.Tensor와 NumPy 비교)
nn.Module 클래스 구조 이해
forward() 메서드 작성법
-
2단계: 간단한 모델 재구현
- Keras로 작성한 기존 프로젝트를 PyTorch로 변환
- Linear regression, Logistic regression부터 시작
- 결과가 동일한지 검증
-
3단계: 학습 루프 마스터
- 수동 학습 루프 작성 연습
- 검증 루프, 조기 종료 구현
- 학습률 스케줄러 사용법 익히기
-
4단계: 고급 기능 습득
- 커스텀 레이어, 손실함수 작성
- 데이터 증강 및 커스텀 Dataset
- 모델 체크포인팅, 로깅
-
5단계: 실전 프로젝트
- 전이학습, Fine-tuning
- 최신 논문 구현
- PyTorch Lightning 등 고수준 라이브러리 활용
💡 유용한 PyTorch 생태계 도구:
1. PyTorch Lightning: Keras처럼 사용할 수 있는 고수준 래퍼
2. torchvision: 이미지 관련 유틸리티, 사전학습 모델
3. torchtext: NLP 데이터 처리
4. torchsummary: Keras의 model.summary()와 동일한 기능
5. tensorboard: PyTorch도 TensorBoard 사용 가능
6. ONNX: PyTorch 모델을 다른 프레임워크로 변환
⚡ 12. 흔한 실수 및 해결법
❌ 자주 하는 실수들:
1. optimizer.zero_grad() 잊어먹기
❌ 잘못된 코드:
loss = criterion(outputs, labels)
loss.backward()
optimizer.step() # 그래디언트가 누적됨!
✅ 올바른 코드:
optimizer.zero_grad() # 필수!
loss = criterion(outputs, labels)
loss.backward()
optimizer.step()
2. 평가 시 model.eval() 안 쓰기
❌ 문제: Dropout과 BatchNorm이 학습 모드로 동작
✅ 해결:
model.eval()
with torch.no_grad():
# 평가 코드
3. GPU-CPU 텐서 혼용
❌ 에러 발생:
model = model.to('cuda')
outputs = model(inputs) # inputs가 CPU에 있으면 에러!
✅ 해결:
inputs = inputs.to(device)
labels = labels.to(device)
outputs = model(inputs)
4. CrossEntropyLoss에 Softmax 중복 적용
❌ 잘못: PyTorch의
nn.CrossEntropyLoss()는 내부에 Softmax 포함
✅ 해결: forward에서 Softmax 제거, logits 그대로 반환
def forward(self, x):
x = self.fc(x)
# return F.softmax(x) ❌
return x # ✅
5. 배치 차원 무시
Keras는 자동 처리하지만 PyTorch는 명시적으로 다뤄야 함
✅ view() 사용 시: x.view(-1, ...)에서 -1은 배치 크기
🎓 13. 학습 자료 추천
📚 공식 문서 및 튜토리얼
🎥 동영상 강의
- PyTorch for Deep Learning (freeCodeCamp)
- Deep Learning with PyTorch (Udacity)
- FastAI 강의: PyTorch 기반의 실용적인 딥러닝
📖 책
- "Deep Learning with PyTorch" - Eli Stevens 외
- "Programming PyTorch for Deep Learning" - Ian Pointer
- "PyTorch Pocket Reference" - Joe Papa
🎯 핵심 요약
🔑 반드시 기억할 핵심 차이점
| 영역 |
Keras 방식 |
PyTorch 방식 |
| 모델 정의 |
Sequential/Functional API |
nn.Module 클래스 상속 |
| 학습 |
model.fit() 한 줄 |
수동 학습 루프 작성 |
| 그래디언트 |
자동 처리 |
zero_grad(), backward(), step() |
| GPU 사용 |
자동 |
.to(device) 명시적 호출 |
| 디버깅 |
제한적 |
Python 디버거 직접 사용 가능 |
✨ PyTorch를 선택해야 하는 이유
- 연구 친화적: 최신 논문의 대부분이 PyTorch로 구현됨
- 완전한 제어: 모든 단계를 세밀하게 조작 가능
- Pythonic: Python 코드처럼 직관적이고 디버깅 쉬움
- 동적 그래프: RNN, GAN 등 동적 구조에 유리
- 커뮤니티: 학계와 연구 커뮤니티의 표준
🎓 학습 로드맵
- PyTorch 기본 문법 (텐서, autograd) - 1주
- 간단한 모델 구현 (MLP, CNN) - 1주
- 학습 루프 패턴 습득 - 1주
- 고급 기능 (커스텀 레이어, Dataset) - 2주
- 실전 프로젝트 구현 - 지속적