본문 바로가기
AI/딥러닝

04. 데이터로더(Data Loader)

by 사라리24 2024. 6. 20.
SMALL

 

1. 데이터로더(Data Loader)

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

 

 

 

데이터로더(DataLoader)의 역할

  • 배치 처리: 데이터를 지정된 배치 크기로 나누어 모델에 입력으로 제공합니다.
  • 셔플(shuffle): 데이터를 섞어서 모델이 데이터의 순서에 의존하지 않도록 합니다. 특히 훈련 데이터에서 사용됩니다.
  • 병렬 처리: num_workers 매개변수를 설정하여 데이터를 병렬로 로드하여 속도를 높일 수 있습니다.

 

  • import

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

 
 

 

  • GPU를 활용할 수 있는지 확인 (런타임 유형 변경 후 런타임 다시 실행)

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




GPU가 없는 경우에는 출력은 'cpu'
CUDA를 사용할 수 있다면, 출력은 'cuda'

 

 

 

  • scikit-learn에서 제공하는 숫자 이미지 데이터셋인 digits를 로드
    : 데이터와 타겟을 출력

       
        digits = load_digits()

        x_data = digits['data']
        y_data = digits['target']

        print(x_data.shape)
        print(y_data.shape)

 
(1797, 64)
(1797,)

 

  • 숫자 이미지 데이터셋을 시각화
    (
    이미지를 2x5 형태로 출력 )

       
        fig, axes = plt.subplots(nrows=2, ncols=5, figsize=(14, 8))

        for i, ax in enumerate(axes.flatten()):
            ax.imshow(x_data[i].reshape((8, 8)), cmap='gray')
            ax.set_title(y_data[i])
            ax.axis('off')


 



  • PyTorch를 사용하여 데이터를 텐서 형태로 변환

       
      x_data = torch.FloatTensor(x_data)
      y_data = torch.LongTensor(y_data)

      print(x_data.shape)
      print(y_data.shape)


x_data = torch.FloatTensor(x_data)

  • x_data를 PyTorch의 FloatTensor로 변환합니다.
  • FloatTensor는 부동소수점 형식의 텐서
     x_data는 이미지 데이터로, 각 픽셀 값이 부동소수점으로 표현

y_data = torch.LongTensor(y_data)

  •  y_data를 PyTorch의 LongTensor로 변환합니다.
  • LongTensor는 정수형 텐서
     y_data는 각 이미지의 실제 숫자 값(타겟)을 나타냅니다.
torch.Size([1797, 64])
torch.Size([1797])


텐서의 형태(모양) 출력
 데이터셋에는 1797개의 이미지 데이터가 있고, 각 이미지는 64개의 특성을 가진다
 데이터셋에는 1797개의 타겟 값이 있음을 나타냅니다. 각 값은 해당 숫자 이미지의 실제 숫자

 

 

 

  • 데이터를 훈련 세트테스트 세트로 나누기

       
        from sklearn.model_selection import train_test_split

        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)


torch.Size([1437, 64]) torch.Size([1437])
torch.Size([360, 64]) torch.Size([360])

 

 

  • DataLoader를 사용하여 데이터셋을 불러오는 과정

       
        loader = torch.utils.data.DataLoader(
            dataset=list(zip(x_train, y_train))
        )

        dataset=list(zip(x_train, y_train))
        dataset


loader = torch.utils.data.DataLoader(dataset=dataset)

    • torch.utils.data.DataLoader 클래스를 사용하여 데이터셋을 로드하는 데이터로더를 생성합니다.
    • dataset 매개변수에는 데이터셋을 전달합니다. 이 경우 위에서 생성한 dataset 변수를 사용합니다.
    • 데이터로더는 데이터셋을 배치(batch)로 나누고, 섞기(shuffle), 병렬로 데이터를 로드할 수 있는 기능을 제공합니다

dataset=list(zip(x_train, y_train))

      • zip(x_train, y_train)을 사용하여 x_train과 y_train 데이터를 묶어서 튜플의 리스트(list)로 만듭니다.
      • 각 튜플은 (x_train[i], y_train[i]) 형태로 구성됩니다.
      • zip 함수는 두 개 이상의 리스트나 튜플에서 각 리스트나 튜플의 동일한 인덱스 요소를 묶어주는 역할을 합니다.
      • 이렇게 구성된 리스트를 dataset이라는 변수에 할당합니다.

dataset

    • dataset을 출력하여 내용을 확인합니다.
    • dataset은 zip 함수를 통해 묶인 x_train과 y_train 데이터의 쌍을 담고 있는 리스트입니다.

[(tensor([ 0.,  0.,  0.,  0.,  9., 15.,  0.,  0.,  0.,  0.,  1., 10., 16., 16.,
           1.,  0.,  0.,  5., 16., 15., 14., 16.,  0.,  0.,  0.,  1.,  8.,  0.,
          10., 16.,  0.,  0.,  0.,  0.,  0.,  0., 11., 16.,  0.,  0.,  0.,  0.,
           0.,  0., 10., 15.,  0.,  0.,  0.,  0.,  0.,  0., 12., 16.,  3.,  0.,
           0.,  0.,  0.,  0.,  8., 16.,  3.,  0.]),
  tensor(1)),
 (tensor([ 0.,  0.,  0., 15., 16., 16., 12.,  4.,  0.,  0.,  4., 14.,  0., 10.,
          12.,  0.,  0.,  0.,  8.,  7.,  1., 15.,  4.,  0.,  0.,  0.,  0.,  0.,
           8., 12.,  0.,  0.,  0.,  0.,  1.,  8., 14., 12.,  3.,  0.,  0.,  0.,
           6., 13., 16., 13.,  2.,  0.,  0.,  0.,  0., 10., 10.,  0.,  0.,  0.,
           0.,  0.,  2., 16.,  2.,  0.,  0.,  0.]),


......


 (tensor([ 0.,  0.,  3., 12., 16., 13.,  0.,  0.,  0.,  1., 14.,  9., 10., 13.,
           0.,  0.,  0.,  0.,  2.,  0., 10., 10.,  0.,  0.,  0.,  0.,  3.,  7.,
          15., 16., 10.,  0.,  0.,  0., 16., 16., 15.,  3.,  0.,  0.,  0.,  0.,
           3., 13.,  7.,  0.,  0.,  0.,  0.,  0.,  0., 16.,  2.,  0.,  0.,  0.,
           0.,  0.,  4., 15.,  0.,  0.,  0.,  0.]),
  tensor(7)),
 (tensor([ 0.,  0.,  9.,  7.,  0.,  0.,  0.,  0.,  0.,  0.,  9., 11.,  0.,  0.,
           0.,  0.,  0.,  0., 15.,  4.,  0.,  0.,  0.,  0.,  0.,  2., 16.,  1.,
           0.,  0.,  0.,  0.,  0.,  5., 16.,  8., 14.,  9.,  0.,  0.,  0.,  5.,
          16., 15.,  8.,  9., 10.,  0.,  0.,  3., 16.,  2.,  0.,  7., 11.,  0.,
           0.,  0.,  7., 14., 16., 12.,  1.,  0.]),
  tensor(6)),
 ...]

 

 

 

  • 데이터셋을 배치(batch) 단위로 로드,
    첫 번째 배치의 이미지를 시각화

       
        loader = torch.utils.data.DataLoader(
            dataset=list(zip(x_train, y_train)),
            batch_size=64,
            shuffle=True
        )
        imgs, labels = next(iter(loader))
        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')

 


DataLoader 설정

  • torch.utils.data.DataLoader 클래스를 사용하여 데이터로더를 설정합니다.
  • dataset=list(zip(x_train, y_train)): x_train과 y_train을 zip 함수를 사용하여 묶어서 데이터셋을 생성합니다.
  • batch_size=64: 배치 크기를 64로 설정합니다. 이는 한 번에 로드할 데이터의 개수를 의미합니다.
  • shuffle=True: 데이터를 섞어서 로드합니다. 특히 훈련 데이터에서 사용하여 모델이 데이터의 순서에 의존하지 않도록 합니다.

  • next(iter(loader)): loader에서 첫 번째 배치를 가져옵니다. iter(loader)는 데이터로더를 반복 가능한 객체로 만들고, next() 함수를 사용하여 첫 번째 배치를 가져옵니다.
  • imgs는 배치에 포함된 이미지 데이터를, labels는 해당 이미지의 레이블(타겟)을 나타냅니다.

시각화

  • fig, axes = plt.subplots(nrows=8, ncols=8, figsize=(14, 14)): 8x8 그리드 형태로 서브플롯을 생성합니다. 각 서브플롯은 이미지를 표시할 공간을 제공합니다.
  • for ax, img, label in zip(axes.flatten(), imgs, labels):: axes.flatten()으로 펼친 서브플롯과 imgs, labels를 순회하면서 각각의 서브플롯에 이미지와 레이블을 출력합니다.
  • ax.imshow(img.reshape((8, 8)), cmap='gray'): imshow() 메서드를 사용하여 이미지를 흑백(gray)으로 표시합니다. img.reshape((8, 8))를 사용하여 이미지를 8x8 크기로 변환하여 표시합니다.
  • ax.set_title(str(label)): 서브플롯의 제목으로 레이블을 설정합니다. str(label)을 통해 레이블을 문자열로 변환하여 제목으로 설정합니다.
  • ax.axis('off'): 서브플롯의 축을 제거하여 이미지 주변의 축을 숨깁니다.
100개의 데이터 포인트와 64개의 특성을 가진 x_train과 그에 해당하는 레이블을 가진 y_train을 사용하여 데이터셋을 생성합니다. 그리고 첫 번째 배치(64개의 데이터)를 로드하여 시각화합니다. 각 이미지는 그리드 형태로 배치되며, 제목에는 해당 이미지의 레이블이 출력됩니다.



 

  • 간단한 신경망 모델을 정의하고, 이를 훈련하는 과정

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

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

          epochs = 50

          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)
              avg_acc = sum_accs / len(loader)
              print(f'Epoch {epoch:4d}/{epochs} Loss: {avg_loss:.6f} Accuracy: {avg_acc:.2f}%')





Epoch    0/50 Loss: 1.522297 Accuracy: 61.89%
Epoch    1/50 Loss: 0.299263 Accuracy: 90.92%
Epoch    2/50 Loss: 0.186552 Accuracy: 94.28%
Epoch    3/50 Loss: 0.155529 Accuracy: 95.27%
Epoch    4/50 Loss: 0.116850 Accuracy: 96.54%
Epoch    5/50 Loss: 0.101433 Accuracy: 97.46%
Epoch    6/50 Loss: 0.105107 Accuracy: 96.54%

.......

Epoch   42/50 Loss: 0.007945 Accuracy: 100.00%
Epoch   43/50 Loss: 0.010049 Accuracy: 99.86%
Epoch   44/50 Loss: 0.009674 Accuracy: 100.00%
Epoch   45/50 Loss: 0.011133 Accuracy: 99.80%
Epoch   46/50 Loss: 0.007075 Accuracy: 100.00%
Epoch   47/50 Loss: 0.006850 Accuracy: 100.00%
Epoch   48/50 Loss: 0.007192 Accuracy: 100.00%
Epoch   49/50 Loss: 0.006950 Accuracy: 100.00%
Epoch   50/50 Loss: 0.006263 Accuracy: 100.00%

모델 정의

  • nn.Sequential을 사용하여 순차적인 레이어를 쌓는 모델을 정의합니다.
  • nn.Linear(64, 10): 입력 크기가 64이고 출력 크기가 10인 선형 레이어를 추가합니다.
    이는 입력 데이터가 64차원 벡터이고, 출력으로 10차원 벡터(클래스 개수)를 반환하는 선형 변환을 수행합니다.

옵티마이저 설정

  • optim.Adam 옵티마이저를 사용하여 모델의 파라미터를 최적화합니다.
  • model.parameters()를 통해 모델의 학습 가능한 모든 파라미터를 가져옵니다.
  • lr=0.01: 학습률을 설정합니다. Adam 옵티마이저의 경우 학습률을 조정할 수 있습니다.

학습 루프

  • epochs = 50: 전체 데이터셋을 몇 번 반복하여 학습할지 설정합니다.
  • for epoch in range(epochs + 1): 주어진 epoch 수만큼 반복합니다.
  • sum_losses = 0와 sum_accs = 0로 각 epoch에서의 총 손실(loss)과 정확도(accuracy)를 초기화합니다.
내부 학습 루프
  • for x_batch, y_batch in loader: 데이터 로더에서 배치 단위로 데이터를 가져옵니다.
  • y_pred = model(x_batch): 모델에 입력 데이터 x_batch를 전달하여 예측값을 계산합니다.
  • loss = nn.CrossEntropyLoss()(y_pred, y_batch): Cross Entropy 손실 함수를 사용하여
                                                                                                      예측값 y_pred와 실제 타겟 y_batch 간의 손실을 계산합니다.
  • optimizer.zero_grad(): 옵티마이저의 gradient를 초기화합니다.
  • loss.backward(): 손실을 역전파하여 각 파라미터의 gradient를 계산합니다.
  • optimizer.step(): 옵티마이저를 사용하여 한 스텝 최적화를 수행합니다.
  • sum_losses = sum_losses + loss: 배치 손실을 총 손실에 더합니다.
정확도 계산
  • y_prob = nn.Softmax(dim=1)(y_pred): 출력을 소프트맥스 함수를 통해 확률로 변환합니다.
  • y_pred_index = torch.argmax(y_prob, axis=1): 예측된 클래스의 인덱스를 가져옵니다.
  • acc = (y_pred_index == y_batch).float().mean() * 100: 배치의 정확도를 계산합니다.
                                                                                                                 맞춘 예측의 비율을 계산하여 백분율로 표시합니다.
  • sum_accs = sum_accs + acc: 배치 정확도를 총 정확도에 더합니다.
에폭 종료 후 평균 손실과 정확도 계산 및 출력
  • avg_loss = sum_losses / len(loader): 전체 데이터셋에 대한 평균 손실을 계산합니다.
  • avg_acc = sum_accs / len(loader): 전체 데이터셋에 대한 평균 정확도를 계산합니다.
  • print(f'Epoch {epoch:4d}/{epochs} Loss: {avg_loss:.6f} Accuracy: {avg_acc:.2f}%'):
    각 epoch의 종료 후 평균 손실과 정확도를 출력합니다.

주의사항

  • 모델의 정의(nn.Sequential)와 학습 루프에서 사용되는 손실 함수(nn.CrossEntropyLoss())
    옵티마이저(optim.Adam)는 간단한 예제를 위해 선택된 것입니다.
  • 실제 문제에 따라 모델 구조, 손실 함수, 학습률 등을 적절히 선택하여 사용해야 합니다.
  • 데이터셋과 모델의 특성에 따라 학습이나 성능 평가 방법이 달라질 수 있습니다.
  • 주석
더보기

 


     
          model = nn.Sequential(
              nn.Linear(64, 10)  # 입력 차원 64, 출력 차원 10인 선형 레이어를 정의합니다.
          )

          optimizer = optim.Adam(model.parameters(), lr=0.01)  # Adam 옵티마이저를 사용하고 학습률은 0.01로 설정합니다.

          epochs = 50  # 전체 데이터셋을 50번 반복하여 학습합니다.

          for epoch in range(epochs + 1):  # 주어진 epoch 수만큼 반복합니다.
              sum_losses = 0  # 현재 epoch의 총 손실을 저장할 변수를 초기화합니다.
              sum_accs = 0  # 현재 epoch의 총 정확도를 저장할 변수를 초기화합니다.

              for x_batch, y_batch in loader:  # 데이터 로더에서 배치 단위로 데이터를 가져옵니다.
                  y_pred = model(x_batch)  # 모델에 입력 데이터를 전달하여 예측값을 계산합니다.
                  loss = nn.CrossEntropyLoss()(y_pred, y_batch)  # Cross Entropy 손실 함수를 사용하여 손실을 계산합니다.

                  optimizer.zero_grad()  # 옵티마이저의 gradient를 초기화합니다.
                  loss.backward()  # 손실을 역전파하여 각 파라미터의 gradient를 계산합니다.
                  optimizer.step()  # 옵티마이저를 사용하여 파라미터를 업데이트합니다.

                  sum_losses = sum_losses + loss  # 배치 손실을 현재 epoch의 총 손실에 더합니다.
                 
                  y_prob = nn.Softmax(dim=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  # 배치 정확도를 현재 epoch의 총 정확도에 더합니다.

              avg_loss = sum_losses / len(loader)  # 전체 데이터셋에 대한 평균 손실을 계산합니다.
              avg_acc = sum_accs / len(loader)  # 전체 데이터셋에 대한 평균 정확도를 계산합니다.
              print(f'Epoch {epoch:4d}/{epochs} Loss: {avg_loss:.6f} Accuracy: {avg_acc:.2f}%')  # 현재 epoch의 평균 손실과 정확도를 출력합니다.


 
 

 

 

 

 

  • 손글씨 숫자 데이터셋에서 테스트 세트의 10번째 데이터를 시각화, 해당 데이터의 레이블을 출력

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

 
 
tensor(7)






 

  • 10번째 데이터에 해당하는 예측값을 출력

       
        y_pred=model(x_test)
        y_pred[10]


tensor([ -6.4605,  -3.0447, -12.9360,  -4.3177,  -3.6224,  -9.9192, -10.9246,
         10.5876,  -2.3388,   1.4592], grad_fn=<SelectBackward0>)

 

 

  • Softmax 함수로 예측된 결과(y_pred)에 대해 확률을 계산

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


tensor([3.9450e-08, 1.2009e-06, 6.0780e-11, 3.3624e-07, 6.7395e-07, 1.2415e-09,
        4.5428e-10, 9.9989e-01, 2.4327e-06, 1.0853e-04],
       grad_fn=<SelectBackward0>)

 

 

 

  • 각 클래스에 해당하는 확률을 순서대로 출력

      
        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
  • for i in range(10)::
    • range(10)은 0부터 9까지의 정수를 순회하는 반복문을 의미합니다.
    • 여기서 i는 반복문을 통해 각 클래스를 순서대로 나타내는 변수입니다. 예를 들어, i가 0일 때는 클래스 0에 해당하는 확률을 출력하게 됩니다.
  • print(f'숫자 {i}일 확률:{y_prob[10][i]:.2f}'):
    • f-string을 사용하여 출력 포맷을 지정합니다.
    • 숫자 {i}일 확률:은 출력 문구로, i에 해당하는 숫자를 표시합니다.
    • {y_prob[10][i]:.2f}는 소프트맥스 확률 분포 y_prob[10]에서 i번째 인덱스에 해당하는 확률을 소숫점 둘째 자리까지 출력합니다.

 

 

  • 예측한 결과(y_pred)와 실제 정답(y_test)을 비교하여 정확도를 계산하고 출력

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


테스트 정확도는 95.83%입니다!