PyTorch, DeepLearning

msung99·2022년 11월 27일
0
post-thumbnail

본 포스트팅은 인하대학교 컴퓨터공학과 오픈소스sw개론 수업자료에 기반하고 있습니다. 개발 포스팅과 달리 개인적인 학습을 위한 정리여서 설명이 다소 미흡할 수 있으니, 간단히 참고하실 분들만 포스팅을 참고해주세요 😎


  • torch.tensor() 와 같이 생성가능
  • 여러 타입이 섞여있으면 안된다.
  • torch 안의 데이터들 변경 가능(tensor 의 Variable 처럼)
  • t.dim() : 차원(rank)

  • torch 는 디폴트로 해당 데이터의 타입들 보고 타입(ex. int32)을 결정
  • torch.FloatTensor() 와 같이 tensor 생성시 타입 지정가능

torch 의 인덱싱과 슬라이싱

이전에 했던것들과 방식이 완전히 동일하다.

  • t[:, 1] : 1열의 모든 데이터를 출력
  • t[:][1] : 모든 행중에서 1행을 출력하는 것. 즉 4,5,6 이 출력된다.
  • t[:, :-1] : 모든 행중에서 맨 마지막 열을 제외한 모든 데이터를 출력

사칙연산

  • tensorflow 의 연산과 동일. 마찬자기로 그냥 곱셈과 matmul 을 햇갈리지 말자!

reshape

  • tensor 와 다른점 : tensor 는 reshape() 함수를 썼지만, torch 는 view() 함수를 사용한다.
  • 마찬가지로 reshape 할때 -1 을 2개이상 쓰지말자!

PyTorch 에서 선형회귀(Linear regrssion)

tensorflow 와 전반적인 선형회귀 과정은 매우 유사하다.

1) dataset 을 pytorch 가 사용 가능한 형태로 dataset 을 얻어온다.

2) model 을 정의한다.

  • 클래스로 정의하면 된다.
  • 다양한 model 종류중에 사용할 모델종류 선택해서 상속받으면 된다.

3) loss function 을 정의한다.

  • 이 과정이 사실 필요하긴 한데, 여기서는 pytorch의 MSE 가 간단해서 그냥 바로 계산했다. (따라서 이 과정은 따로 여기서 없음)

  • 경사하강법을 사용. 경사하강법이란 도달하고 싶은 함수의 형태에 맞춰서 최소화 시키는 과정이라고 했었다. 값을 조금씩 변화시켜 가면서 원하느 함수에 대한 형태로 근사시키는 방식이었다. 조금씩 근사시킬때 방향과 보폭을 미분값을 통해서 알아낼 수 있었다.

4) model 의 predict(예측값 = 결과값) 을 얻어낸다)

  • 예측값을 얻어내고 label (실제값) 을 계산해낸다.

5) gradient(미분 값) 를 계산하고 model 의 변수들을 계속 update 해준다.

  • 지정된 학습률이나 보폭등을 기반으로 학습을 계속 for문으로 반복해주면 된다.

6) 4,5 과정을 수렴할 떄 까지 계속 반복한다.


Linear regression 구현하기 예제

1. Dataset 구성

  • tensor 와 거의 유사함. 우리가 원하는 가상의 직선은 y = 3x + 2 로 정의한다.

  • -2 ~ 2 까지 201개의 데이터를 생성한다.

  • torch.randn() : 201개의 데이터에 대해 noise 를 발생시킨다. 즉 -2~2 사이의 201개의 난수를 발생시킨다.

    • <=> 텐서플로우 : tf.random.normal()

2. Model 정의하기

  • 지난번과 거의 동일.

  • 다른점은 torch.nn.Module 를 상속받아야한다.

  • 또한 텐서플로우의 call 메소드와 동일한 기능을 하는것이 forward 메소드이다.

  • torch.nn.Linear(input 개수 n, output 개수 n ) 함수 : keras 의 dense 와 동일한 역할을 수행.

    • 예를들어 파라미터로 2, 2 을 넣었다면 인풋 2개에서 아웃풋 2개로가는 weight 과 bias 를 만들어달라는 것이된다. 즉, y = wx + b 2개를 만든다.
    • 이 방법으로도 high-level 방식으로써 linear regression 을 만들 수 있는것이다.

3. model 학습시키기

  • 앞서 생성한 MyTorchModel 클래스로 생성한 model 객체가 있을 것이다.
  • 텐서플로우의 update 와 관련한 코드로, 굉장히 low-level 방식으로 assign_sub 와 같은 함수들로 변수의 값들을 바꿨었다.

  • torch.optim.SGD(model.parameters()) : gradient descendent 와 관련해 최적화해주는 모듈.

    • gradient desendent 방식으로의 최적화와 관련한 각종 클래스, 함수등의 기능들이 다양하게 탑제되어 있다.
    • model.parameters() : 해당 model 객체의 모든 변수를 리턴해줘서 학습에 사용한다.
    • lr : learning rate


학습시키기 및 update 과정

  • 앞서 optimzer 를 선언해줬다면, 이제 for문을 조지면서 학습을 시켜주면 된다.

update 과정은 위와 같은 5가지 코드 (step) 을 통해 가능하다.

1. 차원 변환해주기

  • 유의할점은, 앞서 정의하길 x 는 201개의 난수를 리스트 형태로써 저장하고 있었다. 즉 "1차원" 리스트 형태로 저장하고 있었는데,
    y.view(-1, 1) 과 같이 201x 1 형태의 2차원 행렬 형태로 굳이 변환해주었다.

    => 변환 이유 : 해당 객체가 이러한 차원의 형식밖에 못 받기 떄문이다.
    - pytorch 에서는 2차원 형태의 linear 를 받는다.
    - 당연히 이에대한 output 도 2차원 형태로 나온다.


2. 미분값(gradient) 계산하기

  • 바로 계산해준다. 텐서플로우에서는 함수를 따로 구현해줬지만, 이런 간단한 직선 그래프 형태는 그냥 바로 계산가능하다.

대신 pytorch 에서는 model의 변수들을 update 하는 과정이 조금 까다롭다. update 과정을 계속 살펴보자.


3. optimizer 초기화하기

  • update 을 위해선 먼저 optimizer 를 초기화해줘야한다. 매번 update 할때마다 가장 먼저 해줘야하는 과정이다.
    => zero_grad() 함수를 호출해서 초기화 하면 된다.

  • 초기화를 안해주면 계속해서 이전에 경사하강법으로 계산된 미분값들이 현재 변수에 누적된다(더해진다).


4. 미분값(gradient) 얻어내기

  • backward() 를 하면 cost를 계산하는데 쓰인 모든 tensor 들에 대해 미분을 수행한다.
  • 텐서플로우는 콕 찝어서 미분값을 얻어냈었다.

=> 뭔 소리인지 모르겠다면, backward() 를 사용하면 w(weight)와 b(bias)의 미분값(gradient) 가 계산되는 것말 알고 넘어가자.


5. update 해주기

  • step() 을 해주면 update 해주는 것이다.

torch.nn.Linear() 분석

앞서 살펴본 nn.Linear 객체는 2차원 형태였다. 각 차원은 의미를 지닌다.
(각각 어떤 의미를 지니는지는 이따 뉴련 설명할때 나옴!)

  • 개념적으로는 1차원이 대상이지만, 실제로는 2차원에 대해 수행한다.
  • output = model(x.view(-1,1)) : 과 같이 선언해주면 (201, 1) 2차원 행렬 형태를 지니는것으로 변환된다. 즉 각 데이터는 feature 가 1개이고, 총 데이터수는 201개인것이다.

  • cost.item() : pytorch의 스칼라 값을 파이썬 코드로 변환해줌 (중요한건 아님!)


학습 결과 log

나름대로 원래 값 3,2 에 근사하긴 했다.


딥러닝의 등장

  • 딥러닝 : 머신러닝의 일종. 뉴런 네트워크와 표현 학습(representation learning) 기반이다.
    • 표현 학습이란 feature 를 학습하는 것이라고 보면된다.
  • feature 를 사람들이 manually 디자인 하는것이다. ML과 달리 최적의(optimal) set(부분집합) 을 사람이 찾기가 힘들다. 이떄 등장한 것이 DL으로, feature 를 찾아준다.

ML 모델의 경우..

  • ML 의 경우 강아지, 고양이 이미지등의 인풋 데이터를 직접 사람이 넣어줬었다.

  • 이런 방식을 개선해, 기계가 스스로 feature 를 생성하고 학습하는 것이다. 이런 방식이 표현 학습이다.


이렇게 된다면 사람이 할일이 정말 줄어들긴하지만, 아직 성능이 부족했었다.

표현 학습이라는 것은 기계가 feature 에 대해 스스로 알아서 잘 학습해야하는데, 얼굴 형태(ex. 얼굴이 둥글다) 와 같은 어렵고 중요한 정보(high-level fearture) 들을 기계가 feature화 시키기 어려웠다.

대신 정말 단순한 정보인 low-level feature(ex. 이미지가 평평한가?) 들은 기계가 feature 이해하고 feature 로 만들기 쉬웠다.

=> 알고보니, high-level feature 들은 여러 low-level 들의 조합으로 만들 수 있었다! (ex. 엄청작은 픽셀들을 여러개 모아서 눈, 코, 눈썹과 같은 high-level feature 들을 생성 가능했다)

=> 픽셀들을 모아서 눈,코 등을 만들고, 이들을 또 모아서 하나의 얼굴을 만드는 등 계층 구조를 지녔다. 이렇게 층을 쌓아서 만드려면 여러 단계에 걸쳐서 해야지 high-level feature 결과가 나온다.


기존 표현학습의 한계점, 딥러닝의 등장

  • feature 를 학습하는 스탭이 너무 많아서, low-level 을 가지고 high-level feature 를 만드는 과정이 너무 오래걸렸다.

  • 뉴런 네트워크 : 이런 계층을 계속 층을 쌓고 쌓다보니 low-level 정보들을 high-level 정보들로 자라게 되는 것이다.

    • 자잘한 low-level 정보들을 쌓아서 더 큰 high-level 정보를 찾아나가는 확장 방식

=> 이러한 게층적 구조를 통한 학습방식을 딥러닝이라고 한다.


딥러닝이란?

  • 강아지에 대한 feature 를 한번에 바로 도출해내는 것이 아니라, 엄청 확대해서 나온 작은 feature 들을 분석하고 취합하는 방식
  • 인풋을 아웃풋으로 도출해내는 중간과정은 표현학습과 동일한데, 중간에 학습 과정이 하나 더 늘어났다.
    간단한 low-level 인 simple feature(단순한 feature) 부터 학습하기 시작에서, 좀 더 추상적인 feature(Abstract feature) 를 완성해나가는 과정을 수행한다.

뉴런 네트워크

  • 한 신경세포(뉴런)는 다른 여러 신경세포(뉴런)들로 부터 여러 정보를 받는다.

  • 다른 뉴런들로부터 정보를 받는다고 해서 무조건 정보(신호)를 넘겨받는 것이 아니라, 넘겨진 취합된 정보들이 일정 세기 이상이여야 신호가 전달된다.

  • 일정 기준을 넘기지 못하면 없던일이 되는것

  • 일정 기준을 넘기면 뉴런은 그 뒤로 정보를 넘긴다.

=> 정리하면, 정보를 제공해주는 뉴런은 많지만 받은 정보를 다른 뉴런에게 무조건 넘겨주는 것이 아닌, "비선형적으로 전달"해준다.


  • 인공 신경망의 기본 단위 : perceptron (=뉴런)

  • 앞서 살핀 자연계에서의 뉴런 구조처럼, 다양한 뉴런들로부터 인풋을 입력받는다.

  • perceptron 에서 정보가 처리되는 전 과정을 요약한 수식
  • 실제 뉴런 신경망에서의 실시간으로 정보가 일정 시간에 왔다갔다 하는것정보들을 취합을 하고 처리하는 과정을 묘사한 것이다.

1. Wx : Weight(가중치)

  • 각 입력이 들어온것에 대해서, Weight 를 둔다. 일정시간에 들어온 각 정보들에 대해서 Weight 를 곱해줘서 중요도를 판가름해준다.
    • Weight 가 클수록 더 중요한 정보이다.
  • 모든 인풋에 대해서 가중치(Weight)를 곱한다. 이떄 각 인풋마다 중요도에 따라서 서로 다른 가중치를 곱해준다. (극단적으로, 아예 안중요한 정보에 대해서는 가중치를 0을 곱해준다. 즉 해당 인풋을 아예 무시해 버리는것)

2. b(bias)

  • bias는 더할수도 있고, 안더할수도있다. Wx 들의 합연산들로만 정보를 취합하는것은 조금 아쉬우니, 추가적으로 bias 라는 값을 써서 더 더해주기도한다.

결국 아래와 같이 합을 도출해준다.


  • 그리고 앞서 계산된 합을 바로 내보내는 것이 아닌, f(x) 함수를 적용시켜서 아웃풋을 내보낸다.

  • 기본적으로는 디폴트로 바로 아웃풋을 보내주긴하는데, 대부분의 perceptron 에서는 비선형적으로 아웃풋을 내보내준다.

  • 오른쪽과 같은 함수들을 적용해서 비선형적으로 내보내준다.

만일 비선형 연산(non-linear)이 아닌, linear 연산을 적용해줬다면?

  • perceptron 에 정보들이 모여서 아웃풋으로 나갈텐데, linear 함수를 적용한 함수끼리 여러개를 엮으면 시너지가 발생하지 않는다.

    • linear 연산을 한 결과에다 linear 를 또 하면 그것은 그냥 linear 연산을 한 번 한것과 동일하다.

    • linear 로 층을 많이 쌓아봤자 의미가 없다. linear 중첩해봐야 linear 가 나와서 의미가 없는것이다.

  • 층을 쌓아야하는 경우도 있다. (linear 층 말고!) 아까 봤던 얼굴인식처럼, 사람은 쉽게하지만 기계는 쉽게 하기에는 어려운 것들은 feature 를 여러단계에 걸쳐서 분석해야 할 것이다.

=> 우리가 뭔가 복잡한 값에 대해 근사하고 싶은 경우, 충분한 횟수의 비선형 연산을 여러번 쌓아줘야한다. 비선형 연산을 여러번 적용해서 층을 쌓아놓으면, 그 안에서 최적화 하는것은 Weight 와 bias 가 바뀌면서 알아서 하는 것이다.

  • 비선형 연산을 여러번하는 층을 쌓고나서, Weight 와 Bias 를 최적화시켜줘야 복잡한 연산을 근사 가능하다.

뉴런 네트워크에서 경사 하강법이 적용 가능하다.

  • activation 함수가 미분이 가능해야 경사하강법이 적용 가능하다.
  • cf) 어려워서 이 과정은 생략하겠음!

layer

  • 위 그림에서 파란색 동그라미 하나하나가 perceptron 이다.

  • layer : 들어온 인풋에 대해서 동시간대 (같은 스탭)에 처리하는 perceptrons 들의 집합을 layer (층) 이라고 한다.

  • 한 layer 에서 뉴런(perceptrons) 을 더 늘리는 것이 가능하다.
    또한 layer 를 더 늘리는 것도 가능하다.

  • 위처럼 4개의 인풋이 들어오고 아웃이 1개가 나와야하는 상황의 경우,
    이 사이에서 무한대로 뉴런 네트워크를 확장이 자유롭게 가능한것이다.

hidden layer

  • 인풋과 아웃풋이 아닌 중간에 있는 perceptrons 들을 전부 숨겨있다고 해서, hidden layer 라고 부른다.

  • hidden layer 층이 2개 이상있는 뉴런 네트워크는 Deep 뉴런 네트워크라고 표현한다.


MNIST dataset

  • 가장 기본적인 ML, DL 의 dataset 임. (마치 c언어의 hello world 느낌!)
  • 숫자를 표현한 손글씨 이미지의 집합이다.
  • 각 숫자 손글씨마다 0~9 중에서 어떤 숫자인지를 분류해야한다.
  • dataset 의 데이터 개수 : train 데이터는 6만개, test 데이터는 1만개이다.
  • 각 숫자 이미지는 행렬로 표현된다.

=> 출력할 아웃풋 : 0~9 사이의 각 숫자의 클래스에 대한 확률이다.

  • 주어진 이미지가 model 이 판단했을때 7일 확률이 83% 이다! 라고 아웃풋을 만들어내는것

Data reshaping

  • 앞서 살핀 뉴런 네트워크는 1차원에 대한것을 봤었다. 아까봤던 torch linear 를 가지고 구성할 것이다.

  • 개념적으로는 1차원이 대상이고, 실제로는 2차원이 대상이다.

  • 각 숫자의 이미지 데이터는 2차원 또는 3차원이다. (가로 x 세로(2차원) or 가로 x 세로 x 색깔 픽셀값(3차원))

=> 이에따라 이미지는 2차원 또는 3차원으로, 1차원이 아니다. 그런데 우리가 만드는 뉴런 네트워크 model 은 1차원만 받을 수 있어서 문제가 발생한다. 이 문제는 reshape 해서 해결 가능하다. 즉 3차원 값을 일렬로 아래처럼 1차원 공간에 쫙 펴준다. 1차원으로 쫙 펴주면 784개의 픽셀 데이터로 구성된다.


model 생김새

  • 앞으로 구현한 뉴런 네트워크 model 을 아래처럼 단순화해서 표현해봤다.

맨처음에 인풋으로 784개가 들어온다. (직전에 이미지 데이터를 3차원에서 1차원으로 쫙 펴줬으므로)

이를 100개의 뉴런으로 연결하는 100개의 뉴런으로 구성된 layer 를 만든다.

이떄 두 layer 간에는 가능한 모든 경우에 대해 Weight로 연결이 되어있다.
이떄 총 연결개수는 784 x 100 = 78400개이다. 즉 78400개의 Weight 가 필요하다.

  • ReLU activation 을 통해 나온 결과 100개를 또 다음 layer의 100개의 뉴런으로 연결해준다.

  • 그리고 두번째 layer 에서 ReLU 가 아웃풋으로 나갈때는 10개가 나간다.
  • 10개로 아웃풋이 나오는것은 고정이다! 변할수없다. => 각 숫자에 대한 클래스 10개에 대해서 확률을 각각 표시해줘야하기 때문에, 클래스가 10개이므로 10개가 아웃풋으로 나가야한다.

MNIST classification using PyTorch

실습 환경은 아래와 같이 구축했다.


model 정의하기

아까봤던 숫자 글씨체에 대한 dataset 에 대한 뉴런 네트워크를 코드로 아래처럼 구현해봤다.

  • 위처럼 우리가 원하는 layer 들을 선언하면 그 순서대로 차곡차곡 layer 들이 쌓인다.

  • 이 model 이 호출되었을때 어떻게 아웃풋을 도출해줘야 하는지는 forward 함수를 통해 구현했다.

Dataset loading

dataset 을 불러오는데, 아래와 같은 방식으로 코드를 짜면 불러와진다는 것만 간단히 짚고 넘어가자.

  • 이때 한가지 짚고 넘어갈것은, 아까 선형회귀를 했을떄는 201개의 Training data 전체를 한번에 몽땅 들고왔었는데, dataset 용량이 꽤 크므로 잘개잘꺠 쪼개서 가져왔다. 이렇게 쪼개서 가져오는 것을 mini batch 라고한다.

=> dataset 에 대해 쪼갠 데이터 덩어리를 보고(들고와서) update 하고, 그 다음 덩어리보고 update 하는 것 과정을 반복하면서 학습을 진행한다!

  • dataset 을 load 해오는 것은 파이썬의 DataLoader 를 활용해서 가져온다.
    이떄 batch_size 라는 것을 지정해줄 수 있는데, 한번에 가져올 데이터 양을 지정해주는 것이다. (이 예제에서는 100 개를 가져오도록 값을 지정해줬다!)

for문으로 학습시키기

  • for문 조지면서 학습시키면 된다.

  • 아까와 다른점은, loss 를 define 해줬다. (아까는 바로 계산했었음)

  • mini betch 방식으로 데이터를 쪼개서 학습하기 때문에, for문이 가 2개가 돈다.(2중 for문) 쪼갠 데이터 안에서 각 데이터를 보는 for문이 추가된 것이다.

  • cost = critertion(output, y_train) : loss 계산

  • avg_cost : loss value 를 관리하기 위해 필요한 변수

    • 아까는 한번에 loss value 를 계산한거를 print 해주면 됐었지만, 여기서는 잘게 쪼갠 데이터 덩어리에 각각에 대해 loss value 가 나오니까 avg_cost 변수를 활용해서 취합을 해줘야하는 것이다.

loss 값 출력

  • loss 값이 점점 감소하는 것을 볼 수 있다.

Test 돌려보기

  • test 데이터를 이용해서 성능을 확인해본다.

  • argmax() 를 사용하면 인자로 전달받은 첫번째 입력의 dimension 에 대해 최댓값을 가지는 index 를 리턴해준다.

=> argmax 를 사용하고나면 몇번쨰 index 에 들어있는 값이 가장 큰지 확인할 수 있다. 즉 각 숫자 클래스에 대해 나오는 예측값(= 각 숫자 클래스에 대에 들어있는 확률값) 중에 가장 큰 확률값을 확인할 수 있다.


Keras 활용

1. DataSet Loading (불러오기)

  • mnist.load_data() : sklearn 의 train_test_split() 과 동일한 기능

2. Model 정의하기

  • Flatten : 어떤 차원의 tensor 가 들어오던지간에 모두 1차원으로 쭉 평평하게 펴주는것 (인풋이 28x28 2차원인데, 1차원 784 크기로 쭉 펴주는것)

  • Dense : nn.Linear 와 동일한 기능. 인풋 개수는 명시할 필요없이, 아웃풋 개수만 명시해주면 연결해준다.

    • activation 도 지정가능.

3. 학습시키기

  • sklearn 처럼 fit() 으로 학습시키는 것이 가능하다.
  • 위 예제에서는 fit 을 해주기전에 model 을 컴파일해줘서 Optimization 을 해주었다.

log 출력


test 해보기

  • test 는 evaluate 함수로 가능하다.
profile
블로그 이전 : https://haon.blog

0개의 댓글