import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import torch
import torch.nn as nn
import torch.nn.functional as F
from torch.utils.data import Dataset,DataLoader
사용할 경로와 epoch, 배치사이즈, learning rate를 한곳에서 쉽게 조정하여 사용할 수 있도록 하였다.
class PATH:
TRAIN = '/kaggle/input/digit-recognizer/train.csv'
TEST = '/kaggle/input/digit-recognizer/test.csv'
class CONFIG:
lr = 0.001
epoch = 10
batch_size = 256
아래코드는 가능한 경우 GPU를 사용하게 한다.
device = 'cuda' if torch.cuda.is_available() else 'cpu'
print(device)
cpu
학습데이터 만들기¶
df_train = pd.read_csv(PATH.TRAIN)
l = len(df_train)
pandas의 Dataframe형식으로 저장되어있던 이미지들을 사용하기 쉽게 변환해주는 작업이 필요하다. 일차원 배열로 저장되어있는 하나의 이미지 정보를 28x28의 형태로 만들고 256으로 나누어 0~1 사이의 값으로 만들었다. 이후 모델에서 Conv2d를 사용하는데, 이때 입력 이미지 데이터의 형태는 (batch_size, depth, width, height)임으로 이에 맞게 형태를 수정해야 한다. 이후 trainset과 validset를 나누어준다. 여기선 임의로 끝의 3000개 데이터를 validset으로 사용하였다.
_x = torch.Tensor(df_train.iloc[:,1:].values).reshape(l,28,28) / 256 #28*28의 형태로 변환후 0~1 사이의 값으로 변환
_x = _x.unsqueeze(1) # (batch_size, depth, width, height)로 형태를 맞추기 위한 코드
_y = torch.Tensor(df_train.iloc[:,0].values).type(torch.long)
train_x ,valid_x = _x[:39000],_x[39000:] #train,valid 분활
train_y ,valid_y = _y[:39000],_y[39000:]
torch.utils.data의 Dataset과 DataLoader를 이용하여 데이터를 불러오고, shuffle하거나, batch별로 불러오는 작업을 간단히 수행할 수 있다.
class DigitDataset(Dataset):
def __init__(self,x,y=None):
super(DigitDataset).__init__()
self.x = x
self.y = y
def __getitem__(self,idx):
if self.y == None:
return self.x[idx]
return self.x[idx],self.y[idx]
def __len__(self):
return len(self.x)
trainset = DigitDataset(train_x,train_y)
validset = DigitDataset(valid_x,valid_y)
train_dl = DataLoader(trainset,batch_size = CONFIG.batch_size,shuffle=True)
valid_dl = DataLoader(validset,batch_size = CONFIG.batch_size)
학습모델 만들기¶
class DigitModel(nn.Module):
def __init__(self):
super(DigitModel,self).__init__()
self.model = nn.Sequential(
nn.Conv2d(1,32,3),
nn.Conv2d(32,8,3),
nn.Flatten(),
nn.Linear(24*24*8,256),
nn.ReLU(),
nn.Linear(256,128),
nn.ReLU(),
nn.Linear(128,10)
)
def forward(self,x):
x = self.model(x)
x = torch.softmax(x,dim=-1)
return x
model = DigitModel().to(device)
모델 학습 (train)¶
학습에는 손실함수로 CrossEntropy가 사용되었고, optimizer는 Adam을 사용하였다.
lossfn = nn.CrossEntropyLoss()
opt = torch.optim.Adam(model.parameters(), lr=CONFIG.lr)
한번의 epoch에 대하여 trainset 전체를 한번 학습하고 validset으로 검증이 이루어지는 코드이다.
for e in range(CONFIG.epoch):
print(f"Training on progress... {e+1}/{CONFIG.epoch}")
acc =0
total =0
model.train() # train
for x,y in train_dl:
x = x.to(device)
y = y.to(device)
y_hat = model(x)
loss = lossfn(y_hat,y)
opt.zero_grad()
loss.backward()
opt.step()
model.eval() #valid
with torch.no_grad():
for vx,vy in valid_dl:
vx = vx.to(device)
vy = vy.to(device)
vy_hat = model(vx)
pred = vy_hat.max(dim=1)[1]
acc += (pred == vy).sum().item()
print(f"Epoch {e+1} : Acc {100*acc/3000}")
Training on progress... 1/10 Epoch 1 : Acc 92.1 Training on progress... 2/10 Epoch 2 : Acc 94.06666666666666 Training on progress... 3/10 Epoch 3 : Acc 95.16666666666667 Training on progress... 4/10 Epoch 4 : Acc 95.63333333333334 Training on progress... 5/10 Epoch 5 : Acc 96.16666666666667 Training on progress... 6/10 Epoch 6 : Acc 96.0 Training on progress... 7/10 Epoch 7 : Acc 96.2 Training on progress... 8/10 Epoch 8 : Acc 96.3 Training on progress... 9/10 Epoch 9 : Acc 96.63333333333334 Training on progress... 10/10 Epoch 10 : Acc 96.36666666666666
테스트 데이터에 대한 예측 시행¶
df_test= pd.read_csv(PATH.TEST)
l = len(df_test)
test_x = torch.Tensor(df_test.values).reshape(l,28,28) / 256
test_x = test_x.unsqueeze(1)
outputs = model(test_x)
_, pred = torch.max(outputs, 1)
pred = pred.cpu()
숫자를 잘 분류해내는지 확인해보자.
for i,img in enumerate(test_x[50:56]) :
plt.subplot(2,3,i+1)
plt.axis('off')
plt.title(f"Predicted : {pred[50+i]}")
plt.imshow(img.squeeze(0))
아래 코드는 캐글에 제출할 파일을 생성한다.
submission = pd.DataFrame({'ImageId': np.arange(1, (pred.size(0) + 1)), 'Label': pred})
submission.to_csv("submission.csv", index = False)
마무리¶
생성된 파일은 캐글 채점결과 96%의 정확도를 보여주었다. 대충만든 모델임에도 꽤 높은 정확도를 보여주었다. 그러나 성능향상의 여지는 상당히 남아있다. 좀더 생각해서 모델을 구성하고 학습방법을 수정한다면 더 높은 정확도를 달성할 수 있을 것이다.
관련 캐글 대회에서 다른 예제들을 확인할 수 있다. : https://www.kaggle.com/c/digit-recognizer/overview
'Study > Python' 카테고리의 다른 글
Python에서 Glob으로 파일 혹은 폴더의 경로 불러오기 (0) | 2021.03.26 |
---|---|
Pytorch Tensor(텐서) 만들기 (0) | 2021.03.23 |
Pytorch로 선형 회귀(Linear Regression) 구현하기 (0) | 2021.03.15 |
.py를 .ipynb으로, 또 그 반대로 (3) | 2020.10.17 |
Python에서 c,c++ 코드 사용하기 (0) | 2020.10.04 |
댓글