
MNIST 데이터 실습
먼저 데이터를 MNIST 데이터를 가져와보자.
MNIST 데이터는 손글씨(0~9)의 흑백 이미지로 구성된 데이터이다.
데이터를 가져올 주소
https://github.com/pytorch/tutorials/raw/main/_static/
file name : mnist.pkl.gz
가져온 데이터는 python pickle 파일 이기 때문에 이를 풀어 우리가 원하는 Python 데이터로 바꿔보자.
# 1. Data Load
import pickle
import gzip
PATH = input("파일 경로 입력:")
FILENAME = "/mnist.pkl.gz"
with gzip.open((PATH+FILENAME), "rb") as f:
((x_train, y_train), (x_valid, y_valid), _) = pickle.load(f, encoding="latin-1")
import numpy as np
print("X_train shape :",x_train.shape)
print("Y_train shape :",y_train.shape)
print("X_valid shape :",x_valid.shape)
print("Y_valid shape :",y_valid.shape)
# 이미지 데이터이기 때문에 이미지 형태 (50000, 28, 28)
# 28 * 28 크기의 데이터를 시각화해서 살펴보기
import matplotlib.pyplot as plt
plt.imshow(x_train[0].reshape((28,28)),cmap='gray')
plt.show()

데이터는 잘불러 온 것 같다.
그다음에는 Pytorch의 데이터 형태인 Tensor로 변환해주자.
# Tensor로 변환
import torch
X_train, Y_train, X_val, Y_val = (torch.tensor(i.copy()) for i in [x_train, y_train, x_valid, y_valid])
n,c = x_train.shape
print("X_train shape :",X_train.shape)
print("Y_train shape :",Y_train.shape)
print("X_valid shape :",X_val.shape)
print("Y_valid shape :",Y_val.shape)

이제 데이터가 준비되었으니 신경망 모델을 만들어보자.
신경망을 만들기 위해 가중치와 편향을 초기화 할 필요가 있다.
초기화 방식은 Xavier initialisation 기법을 사용하여 초기화 할 것이다.
# 1. 가중치 초기화
# Xavier initialisation
# n[l-1] : 이전 레이어의 입력 feature의 수
# shape : 가중치 행렬의 크기
# W[l] = np.random.randn(shape) * np.sqrt(1/(n[l-1]))
# 출처: https://pupbani.tistory.com/194 [Pupbani's Program Lab:티스토리]
import math
weights = torch.randn(c,10) / math.sqrt(c)
weights.requires_grad_() # 해당 단계가 기울기에 포함되는 것을 원하지 않음
bias = torch.zeros(10,requires_grad=True)
# activation function -> log_softmax 사용
def log_softmax(x): return x-x.exp().sum(-1).log().unsqueeze(-1)
# activation function을 사용하는 함수(forward propagation)
def model(xb):return log_softmax((xb @ weights) + bias) # @은 행렬 곱
그리고 이 가중치와 편향을 사용해 log_softmax 함수를 정의하고 이 활성화 함수를 사용하여 Forward Propagation을 진행하는 함수를 정의한다.
그 다음으로 Mini-batch를 만들고 이 함수를 사용하여 예측을 진행해보자.
# 2. Mini-batch
batch_size = 64
# mini-batch 추출
xb = torch.tensor(x_train[0:batch_size])
# mini-batch를 사용해 forward propagation 진행
preds = model(xb)
print(preds[0])
print(preds.shape)

출력값을 살펴보면 preds 텐서는 값 이외에도, 기울기 함수(Gradient Function)을 포함한다. 이 기울기 함수는 나중에 Back Propagation에서 사용할 것 이다.
이제 손실을 구하기 위해 음의 우도(Negative log-likelihood)를 구하는 함수를 구현해보자.

# 3. 손실 계산(음의 로그 우도)
def nll(input,target): return -input[range(target.shape[0]),target].mean()
loss_func = nll
# 무작위 모델에 대한 손실을 구해서 Back Propagation 이후 개선이 있는지 확인
yb = y_train[0:batch_size]
print(loss_func(preds,yb))
무작위 모델에 대한 손실을 구해서 Back Propagation 이후 개선이 있는지 확인한다.

분류 모델을 만들것이므로 정확도(accuracy)를 계산하기 위한 함수를 구현하자.
(매 예측마다, 만약 가장 큰 값의 인덱스가 target value와 동일하다면, 그 예측은 올바른 것이다.)
def accuracy(out, yb):
pred = torch.argmax(out,dim=1)
yb_t = torch.tensor(yb)
acc = (pred == yb_t).float().mean()
return acc
print(accuracy(preds,yb))

이제 위에서 만들어 놓은 함수들로 Training Loop를 구성해보자. 매 반복마다 다음을 수행할 것 이다.
- 데이터의 Mini-Batch 선택
- 모델을 통한 예측 수행(Forward Propagation)
- 손실 계산
- loss.backward()를 이용하여 모델의 기울기 업데이트(Weight, bias Update)(Back Propagation)
def my_logistc_model(train,target,learning_rate=0.5,epochs=5,batch_size=64,weights=weights,bias=bias):
for epoch in range(epochs): # epoch
for i in range((n-1)//batch_size+1): # Mini-Batch Loop
start = i * batch_size
end = start + batch_size
# mini-batch select
xb = train[start:end]
yb = target[start:end]
# Forward Propagation
pred = model(xb)
# Calculate loss
loss = loss_func(pred,yb)
# Backward Propagation
loss.backward()
# 파라미터 업데이트
with torch.no_grad():
weights -= weights.grad * learning_rate
bias -= bias.grad * learning_rate
# 변화도 초기화
weights.grad.zero_()
bias.grad.zero_()
# 정확도와 손실
ac = accuracy(model(xb),yb)
print(f"loss :{loss} ---- accuracy : {ac}")
return (loss,pred,ac)
loss,pred,ac= my_logistc_model(X_train,Y_train)

print("Final")
print("loss:",loss)
print("accuracy:",ac*100)

이제 torch.nn을 사용해서 간단하게 모델을 만들어보자.
제일 먼저 구현 했던 손실 함수를 대체해보자.(torch.nn.functional에 있는 함수)
# 손실 함수
import torch.nn.functional as F
print("loss_func",loss_func(model(xb),yb))
# cross_entropy는 NLL과 log softmax를 결합한것임
loss_func = F.cross_entropy
print("torch.nn.f",loss_func(model(xb),yb))

그 다음은 nn.Module, nn.parameter를 사용해서 아까 위에서 만들었던 모델을 리팩토링 해보자.
# Refactorying Function
from torch import nn
class Mnist_Logistic(nn.Module):
"""
shape : X 데이터 1개의 형태 ex. 784(= 28 x 28)
layer : 레이어의 크기
"""
def __init__(self,shape,layer):
super().__init__()
self.weights = nn.Parameter(torch.randn(shape,layer)/math.sqrt(shape))
self.bias = nn.Parameter(torch.zeros(layer))
# forward()
def forward(self,xb):
return xb @ self.weights + self.bias
def fit(X,Y,learning_rate=0.5,epochs=5,batch_size=64,Layer=10):
# model init
model = Mnist_Logistic(X.shape[1],Layer)
# Train Loop
for epoch in range(epochs): # epoch
for i in range((n-1)//batch_size+1): # Mini-Batch Loop
start = i * batch_size
end = start + batch_size
# mini-batch select
xb = X[start:end]
yb = Y[start:end]
# Forward Propagation
pred = model(xb)
# Loss Calculate
loss = loss_func(pred,yb)
# Backward Propagation
loss.backward()
# Update Parameter
with torch.no_grad():
for p in model.parameters():
p -= p.grad * learning_rate
model.zero_grad()
# 정확도와 손실
ac = accuracy(model(xb),yb)
print(f"loss :{loss} ---- accuracy : {ac}")
return (loss,pred,ac)
loss,pred,ac = fit(X_train,Y_train)

print("-------------Final-------------")
print("loss:",loss)
print("accuracy:",ac)

nn.Linear
nn.Linear를 사용하여 리팩토링 할 수 있다.
weights, bias를 계산하는 대신에, 모든 것을 해줄 nn.Linear를 선형 레이어로 사용하면 된다.
이는 종종 기존 코드보다 속도를 빠르게 한다.
# Linear를 사용하여 리팩토링
class Mnist_Logistic(nn.Module):
"""
shape : X 데이터 1개의 형태 ex. 784(= 28 x 28)
layer : 레이어의 크기
"""
def __init__(self,shape,layer):
super().__init__()
self.lin = nn.Linear(shape,layer)
# forward()
def forward(self,xb):
return self.lin(xb)
fit(X_train,Y_train)
print("-------------Final-------------")
print("loss:",loss)
print("accuracy:",ac)


Optimization
Pytorch에는 다양한 Optimization 알고리즘을 가진 패키지인 torch.optim가 있다.
각 매개변수를 수동으로 업데이트 하는 대신, Optimizer의 step 메소드를 사용하여 업데이트를 진행할 수 있다.
from torch import optim
def get_model(X,Y,learning_rate):
model = Mnist_Logistic(X.shape[1],10)
return model,optim.SGD(model.parameters(),lr=learning_rate)
def fit(X,Y,opt,model,learning_rate=0.5,epochs=5,batch_size=64,Layer=10):
# Train Loop
for epoch in range(epochs): # epoch
for i in range((n-1)//batch_size+1): # Mini-Batch Loop
start = i * batch_size
end = start + batch_size
# mini-batch select
xb = X[start:end]
yb = Y[start:end]
# Forward Propagation
pred = model(xb)
# Loss Calculate
loss = loss_func(pred,yb)
# Backward Propagation
loss.backward()
# Update Parameter
opt.step()
opt.zero_grad()
# 정확도와 손실
ac = accuracy(model(xb),yb)
print(f"loss :{loss} ---- accuracy : {ac}")
return (loss,pred,ac)
learning_rate = 0.5
model,opt = get_model(X_train,Y_train,learning_rate)
loss, pred,ac = fit(X_train,Y_train,opt,model,learning_rate=learning_rate)

print("-------------Final-------------")
print("loss:",loss)
print("accuracy:",ac)

Dataset을 이용하여 리팩토링
기존에 x,y 값을 각각 미니 배치를 별도로 반복했다면
Dataset을 사용하여 함께 수행 할 수 있다.(TensorDataset())
from torch.utils.data import TensorDataset
train_ds = TensorDataset(X_train,Y_train) # 데이터 합치기
def fit(dataset,opt,model,learning_rate=0.5,epochs=5,batch_size=64,Layer=10):
# Train Loop
for epoch in range(epochs): # epoch
for i in range((n-1)//batch_size+1): # Mini-Batch Loop
start = i * batch_size
end = start + batch_size
# mini-batch select
xb,yb = dataset[start:end] # 하나의 dataset으로 mini-batch
# Forward Propagation
pred = model(xb)
# Loss Calculate
loss = loss_func(pred,yb)
# Backward Propagation
loss.backward()
# Update Parameter
opt.step()
opt.zero_grad()
# 정확도와 손실
ac = accuracy(model(xb),yb)
print(f"loss :{loss} ---- accuracy : {ac}")
return (loss,pred,ac)
TensorDataset을 사용하면 데이터를 하나로 합쳐서 사용할 수 있으므로 Mini-batch를 만드는 일을 두 번안해도 된다.
DataLoader을 이용하여 리팩토링
DataLoader()를 사용하면 데이터를 합칠 때, 미니배치를 자동으로 할 수 있게 만들어준다.
from torch.utils.data import DataLoader
train_ds = TensorDataset(X_train,Y_train)
batch_size = 64
train_dl = DataLoader(train_ds,batch_size=batch_size) # Mini-batch
def fit(dataset,opt,model,learning_rate=0.5,epochs=5,batch_size=64,Layer=10):
# Train Loop
for epoch in range(epochs): # epoch
for xb,yb in dataset: # Mini-Batch Loop
# Forward Propagation
pred = model(xb)
# Loss Calculate
loss = loss_func(pred,yb)
# Backward Propagation
loss.backward()
# Update Parameter
opt.step()
opt.zero_grad()
# 정확도와 손실
ac = accuracy(model(xb),yb)
print(f"loss :{loss} ---- accuracy : {ac}")
return (loss,pred,ac)
Validation Set
훈련 중에 항상 과적합(Overfitting)을 확인하기 위해서 향상 검증 데이터셋이 있어야한다.
훈련 데이터를 섞는 것은 배치와 과적합 사이의 상관 관계를 방지하기 위해 중요하다.
(검증 손실은 검증 데이터셋을 섞든 안섞든 동일한다.)
검증 데이터셋에 대한 배치 크기는 학습 데이터셋 배치 크기의 2배를 사용하는데, 검증 데이터셋에 대해서는 역전파가 필요하지 않으므로 메모리를 덜 사용하기 때문이다.(기울기 저장할 필요 없음), 더 큰 배치를 사용하여 손실을 더 빨리 계산하기 위함
train_ds = TensorDataset(X_train, Y_train)
train_dl = DataLoader(train_ds, batch_size=batch_size, shuffle=True)
valid_ds = TensorDataset(X_val, Y_val)
valid_dl = DataLoader(valid_ds, batch_size=batch_size * 2)
def fit(train_dataset, val_dataset,opt,model,learning_rate=0.5,epochs=5,batch_size=64,Layer=10):
# Train Loop
for epoch in range(epochs): # epoch
model.train()
for xb,yb in train_dataset: # Mini-Batch Loop
# Forward Propagation
pred = model(xb)
# Loss Calculate
loss = loss_func(pred,yb)
# Backward Propagation
loss.backward()
# Update Parameter
opt.step()
opt.zero_grad()
# 정확도와 손실
ac = accuracy(model(xb),yb)
# 검증
model.eval()
with torch.no_grad():
valid_loss = sum(loss_func(model(xb),yb) for xb,yb in val_dataset)
print(f"epoch{epoch} loss :{loss} ---- accuracy : {ac} ---- val_loss:{valid_loss / len(val_dataset)}")
return (loss,pred,ac,valid_loss)
loss,pred,ac,val_loss = fit(train_dl,valid_dl,opt,model)

print("-------------Final-------------")
print("loss:",loss)
print("accuracy:",ac)

하나의 모델로 만들기
앞의 모델을 클래스로 하나로 합치기
class PipeLine:
def __init__(self,x_train,y_train,x_valid,y_valid,batch_size=64,learning_rate=0.5):
# Var
self.batch_size = batch_size
self.learning_rate = learning_rate
# Data
# To Tensor
self.X_train, self.Y_train, self.X_val, self.Y_val = (torch.tensor(i.copy()) for i in [x_train, y_train, x_valid, y_valid])
self.train_ds = TensorDataset(self.X_train, self.Y_train)
self.train_dl = DataLoader(self.train_ds, batch_size=self.batch_size, shuffle=True)
self.valid_ds = TensorDataset(self.X_val, self.Y_val)
self.valid_dl = DataLoader(self.valid_ds, batch_size=self.batch_size * 2)
# Model
self.model = Mnist_Logistic(self.X_train.shape[1],10)
# Optimization
self.opt = optim.SGD(self.model.parameters(),lr=learning_rate)
def get_model(self): return self.model
def fit(self,epochs=5):
# Train Loop
for epoch in range(epochs): # epoch
self.model.train()
for xb,yb in self.train_dl: # Mini-Batch Loop
# Forward Propagations
pred = self.model(xb)
# Loss Calculate
loss = loss_func(pred,yb)
# Backward Propagation
loss.backward()
# Update Parameter
self.opt.step()
self.opt.zero_grad()
# 정확도와 손실
ac = accuracy(self.model(xb),yb)
# 검증
self.model.eval()
with torch.no_grad():
valid_loss = sum(loss_func(self.model(xb),yb) for xb,yb in self.valid_dl)
print(f"epoch{epoch} loss :{loss} ---- accuracy : {ac} ---- val_loss:{valid_loss / len(self.valid_dl)}")
return (loss,pred,ac,valid_loss / len(self.valid_dl))
mn = PipeLine(x_train,y_train,x_valid,y_valid)
loss,pred,ac,valid_loss = mn.fit()
print()
print("-------------Final-------------")
print("loss:",loss)
print("accuracy:",ac)
print("valid_loss :",valid_loss)

'AI > AI 라이브러리' 카테고리의 다른 글
[Tensorflow] Tensorflow 입문 (0) | 2024.01.08 |
---|---|
[Pytorch] 연습 데이터로 라이브러리 익숙해지기2 (0) | 2024.01.03 |
[Pytorch] 모델 저장하고 불러오기 (0) | 2023.12.29 |
[Pytorch] 모델 매개변수 최적화하기 (0) | 2023.12.28 |
[Pytorch] Autograd (0) | 2023.12.28 |