본문 바로가기
AI 컴퓨터 비전프로젝트/[ML,DL]머신러닝,딥러닝

[ML/DL] 데이터로더의 의미를 확인 후 손글씨 데이터셋으로 테스트해보기

by 바다의 공간 2024. 7. 25.

1. 데이터 로더(Data Loader)

  • 데이터 양이 많을 때 배치 단위로 학습하는 방법을 제공

데이터를 쪼깬 후 그 작은 데이터를 학습시키고 다시 또 내보내는 역할을 합니다.

 


 


2. 손글씨 인식 모델 만들기

import torch
import torch.nn as nn
import torch.optim as optim
import matplotlib.pyplot as plt
from sklearn.datasets import load_digits
from sklearn.model_selection import train_test_split

 

#디바이스를 쿠바로 사용할 수 있도록 하려고 합니다.

device = 'cuda' if torch.cuda.is_available() else 'cpu'
device

 

#데이터셋 가져오기

digits = load_digits()

X_data = digits['data']
y_data = digits['target']
print(X_data.shape)
print(y_data.shape)

#(1797, 64)
#(1797,)

64개의 픽셀로 실행했다고 볼 수 있습니다.

x_data는 data를 y_data는 target을 가져옵니다.

 

 

#시각화 하기

# 그림을 그리기 위한 코드
# 2줄(row)과 5칸(column)으로 이루어진 그림판
# figsize=(14, 8) = 가로 14인치 세로 8인치
fig, axes = plt.subplots(nrows=2, ncols=5, figsize=(14, 8))



# 그림 그리기
# 그림판의 각 칸에 하나씩 그림을 그리기 위해 반복문 사용함.
# i == 몇번째 그림을 그리고 있는지 알려주는숫자
# ax == 현재 그릴 위치를 알려주는 칸
for i,ax in enumerate(axes.flatten()):

#x_data[i]== i번째 숫자 그림
#reshape(8*8) == 숫자그림을 8*8 크기의 모양으로 바꿔줌
# cmap=gray == 그림을 회색으로 그려달라고 하기
  ax.imshow(X_data[i].reshape(8,8), cmap='gray')
  
#제목을 붙여주는 부분
#y_daya[i] ==3 이라면 그 칸의 제목은 3
  ax.set_title(y_data[i])
  
#그림 주위에 있는 선들을 없애주는 부분 off를 하게되면 그림만 깔끔하게 보입니다.  
  ax.axis('off')

1줄: 전체적인 사이즈를 확인함

 

for문 

8*8로 사이즈를 정해주고 cmap을 gray로 맞춰줍니다.

label을 타이틀로 삼고 찍어주었습니다.

 

 

 

 

#텐서형으로 변경하기

X_data = torch.FloatTensor(X_data)
y_data = torch.LongTensor(y_data)
print(X_data.shape)
print(y_data.shape)

#torch.Size([1797, 64])
#torch.Size([1797])

 

#데이터 나누기

x_train, x_test, y_train, y_test = train_test_split(X_data, y_data, test_size=0.2, random_state=2024)

 

#데이터 사이즈 확인하기

print(x_train.shape, y_train.shape)
print(x_test.shape, y_test.shape)

 

#데이터 분할하기

x_train, x_test, y_train, y_test = train_test_split(X_data, y_data, test_size=0.2, random_state=2024)
print(x_train.shape, y_train.shape)
print(x_test.shape, y_test.shape)

8:2로 잘 분할된것을 확인할 수 있습니다.

 

 

#데이터로더 구축하기

하는역할   : 데이터셋를 뭉텅이를 갖고있고 한번 학습을 할 때마다 내가원해준 배치사이즈만큼 뱉어주는 역할합니다.

직접 구현해도 되지만(이터레이터사용하기 ) 파이토치로 사용가능합니다.

loader = torch.utils.data.DataLoader(
      dataset=list(zip(x_train, y_train)),
      batch_size=64,
      shuffle=True,
      drop_last=False  #짜투리를 쓸건지(true= 버려라, false=버리지마라)
)

imgs, labels = next(iter(loader)) #loader을 iterrater하게 순서를 나열해주고 next로 첫번째 블록을 뽑게 됩니다. 그러면 64개 이미지에 64개 정답이 들어가게 됩니다.
fig, axes = plt.subplots(nrows=8, ncols=8, figsize=(14, 14))

for ax, img, label in zip(axes.flatten(), imgs, labels):
    ax.imshow(img.reshape(8,8), cmap='gray')
    ax.set_title(str(label))
    ax.axis('off')

 

#코드해석

#데이터 로더는 데이터를 조금씩 나누어서 가져오는 도구예요. 큰 데이터를 한 번에 처리하기 어렵기 때문에 작은 덩어리로 나누어 처리해요.
#여기서는 x_train과 y_train 데이터를 묶어서 한 번에 64개씩 가져오도록 설정해요.

#dataset=list(zip(x_train, y_train)): x_train과 y_train을 묶어서 데이터셋을 만듭니다. x_train은 이미지 데이터, y_train은 그 이미지에 해당하는 정답(라벨)입니다.
#batch_size=64: 한 번에 64개의 데이터(이미지와 라벨)를 가져옵니다.
#shuffle=True: 데이터를 무작위로 섞어서 가져옵니다.
#drop_last=False: 마지막 남은 데이터를 버리지 않습니다.

loader = torch.utils.data.DataLoader(
      dataset=list(zip(x_train, y_train)),
      batch_size=64,
      shuffle=True,
      drop_last=False)

#iter(loader): 데이터 로더를 순서대로 나열할 수 있는 도구로 만듭니다.
#next(...): 첫 번째 64개의 데이터를 가져옵니다.
#imgs에는 64개의 이미지가, labels에는 64개의 정답이 들어갑니다.
imgs, labels = next(iter(loader)

#plt.subplots(nrows=8, ncols=8, figsize=(14, 14)): 8줄과 8칸으로 이루어진 큰 그림판을 준비합니다. 전체 크기는 14인치 x 14인치입니다.
fig, axes = plt.subplots(nrows=8, ncols=8, figsize=(14, 14))


#zip(axes.flatten(), imgs, labels): 각 칸(ax), 이미지(img), 라벨(label)을 묶어서 차례대로 처리합니다.
#ax.imshow(img.reshape(8,8), cmap='gray'): 이미지를 8x8 크기로 바꾸고, 회색조(gray)로 칸에 그림을 그립니다.
#ax.set_title(str(label)): 해당 칸 위에 정답(라벨)을 제목으로 적습니다.
#ax.axis('off'): 칸 주위의 선을 없앱니다.

for ax, img, label in zip(axes.flatten(), imgs, labels):
    ax.imshow(img.reshape(8,8), cmap='gray')
    ax.set_title(str(label))
    ax.axis('off')

shuffle=True,

drop_last=False 

짜투리를 쓸건지 안 쓸건지를 선택할 수있습니다.

그러면 False를 하게되면 그냥 짜투리 나머지값이 1로 돌게됩니다.

drop_last =

그 후 이미티, 라벨을 뱉어줄겁니다.

imgs, labels = 각각 x_train, y_train이 imgs, labels값에 들어가게 됩니다.

next라는 함수, iter함수를 사용하게되면  순서대로 나열되게 갹체를 만들어주고  next를 사용하면 가장 첫번째에있는것을 뽑게 됩니다.

그럼 imgs에 64개의 이미지 64개의 정답이 들어가게 됩니다.

 

이런식으로 8*8 개로 모양이 나타나게 됩니다.


#모델생성

model = nn.Sequential(
    nn.Linear(64, 10),
)

다중분류라서 Liner(64개의 데이터값이 들어오고, 나가는건 10개의 클래스로 아웃풋이죠)

그리고 다중분류라서 소프트맥스를 사용합니다.

소프트맥느는 크로스 앤트로피에 포함되어있기때문에 위에는 따로 적어주지앟습니다.

 

#옵티마이저 선언

optimizer = optim.Adam(model.parameters(), lr=0.01)

아담을 사용했습니다.

 

#에포크

epochs = 100

#64개의 데이터를 활용해 오차값+정확도 구하기
for epoch in range(epochs + 1):
    sum_losses = 0
    sum_accs = 0
    for x_batch, y_batch in loader:
        y_pred = model(x_batch)
        # 오차 값을 확인
        loss = nn.CrossEntropyLoss()(y_pred, y_batch)
        # 옵티마이저 초기화
        optimizer.zero_grad()
        # 기울기 구하기
        loss.backward()
        # 기울기 업데이트
        optimizer.step()
        # 오차를 더해서 누적시켜줌
        sum_losses = sum_losses + loss
        # 예측값을 넣어서 확률값을 얻기
        y_prob = nn.Softmax(1)(y_pred)
        # 가장 높은 값을 얻어서 변수 저장
        y_pred_index = torch.argmax(y_prob, axis=1)\
        # 맞췄는지 예측해보기
        acc = (y_batch == y_pred_index).float().sum() / len(y_batch) * 100
        # 누적시키기
        sum_accs = sum_accs + acc

    #전체 로스값 평균
    avg_loss = sum_losses / len(loader)
    #한 에폭에 대한 acc구하기
    avg_acc = sum_accs / len(loader)
    print(f'Epoch {epoch:4d}/{epochs} Loss: {avg_loss:.6f} Accuracy: {avg_acc:.2f}%')

이렇게되면 학습이 잘 되고 에포크값을 50으로 주었다가 맨 마지막 값이 98로 정확도가 살짝 떨어져서

100으로 올렸습니다. 학습빈도를 높이니 확연하게 정확도가 100%로 높아진것을 확인할 수 있었습니다.

 

물론 오버피팅이되어서 테스트값에는 잘 되지않을수도 있다는점도 염두에 두어야 합니다.

 

#시각화하기

plt.imshow(x_test[10].reshape((8, 8)), cmap='gray')
print(y_test[10])

x_test변수의 10번째를 8*8로 다시 모양을 만들어서 보면 

tensor[7]이고 8*8 사이즈로 잘 보여지는것을 확인할 수 있습니다.

#테스트 확인해보기

y_pred = model(x_test)
y_pred[10]

#tensor([ -8.1957,  -2.1434, -13.2778,  -4.5141,  -2.2247,  -8.6100, -17.3419,
#         14.4257,   0.3651,   4.5746], grad_fn=<SelectBackward0>)

 

이건 값일뿐입니다.(확률아님)

 

#확률

y_prob = nn.Softmax(1)(y_pred)
y_prob[10]

#tensor([1.4984e-10, 6.3693e-08, 9.3007e-13, 5.9502e-09, 5.8723e-08, 9.9016e-11,
#        1.5976e-14, 9.9995e-01, 7.8263e-07, 5.2686e-05],
#       grad_fn=<SelectBackward0>)

이것도 실수가 꽤 깊어서 for문을 돌려서 다시 표현해보겠습니다.

for i in range(10):
  print(f'숫자 {i}일 확률: {y_prob[10][i]:.2f}')
  
#숫자 0일 확률: 0.00
#숫자 1일 확률: 0.00
#숫자 2일 확률: 0.00
#숫자 3일 확률: 0.00
#숫자 4일 확률: 0.00
#숫자 5일 확률: 0.00
#숫자 6일 확률: 0.00
#숫자 7일 확률: 1.00
#숫자 8일 확률: 0.00
#숫자 9일 확률: 0.00

 

#테스트 정확도 알아보기

오버피팅될수도있으니 테스트 정확도를 확인해보기

y_pred_index = torch.argmax(y_prob, axis=1)
# y_test, y_pred_index 비교하기
accuracy = (y_test == y_pred_index).float().sum() / len(y_test) * 100
print(f'테스트 정확도는: {accuracy:.2f}% 입니다.')

#테스트 정확도는: 95.83% 입니다.