데보션앱 소개페이지 바로가기
로그인 선택

신고하기

CLOSE
신고사유 (대표 사유 1개)
상세내용 (선택)
0/200
  • 신고한 게시글은 더 이상 보이지 않습니다.
  • 이용약관과 운영정책에 따라 신고사유에 해당하는지 검토 후 조치됩니다.
  • 허위 신고인 경우, 신고자의 서비스 이용이 제한될 수 있으니 유의하시어 신중하게 신고해 주세요.
(이 회원이 작성한 모든 댓글과 커뮤니티 게시물이 보이지 않고, 알림도 오지 않습니다.)

미리보기

커뮤니티

      1,234

      badge 23.06.15

      글 등록

      카테고리를 선택해주세요.

      DEVOTEE를 활성화 시키면
      지금 작성한 커뮤니티 글에 대해 1개의 댓글을 달아줍니다.

      버튼을 누르면 글 수정 시 ChatGPT가 작성한 댓글이 수정됩니다.

      임시저장함에 저장되었습니다. 저장일시 : 2022.5.17 14:29:08

      임시저장함

      제목을 선택하시면 이어서 작성이 가능하며,
      최대 20건까지 저장합니다.
      컨텐츠 유형, 제목, 저장일시, 삭제로 이뤄진 임시저장 목록
      컨텐츠 유형 제목 저장일 삭제

      데보션 블로그 게재 요청

      CLOSE
      • *
      • *

      본인인증

      효율적인 데보션 서비스 이용 및
      고객님의 소중한 개인정보보호를 위해
      본인인증을 진행해주세요. 본인인증 미 진행 시 로그인이 제한됩니다.
      본인인증 실패

      본인인증 로그인에 실패하였습니다.
      회원이 아니시거나 본인인증 등록이
      완료되지 않은 사용자입니다.

      회원정보 연결

      고등학생도 이해하는 Transformer (Deep Learning) #5 (업데이트)

      fromDK 24.11.13
      2,441 12 0
      DEVOTEE 요약
      본 블로그는 고등학생도 이해할 수 있도록 딥러닝의 Transformer 개념을 설명하면서, 특히 벡터의 내적과 Linear Layer를 어떻게 활용하는지를 마치 강의를 하듯 설명합니다. MNIST 데이터셋을 통해 손글씨 숫자 인식 작업을 수행하며, Linear Layer를 활용해 입력 벡터와 다차원 매트릭스를 내적 연산으로 분류의 확률을 구합니다. 학습 과정에서 모델의 파라미터가 어떻게 변하는지를 예측하고 내적의 중요성을 이해하는 것이 중요하다고 강조합니다.
      DEVOTEE 추천 블로그

      고등학생도 이해하는 Transformer (Deep Learning) #1

      고등학생도 이해하는 Transformer (Deep Learning) #2

      고등학생도 이해하는 Transformer (Deep Learning) #3

      고등학생도 이해하는 Transformer (Deep Learning) #4


      지난 시간에 내적의 의미를 다시 한번 생각 해봤습니다.

      내적의 의미를 알면 계산을 하지 않아도 어느 벡터가 서로 얼마나 유사한지, 영향을 주는지, 닮았는지 형태만 보고서도 알 수 있었습니다.


      지금까지 나온 이야기가 딥러닝과 너무 거리가 멀다는 이야기가 있어서 이번에는 실제 코드로 내적을 실습 해보고자 합니다.

      (아~ 고등학생은 Python 모를려나요? ^^;; 대신 그림으로 최대한 설명 하겠습니다.)


      기존 MNIST 를 이용한 Deep Learning 학습 방법들은 이런 식으로 구성하면 학습이 된다에 많이 Focus되어 있다면,


      제가 아래에 설명 드리는 방법은 내적을 이해 하고,

      가장 기본적인 Deep Learning 모듈중 하나인 Linear Layer 를 통해 학습을 하고 내적으로 이해하는 방법을 설명 드리려고 합니다.


      학습 전체 코드는 아래 링크를 참고 바랍니다.

      학습 소스 코드


      MNIST Datasets을 이용하여 손글씨 숫자를 분류하는 Task를 만들어 보겠습니다.

      MNIST Dataset은 아래와 같이 손글씨 숫자로 28x28 크기의 이미지와 어떤 숫자인지를 나타내는 Label이 같이 들어 있습니다.

      이 데이터셋을 통해서 Deep Learning Network를 학습 해보겠습니다.



      우선 네트워크 설계를 합니다. DigitNet class 를 선언하고 초기화 함수( __init __ )에서 입력 벡터의 차원(in_channels)과 몇개의 class로 나눌지 입력 받습니다.

      Deep Learning에서는 벡터의 차원을 channel로 명명하고, 이러한 Vector가 모인 Matrix의 차원을 Dimension이라고 합니다.

      여기서는 0~9의 손글씨 숫자를 입력 받아서 0~9까지 분류(Classification)을 수행할 예정이므로 num_classes는 10으로 합니다.

      네트워크는 아주 간단하게 Linear Layer 하나만 추가하였습니다. Linear Layer는 단순히 하나의 메트릭스(Vector의 묶음)만 구성되어 있습니다.

      실제 네트워크가 호출될 때 실행되는 함수인 foward함수에서 linear레이어를 통과(self.fc()) 후 log_softmax함수를 통해서 각 분류의 확률로 변환 됩니다.

      softmax는 지난 시간에 잠깐 설명 드렸었고, log_softmax는 softmax 함수에 log를 취한 값이라고 보시면 됩니다.

      수식이 어려우면 그냥 log와 exp 를 제거 하고 이전 시간에 설명 드린 simple prob와 같이 모든 값을 더한 값으로 각 개별 값을 나누어 확률로 변환한다고 생각 하시면 조금 쉽게 이해 될것 같습니다.


      class DigitNet(nn.Module):
          def __init__(self, in_channels, num_classes=10):
              super(Net, self).__init__()
              self.fc = nn.Linear(in_channels, num_classes, bias=False)
      
          def forward(self, x):
              x = self.fc(x)
              output = F.log_softmax(x, dim=1)
              return output

      코드가 복잡(?)하나 중요한것은 4번 라인과 7,8 번 라인만 참고 하시면 됩니다.

      4번 라인은 아래와 같이 Linear Layer 생성 후 self.fc (Fully Connected) 에 객체를 저장합니다. Fully Connected는 바로 아래에서 다시 설명 드릴께요. 지금은 전체 흐름을 보도록 하겠습니다.

      self.fc = nn.Linear(in_channels, num_classes, bias=False)

      7,8번 라인은 Linear 레이어를 통과 후 log_softmax함수를 실행하여 확률로 변환 합니다.

      x = self.fc(x)
      output = F.log_softmax(x, dim=1)

      아래와 같이 torch.nn.Linear 클래스는 in_features로 in_channels을 out_features로 num_classes로 설정하여 생성하면,

      num_classes, x in_channels 크기의 Matrix가 생성되고 입력 벡터와 내적 연산을 수행하게 됩니다.


      정리하면, torch.nn.Linear 클래스는 내부에 num_classes x, in_channels 크기의 Matrix가 있고, 이 Matrix와 입력 데이터와 곱, Dot Product, 내적을 수행하는 모듈입니다. 아래에서 그림으로 다시 설명 드릴께요.

      class torch.nn.Linear(in_features, out_features, bias=True, device=None, dtype=None)

      참고 : pytorch Linear


      아래 Pytorch Linear 의 설명을 보면 "Applies an affine linear transformation ..." 이라고 나옵니다. 어~ 잠깐, 어디서 보던 단어인데요.

      잘 기억 하실지 모르지만 지난 3편에서 Affine Transformation에 대해 잠깐 설명 드렸습니다 ^^ Basis Vector와 입력 벡터간의 내적을 통해 변환을 수행한다고 보시면 됩니다. 그리고 Affine Transformation이 아닌 왜 Affine Linear Transformation 일까요? Linear Function 및 Non-Linear Function에 대해서도 추후 설명 드리도록 하겠습니다.

      (참고로 ML/DL을 조금 아시는 분은 본 학습 방법에는 하나 빠진 부분이 있는 것을 알 수 있는데요. 바로 Activation Function 입니다. 이 이야기는 나중에 Linear/Non-Linear Function 이야기 때 다시 자세히 설명 드릴께요.)


      image.png

      위 링크의 설명과 같이 Linear Layer는 x에 A 행렬과 곱(벡터 내적)을 수행 후 b(bias)를 더하도록 되어 있습니다.

      (bias는 옵션이라 여기서는 제외하도록 하겠습니다. 그럼 xA^T만 남는 군요)

      A는 메트릭스 곱의 연산을 위해 Transpose 한, 즉 행과 열을 바꾼 계산을 위해 90도 회전한( 사실 이부분은 모르시면 그냥 넘어가도 됩니다. 메트릭스 곱을 위해 90도 회전 했을 뿐입니다.) 행렬입니다.


      image.png

      이 연산을 그림으로 나태내면 아래와 같습니다.

      image.png

      입력 벡터 x 는 in_channels 크기의 벡터이고, A는 num_classes x in_channels 크기의 행렬이 있습니다.


      linear layer에서는 아래 그림과 같이 입력 벡터 x와 행렬 A(벡터의 모음)과 내적을 계산하고 결과값을 차례로 저장합니다

      image.png

      image.png

      image.png

      그리고는 마지막으로 log_softmax 함수를 통해 각 분류별 확률로 변환합니다. 만약 x 가 "1"의 이미지가 입력된경우 Label 1(①)인 확률이 가장 크겠지요.


      학습 코드를 간단히 설명하면 아래와 같이 학습할 데이터를 로딩하고, 학습에 이용할 train_dataset과 성능 확인에 이용할 test_dataset으로 나눕니다.

      transform=transforms.Compose([
          transforms.ToTensor(),
          transforms.Normalize((0.1307,), (0.3081,))
          ])
      train_dataset = datasets.MNIST('../data', train=True, download=True, transform=transform)
      test_dataset = datasets.MNIST('../data', train=False, transform=transform)
      
      train_loader = torch.utils.data.DataLoader(train_dataset, batch_size=10)
      test_loader = torch.utils.data.DataLoader(test_dataset,  batch_size=10)

      이후, 이미지의 크기를 DigitNet 네트워크의 Linear Layer 입력 feature(in_channels)로 설정하고, 출력 feature는 0~9까지 숫자인 10(num_classes)로 설정합니다.

      Optimizer는 SGD(Stochastic Gradient Descent)로 설정합니다.

      Optimizer는 간단히 설명하면, 학습을 통해 어느 방향으로 A의 값들을 변화해야 학습이 잘될 수 있는지를 미분을 통해 Loss가 줄어드는 방향으로 A의 값들을 변화하기 위한 모듈입니다.

      (이건 추후 설명 드리도록 하겠습니다.)

      width = train_dataset[0][0].shape[1]
      height = train_dataset[0][0].shape[2]
      image_size = width * height
      
      model = DigitNet(in_channels=image_size).to(device)
      optimizer = optim.SGD(model.parameters(), lr=0.000005)

      쉽게 설명 드리면 입력 x 벡터와 A에서 0~9에 해당하는 벡터 10개와 각각 내적을 계산 합니다. 내적의 의미는 유사도 라고 했지요?

      이를 softmax함수를 통하면 확률로 변환 되는데 이는 각 0~9와 유사한 확률이라고 할 수 있습니다. 따라서 가장 높은 확률이 정답이라고 할 수 있는데요.

      학습 초기에 A 에는 Random한 값들이 들어가 있습니다. 그러므로 처음에는 성능이 낮고, 틀릴 수 있는데요.

      채점을 통해 정답과 틀릴 경우 선생님께 혼나고, 틀린 문제들을 학습하면서 A의 값을 수정(정답과 유사하게 변경)하게 됩니다.

      학습을 더해 갈 수록 정확도가 올라가면서 A에 값들에 변화가 생기게 됩니다.

      아래는 선생님께 혼나고, A값의 수정 방향을 결정하는 코드 입니다.

      optimizer.zero_grad()
      data = data.view(-1, width*height)
      output = model(data)
      loss = F.nll_loss(output, target)
      loss.backward()
      optimizer.step()

      입력 이미지(data)는 2차원이므로 이를 1차원으로 변환하기 위해 data.view(-1, width*height)로 벡터로 변환합니다.

      이경우 아래 그림과 같이 이미지가 Raster Scaninig 되어 벡터로 변환됩니다.



      아래 로그는 학습이 진행되면서 정확도가 79% -> 90%로 올라가는 것을 보실 수 있습니다.

      2차원 이미지를 단순히 1차원으로 처리 했는데도 성능이 잘? 나오네요.

      이미지 공간적 특성인 2차원을 고려한 모듈인 CNN(Convolution Neural Network)을 이용하면 성능이 더 잘 나올 것 같습니다.

      idx:5900 loss:0.849: : 6000it [00:24, 244.47it/s]
      100%|██████████| 10000/10000 [00:06<00:00, 1580.87it/s]
      
      0 Test set: Average loss: 0.9843, Accuracy: 7921/10000 (79%)
      
      idx:5900 loss:0.539: : 6000it [00:20, 298.69it/s]
      100%|██████████| 10000/10000 [00:06<00:00, 1532.86it/s]
      
      1 Test set: Average loss: 0.7116, Accuracy: 8437/10000 (84%)
      
      idx:5900 loss:0.427: : 6000it [00:19, 313.26it/s]
      100%|██████████| 10000/10000 [00:06<00:00, 1541.27it/s]
      
      2 Test set: Average loss: 0.6023, Accuracy: 8584/10000 (86%)
      
      idx:5900 loss:0.369: : 6000it [00:19, 301.94it/s]
      100%|██████████| 10000/10000 [00:06<00:00, 1534.79it/s]
      
      3 Test set: Average loss: 0.5418, Accuracy: 8673/10000 (87%)
      
      idx:5900 loss:0.334: : 6000it [00:18, 316.38it/s]
      100%|██████████| 10000/10000 [00:06<00:00, 1539.99it/s]
      
      4 Test set: Average loss: 0.5026, Accuracy: 8730/10000 (87%)
      
      idx:5900 loss:0.309: : 6000it [00:19, 311.95it/s]
      100%|██████████| 10000/10000 [00:06<00:00, 1567.38it/s]
      
      5 Test set: Average loss: 0.4748, Accuracy: 8784/10000 (88%)
      
      idx:5900 loss:0.292: : 6000it [00:19, 306.45it/s]
      100%|██████████| 10000/10000 [00:05<00:00, 1668.49it/s]
      
      6 Test set: Average loss: 0.4539, Accuracy: 8826/10000 (88%)
      
      idx:5900 loss:0.278: : 6000it [00:19, 305.49it/s]
      100%|██████████| 10000/10000 [00:05<00:00, 1687.02it/s]
      
      7 Test set: Average loss: 0.4376, Accuracy: 8848/10000 (88%)
      
      idx:5900 loss:0.268: : 6000it [00:19, 307.42it/s]
      100%|██████████| 10000/10000 [00:05<00:00, 1683.10it/s]
      
      8 Test set: Average loss: 0.4243, Accuracy: 8877/10000 (89%)
      
      idx:5900 loss:0.259: : 6000it [00:19, 305.77it/s]
      100%|██████████| 10000/10000 [00:05<00:00, 1689.25it/s]
      
      9 Test set: Average loss: 0.4132, Accuracy: 8892/10000 (89%)
      
      idx:5900 loss:0.252: : 6000it [00:19, 305.15it/s]
      100%|██████████| 10000/10000 [00:05<00:00, 1670.66it/s]
      
      10 Test set: Average loss: 0.4039, Accuracy: 8908/10000 (89%)
      
      idx:5900 loss:0.246: : 6000it [00:19, 305.17it/s]
      100%|██████████| 10000/10000 [00:06<00:00, 1595.37it/s]
      
      11 Test set: Average loss: 0.3959, Accuracy: 8934/10000 (89%)
      
      idx:5900 loss:0.241: : 6000it [00:19, 305.83it/s]
      100%|██████████| 10000/10000 [00:06<00:00, 1524.49it/s]
      
      12 Test set: Average loss: 0.3889, Accuracy: 8951/10000 (90%)
      
      idx:5900 loss:0.236: : 6000it [00:19, 315.15it/s]
      100%|██████████| 10000/10000 [00:06<00:00, 1518.52it/s]
      
      13 Test set: Average loss: 0.3828, Accuracy: 8963/10000 (90%)
      
      idx:5900 loss:0.232: : 6000it [00:18, 317.95it/s]
      100%|██████████| 10000/10000 [00:06<00:00, 1539.09it/s]
      
      14 Test set: Average loss: 0.3773, Accuracy: 8974/10000 (90%)
      
      idx:5900 loss:0.229: : 6000it [00:18, 316.42it/s]
      100%|██████████| 10000/10000 [00:06<00:00, 1544.58it/s]

      우리는 지금까지 내적에 의미에 대해 배웠었는데요.

      우리 한번 학습을 진행 할 수록 한번 A가 어떻게 변화될지 한번 추측해 볼까요?


      지금 까지 강의를 잘 듣고 내적의 의미를 이해 하셨다면 A가 어떻게 변화 되었을지 쉽게 예측 하실 수 있습니다!!

      참고로 해당 벡터는 무려 784 차원 입니다. (3차원을 넘어서 784차원을 이해하신 것 입니다!!)


      결과는 아래와 같습니다.


      내적은 벡터가 서로 얼마나 유사한지, 영향을 주는지, 닮았는지를 나타낸다고 했습니다.

      내적의 의미를 이해 했다면 Linear Layer의 A 행렬이 학습을 거듭할 수록 입력과 유사하게 변화할것이라는 것을 예측 하실 수 있었을 것 입니다.


      Linear Layer의 A행렬을 0~9에 해당하는 벡터로 분리 후, 다시 이미지로 볼 수 있도록 2차원으로 변경하여 이미지로 출력 하였습니다.

      잘 보시면 A행렬이 각각 0~9에 해당하는 글자와 유사하게 학습 된 것을 확인 하실 수 있습니다.

      추출한 이미지의 검은 부분은 픽셀데이터가 존재하면 안되는 부분이고(유사하지 않아야 하는 부분), 흰색 부분은 픽셀 데이터가 존재 해야 하는 부분(유사해야 하는 부분)입니다.

      주위의 회색부분은 학습 데이터에서 픽셀이 존재하지 않아서 영향이 크지 않은 부분(초기 값의 랜덤한 값)입니다.

      이렇게 데이터를 모델에 입력하여 학습을 진행하면 Machine 이 알아서 해당 분류 Task를 수행하고 최적의 Weight(Basis Vector)를 찾는 과정을 진행 하게 됩니다.

      이러한 Layer를 여러 층을 거치게 되면 Deep Learning이 되게 됩니다.

      image.png


      추가로 그림을 더 극적으로 보이게 하기 위해 중간에 Fully Connected 레이어의 초기 값을 Random Value가 아닌 -100.0으로 초기화 하겠습니다.

      # 모든 weight 값을 -100.0 로 초기화
      with torch.no_grad():
          model.fc.weight.fill_(-100.0)

      위와 같은 코드를 모델 생성 후 학습 루프 시작전에 추가 합니다.


      그리고 다시 학습을 진행해보면 아래와 같이 각 숫자와 유사한 분포로 weight가 변한 것을 보실 수 있습니다. 기존에는 초기값이 Random Value여서 코너나 각 숫자의 픽셀값이 업는 부분은 Random 한 값이 변화가 없어서 첫번째 그림처럼 약간 지저분한 그림이 출력되지만, -100.0으로 초기화를 수행 후에는 숫자의 픽셀 데이터가 있는 부분은 값이 큰 값으로 표시되고 나머지 부분(픽셀데이터가 없는 부분은 검은색으로 표시 됩니다) 학습이 진행 되면서 매 에폭마다 weight 값이 점점 변화되는 모습을 확인 하실 수 있습니다.


      아래 그림은 첫번째 epoch에서의 weight 이미지



      아래 그림은 마지막 epoch의 weight 이미지. 학습이 진행될 수록 Weight가 입력 값과 유사하게 학습되는 것을 알 수 있습니다.



      정리하면 Fully Connected (Linear)레이어는 학습 가능한 값으로 된 Matrix가 입력 데이터와 내적을 수행하는 Layer 이며, 학습을 통해 입력 값의 어떤 특징을 추출(특징과 유사하면 값이 높음)하도록 학습이 되는 모듈입니다.

      처음에는 -100.0으로 모든 Label의 Vector가 검은색이였으나, 학습을 통해 각 숫자와 유사한 형태로 Vector가 구성이 되었습니다.


      예를 들면 1번 Label의 Vector는 "1"의 입력이 들어오면 1과 유사하면 선생님께 칭찬을 받고, 1과 유사하지 않으면 혼나게되어 결국 1과 유사하게 되는 구조입니다.


      이러한 Label별 Vector를 모은게 행렬(Matrix)로 Linear 레이어에서 학습이 됩니다.


      Linear Layer는 Dense Layer, Fully Connected Layer, Projection Layer, Feed Forward Network 등으로 불리며 여러 곳에서 많이 사용되는 모듈입니다.

      위 예제를 Linear Layer를 통해 내적의 의미를 이해하는데 많은 도움이 되었으면 좋겠습니다.

      댓글 0

      DEVOTEE를 활성화 시키면
      지금 작성한 댓글에 AI가 댓글을 달아줍니다.

      fromDK 님의 최신 블로그

      더보기

      DEVOTEE 추천 블로그

      동영상 기고하기