1. 라이브러리 및 데이터셋 준비
import torch
from torch import nn
from torch.utils.data import DataLoader
from torchvision import datasets
from torchvision.transforms import ToTensor, Lambda, Compose
import matplotlib.pyplot as plt
import numpy as np
- torch, torch.nn: Pyorch의 핵심 라이브러리와 신경망 계층 구현을 위한 모듈.
- torch.utils.data.DataLoader: Dataset을 반복(iteration) 가능한 형태로 감싸는 클래스.
- torchvision.datasets: 유명한 공개 데이터셋(MNIST, CIFAR 등)을 쉽게 로드할 수 있는 모듈.
- torchvision.transforms: 이미지 전처리를 위한 기능(ToTensor, Normalize 등).
- matplotlib.pyplot: 시각화 라이브러리.
- numpy: 수치 계산 라이브러리.
# 공개 데이터셋에서 학습 데이터를 내려받음
training_data = datasets.MNIST(
root = 'data',
train=True,
download=True,
transform=ToTensor(),
)
test_data = datasets.MNIST(
root = 'data',
train=False,
download=True,
transform=ToTensor(),
)
- MNIST: 28×28 크기의 흑백 손글씨 숫자(0~9) 이미지.
- root='data': 데이터 저장 경로.
- train=True/False: train용/테스트용 데이터 구분.
- download=True: 해당 위치에 데이터 없으면 자동 다운로드.
- transform=ToTensor(): PIL Image나 ndarray를 PyTorch Tensor로 변환 + [0,1] 범위로 스케일링.
batch_size = 64
train_dataloader = DataLoader(training_data, batch_size=batch_size)
test_dataloader = DataLoader(test_data, batch_size=batch_size)
for X, y in test_dataloader:
print('Shape of X [N, C, H, W] : ', X.shape)
print('Shape of y', y.shape, y.dtype)
break
- DataLoader(…):
- batch_size=64: 한 번에 64개 샘플을 묶어 반환.
- 기본적으로 shuffle=False (학습용이면 보통 shuffle=True).
- for X, y in test_dataloader:: 첫 미니배치 데이터를 받아와 출력.
- X.shape → [N, C, H, W] (배치 크기, 채널, 높이, 너비).
- MNIST는 흑백 → C=1, 따라서 [64, 1, 28, 28].
- y.shape → [N] (정답 라벨, 크기 64).
len(training_data)
- len(training_data)는 MNIST 훈련 데이터셋의 전체 샘플 수(6만 장).
내용 정리
- root: 데이터 저장 위치
- train: 학습용인지 테스트용인지
- download: 미존재 시 자동 다운로드
- transform: 이미지 전처리 (여기서는 ToTensor).
2. GPU 사용 설정
device = 'cuda' if torch.cuda.is_available() else 'cpu'
print(f'Using {device} device')
- torch.cuda.is_available()로 GPU 사용 가능 여부 체크.
- Colab에서 GPU 사용하려면 “런타임 유형 변경 → GPU”로 설정.
3. 모델 클래스 정의
class NeuralNetwork(nn.Module):
def __init__(self):
super(NeuralNetwork, self).__init__()
self.flatten = nn.Flatten()
self.linear_relu_stack = nn.Sequential(
nn.Linear(28*28, 128),
nn.ReLU(),
nn.Dropout(0.2),
nn.Linear(128, 10)
)
def forward(self, x):
x = self.flatten(x) # (N,1,28,28) → (N, 784)
logits = self.linear_relu_stack(x)
return logits
- nn.Module을 상속받아 사용자 정의 모델 구현.
- __init__:
- nn.Flatten(): 입력 [N,1,28,28]을 [N,784]로 변경.
- nn.Sequential(...): 순차적 레이어 묶음.
- nn.Linear(784, 128) + nn.ReLU() + nn.Dropout(0.2) + nn.Linear(128,10)
- forward(self, x): 모델 순전파 로직.
- flatten → linear_relu_stack
model = NeuralNetwork().to(device)
print(model)
model.__dict__
- NeuralNetwork() 인스턴스 생성 후 .to(device) → GPU로 이동.
- print(model) → 모델 구조(Flatten → Linear → ReLU → Dropout → Linear) 확인.
- model.__dict__로 내부 속성 확인 가능.
Flatten, Dropout, Linear, ReLU
- Flatten: 다차원 텐서를 1차원으로 펼침.
- Linear(784, 128): 완전연결층, 입력 784 → 출력 128.
- ReLU: 활성화 함수, 음수 → 0.
- Dropout(0.2): 학습 시 20% 확률로 뉴런 비활성화하여 오버피팅 방지.
- Linear(128, 10): 최종 분류 10개 클래스.
4. Loss 함수 & Optimizer 설정
loss_fn = nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(model.parameters(), lr=1e-3)
- CrossEntropyLoss: 소프트맥스 + 로그우도 손실 결합.
- 라벨이 정수(0~9)인 경우 직접 사용 가능.
- Adam 옵티마이저
- model.parameters()는 모델의 학습 가능 파라미터(m, b, weights 등).
- lr=1e-3: 학습률.
5. 훈련 함수(train)
def train(dataloader, model, loss_fn, optimizer):
size = len(dataloader.dataset)
for batch, (X, y) in enumerate(dataloader):
X, y = X.to(device), y.to(device)
pred = model(X)
print("pred[0]:", pred[0])
print("y[0]:", y[0])
loss = loss_fn(pred, y)
optimizer.zero_grad()
loss.backward()
optimizer.step()
if batch % 100 == 0:
loss_val = loss.item()
current = batch * len(X)
print(f"loss: {loss_val:>7f} [{current:>5d}/{size:>5d}]")
- size = len(dataloader.dataset) → 총 샘플 수 (ex. 60000).
- for batch, (X, y) in enumerate(dataloader): → 미니배치 반복.
- X, y = X.to(device), y.to(device) → GPU로 전송.
- pred = model(X) → forward 계산 (로짓).
- loss = loss_fn(pred, y) → CrossEntropyLoss 계산.
- 역전파
- optimizer.zero_grad()로 기울기 초기화
- loss.backward()로 기울기 계산
- optimizer.step()로 파라미터 업데이트
- if batch % 100 == 0: ... → 특정 배치마다 학습 상황 출력.
- pred[0]는 첫 샘플에 대한 로짓. 예: tensor([-18.9012, -13.6001, ...], grad_fn=...)
- y[0]는 첫 샘플의 실제 라벨, 예: tensor(5, ...)
6. 테스트 함수(test)
def test(dataloader, model, loss_fn):
size = len(dataloader.dataset)
num_batches = len(dataloader)
model.eval()
test_loss, correct = 0, 0
with torch.no_grad():
for X, y in dataloader:
X, y = X.to(device), y.to(device)
pred = model(X)
test_loss += loss_fn(pred, y).item()
correct += (pred.argmax(1) == y).type(torch.float).sum().item()
test_loss /= num_batches
correct /= size
print(f"Test Error: \n Accuracy: {(100*correct):>0.1f}%, Avg loss: {test_loss:>8f} \n")
- model.eval(): 평가 모드(배치정규화, 드롭아웃 등 학습 비활성화).
- with torch.no_grad(): → 기울기 계산 안 함.
- 누적 손실: test_loss += loss_fn(pred, y).item()
- 정확도: (pred.argmax(1) == y) → True(1)/False(0).
- .sum().item()로 맞춘 개수 계산, correct /= size로 비율.
- 최종 평균 손실, 정확도 출력.
7. 실제 학습 루프
epochs = 10
for t in range(epochs):
print(f'Epoch {t+1}----------------------------')
train(train_dataloader, model, loss_fn, optimizer)
test(test_dataloader, model, loss_fn)
Epoch 10\----------------------------
loss: 0.010015 [ 0/60000]
loss: 0.010355 [ 6400/60000]
loss: 0.016117 [12800/60000]
loss: 0.019655 [19200/60000]
loss: 0.011582 [25600/60000]
loss: 0.011333 [32000/60000]
loss: 0.003123 [38400/60000]
loss: 0.018862 [44800/60000]
loss: 0.028464 [51200/60000]
loss: 0.009460 [57600/60000]
10000
Test Error:
Accuracy: 97.8%, Avg loss: 0.074867
- 총 10 에폭 반복.
- train(...): 한 에폭 동안 모든 train_dataloader 배치 처리.
- test(...): 한 에폭 종료 후 테스트 데이터셋 성능 평가.
중간 디버그용 train 함수
# train 함수 중간 디버깅
def train(dataloader, model, loss_fn, optimizer):
size = len(dataloader.dataset)
for batch, (X, y) in enumerate(dataloader):
X, y = X.to(device), y.to(device)
pred = model(X)
print("pred[0] : ", pred[0])
print("pred.argmax(1)[0] : ", pred.argmax(1)[0])
print("y[0] : ", y[0])
loss = loss_fn(pred, y)
break
optimizer.zero_grad()
loss.backward()
optimizer.step()
...
pred[0] : tensor([-18.9012, -13.6001, -4.5011, 8.7944, -32.9612, 15.3064, -31.3961,
-10.8590, -11.8794, -11.1282], device='cuda:0',
grad_fn=<SelectBackward0>)
pred.argmax(1)[0] : tensor(5, device='cuda:0')
y[0] : tensor(5, device='cuda:0')
- 이 코드는 중간에 break가 있어 첫 배치만 확인. 이후 훈련 중단.
- print(...)로 첫 번째 배치 예측값과 정답을 확인하는 디버그 목적.
8. 손글씨 이미지 예측(three.png)
(예시 코드, MNIST 학습 후 사용자 이미지 입력 테스트)
# ... 생략: 이미지 읽기, 흑백 변환, 배경 반전, 0~1 스케일링
image = torch.as_tensor(image).to(device).reshape(1, 1, 28, 28)
model.eval()
predict = model(image)
print('model이 예측한 값은 {}입니다.'.format(predict.argmax(1).item()))
- model.eval(): 평가 모드.
- image.shape = [1,1,28,28].
- predict = model(image) → 로짓.
- predict.argmax(1).item() → 예측된 클래스(0~9).
9. FashionMNIST 예시
training_data = datasets.FashionMNIST(
root="data", train=True, download=True, transform=ToTensor()
)
test_data = datasets.FashionMNIST(
root="data", train=False, download=True, transform=ToTensor()
)
labels_map = {
0: "T-Shirt", 1: "Trouser", 2: "Pullover", 3: "Dress",
4: "Coat", 5: "Sandal", 6: "Shirt", 7: "Sneaker",
8: "Bag", 9: "Ankle Boot",
}
- FashionMNIST: 옷, 신발 등 10개 카테고리의 흑백 이미지(28x28).
- labels_map로 인덱스 → 문자 라벨 대응.
figure = plt.figure(figsize=(8, 8))
cols, rows = 3, 3
for i in range(1, cols*rows + 1):
sample_idx = torch.randint(len(training_data), size=(1,)).item()
img, label = training_data[sample_idx]
figure.add_subplot(rows, cols, i)
plt.title(labels_map[label])
plt.axis("off")
plt.imshow(img.squeeze(), cmap='gray')
plt.show()
- 무작위 샘플 9개 표시 (3×3).
- img.squeeze()로 (1,28,28)을 (28,28)으로 변형.
- labels_map[label]으로 라벨 이름 추출.
DataLoader
train_dataloader = DataLoader(training_data, batch_size=64, shuffle=True)
test_dataloader = DataLoader(test_data, batch_size=64, shuffle=False)
- FashionMNIST도 train/test를 batch=64로 묶음.
- shuffle=True: 학습 시 무작위 배치.
train_features, train_labels = next(iter(train_dataloader))
print(f'Feature batch shape: {train_features.size()}')
print(f'Labels batch shape: {train_labels.size()}')
img = train_features[0].squeeze()
label = train_labels[0]
plt.imshow(img, cmap='gray')
plt.show()
print(f'Label: {label}')
print(labels_map[int(label)])
print(label.numpy())
- next(iter(train_dataloader)): 첫 배치(이미지, 라벨)만 가져옴.
- img.shape → [28,28], label은 정수 라벨.
- 시각화: plt.imshow(img, cmap='gray').
Iteration 예시
list1 = [1,2,3,4,5]
iterator = iter(list1)
a = next(iterator) # 1
a = next(iterator) # 2
a = next(iterator) # 3
a1 = next(iterator) # 1
- next(iter(...))가 호출될 때마다 다음 요소 반환.
- DataLoader도 유사하게 작동 → 각 배치 순회.
정리
- 데이터셋 로드: datasets.MNIST(...), datasets.FashionMNIST(...)
- DataLoader로 배치 구성 → train_dataloader, test_dataloader
- 모델 정의: class NeuralNetwork(nn.Module)
- Flatten → Linear → ReLU → Dropout → Linear
- 손실 / 최적화 함수 설정: CrossEntropyLoss, Adam
- train(...) 함수:
- 각 배치에 대해 forward → loss 계산 → backward → optimizer.step
- 100단계마다 중간 손실 출력
- test(...) 함수:
- model.eval() + no_grad() → 평가 모드, 손실/정확도 측정
- 학습 루프(for t in range(epochs):)
- train(...) 후 test(...)
- 추가 예시:
- 직접 업로드한 이미지(three.png) → 전처리(흑백, 28x28, 반전) → model(image) 예측
- FashionMNIST로 데이터셋 바꿔 테스트
1. Python에서 super()란 무엇인가?
- super()
- 부모(슈퍼) 클래스의 임시 객체를 반환하여, 부모 클래스 메서드에 접근할 수 있게 함.
- 예: super().__init__() → 부모 클래스의 생성자 실행.
- super().some_method() → 부모 클래스의 some_method 호출.
- super(SubClass, self) vs super()
- 파이썬 3에서는 대부분 super()로 간단히 사용.
- 내부적으로 super(SubClass, self)는 SubClass의 MRO(메서드 해석 순서)에 따라 상위 클래스를 탐색.
- 다중 상속이나 할머니 클래스가 있는 경우, super(명시적 하위클래스, self)와 super()의 탐색 범위가 달라질 수 있음.
- 상속 예시
- Square는 Rectangle을 상속받고, super().__init__(length, length)로 부모 클래스의 초기화 로직 실행.
- area(), perimeter() 등을 그대로 상속 및 재사용 가능.
class Rectangle:
def __init__(self, length, width):
self.length = length
self.width = width
def area(self):
return self.length * self.width
class Square(Rectangle):
def __init__(self, length):
super().__init__(length, length)
- 부모의 부모(할머니) 상속 예시
- super(Square, self).area() → Square의 직접 부모(Rectangle) 탐색
- super().area() → Cube 기준으로 MRO를 거쳐 Square의 area 탐색.
2. Numpy에서 sum 함수와 axis 이해
Numpy에서 np.sum 함수의 axis 이해
Numpy의 sum은 유용한 함수입니다. 그러나 처음 sum 함수를 사용할 때 axis 파라미터가 무엇을 의미하는지 혼동되는 것이 사살입니다. axis의 의미를 정리합니다.
taewan.kim
- 배열(텐서)의 차원(Dimension)
- 1차원(벡터), 2차원(행렬), 3차원(텐서), …
- 예: (4,2,4) 형태의 3차원 배열 → Row=4, Column=2, Depth=4.
- v.sum(axis=None)
- 모든 원소를 단순 합산 → 스칼라 반환.
- axis=0
- 가장 바깥 차원(ROW)을 합산해 줄임.
- 결과 shape에서 row가 사라지므로 (2,4)로 감소.
- axis=1
- 두 번째 축(컬럼)을 합산. (4,4) 형태 → row별 column들을 하나로 합침.
- axis=2
- 세 번째 축(depth)을 합산. (4,2) 형태 → 각 row·column마다 depth의 합.
- 일반적 개념
- axis=n은 n번째 축(0부터 시작)을 기준으로, 그 축을 합산 대상으로 삼아 제거.
- 다차원 배열을 다룰 때 axis 설정은 “어떤 차원을 합쳐서 없앨 것인가?”라고 보면 됨.
3. TensorFlow vs. PyTorch 비교
- 모델 정의 방식
TensorFlow(Keras):
class MyModel(tf.keras.Model):
def __init__(self):
super().__init__()
# layers 정의
def call(self, x):
# forward pass
return ...
model = MyModel()
PyTorch:
class NeuralNetwork(nn.Module):
def __init__(self):
super().__init__()
# layers 정의
def forward(self, x):
return ...
model = NeuralNetwork().to(device)
- 차이점:
- Keras: 자동으로 GPU 설정(Colab 등).
- PyTorch: model.to(device)로 명시적 GPU 할당.
- 컴파일
- Keras: model.compile(optimizer, loss, metrics=...)
- PyTorch:
- loss_fn = nn.CrossEntropyLoss()
- optimizer = torch.optim.Adam(model.parameters(), lr=...)
- 학습(fit) vs. custom training loop
- Keras: model.fit(x_train, y_train, epochs=...)로 간단 호출.
- PyTorch: 수동 반복문으로 batch 처리, forward, backward, optimizer step을 명시.
# Pytorch
def train(dataloader, model, loss_fn, optimizer):
for X,y in dataloader:
pred = model(X)
loss = loss_fn(pred, y)
optimizer.zero_grad()
loss.backward()
optimizer.step()
- 평가
- Keras: model.evaluate(x_test, y_test)
- PyTorch: 결과적으로 PyTorch는 더 자유도 높고 세부 제어 가능하지만, 직접 코드 작성 많음.
model.eval()
with torch.no_grad():
# forward & calculate accuracy
- 정리
- TensorFlow는 고수준 API(fit, compile 등) 제공해 편의성 높음.
- PyTorch는 직관적이고 명시적(forward, backward)하며 연구/디버깅에 유리.
최종 요약
- super(): 파이썬에서 상속 시, 부모 클래스 메서드/생성자 등을 호출하기 위한 임시 객체. 다중 상속 시 super(하위클래스, self) 형태로 부모/조상 클래스를 유연히 찾을 수 있음.
- Numpy sum(axis=...): 다차원 배열에서 특정 축(차원)을 합산 기준으로 삼아 제거. 예) axis=0은 row 합산, axis=1은 column 합산 등.
- TensorFlow vs. PyTorch:
- 모델 정의, 컴파일, 학습 방식이 조금 다름.
- Keras는 fit(), compile() 중심의 간단 API; PyTorch는 직접 training loop를 작성(자유도 높음).
- 두 프레임워크 모두 Layer, Optimizer, Loss 개념은 유사하나, API 사용 패턴에서 차이 발생.