선형 회귀와 자동 미분

  • 데이터에 대한 이해(Data Definition) : 학습할 데이터에 대해서 알아봅니다.
  • 가설(Hypothesis) 수립 : 가설을 수립하는 방법에 대해서 알아봅니다.
  • 손실 계산하기(Compute loss) : 학습 데이터를 이용해서 연속적으로 모델을 개선시키는데 이 때 손실(loss)를 이용합니다.
  • 경사 하강법(Gradient Descent) : 학습을 위한 핵심 알고리즘인 경사 하강법(Gradient Descent)에 대해서 이해합니다.

 

데이터에 대한 이해

- 훈련 데이터셋과 테스트 데이터 셋 : 훈련에 사용되는 데이터를 train dataset, 학습이 끝난 후 이 모델이 얼마나 잘 작동하는지 판별하는 데이터셋을 test dataset.

- x_train과 y_train은 맵핑되어야함.

 

가설 수립

- 머신러닝에서 식을 세울때 이를 가설이라고함. 이는 추정일수도, 경험에서 나오는 것일 수도 있음. 맞는 가설이 아니라면 계속 수정해나가면 됨.

- y = Wx + b 와 같은 형식 혹은 가설(hypothesis)의 h를 따서 H(x) = Wx + b라고도 표현.

- W를 가중치(Weight), b를 편향(bias)이라고함.

 

비용 함수(Cost function)에 대한 이해

- 비용 함수(cost function) = 손실 함수(loss function) = 오차 함수(error function) = 목적 함수(objective function)

 

출처 : https://wikidocs.net/53560

- 다음과 같은 그림에서 점 4개를 가장 잘 표현하는 직선이 무엇인지 수학적 근거가 필요. -> '오차'라는 개념 도입

- 단순 '실제값 - 예측값'으로 정의한다면 여러 문제가 생김 -> 그럼 오차는 어떻게 정의할 것인가.

-> 각 오차들을 전부 제곱해준 후 더하는 방식(MSE) 사용. (물론 다른 방법도 많지만 여기선 평균 제곱 오차 설명)

- 제곱을 하여 더해주면 음수의 데이터에 영향을 받지 않고, 각 오차가 클수록 더 심각하게 좋지 않다는 가중치의 의미도 부여.

Cost(W,b)를 MSE로 위와 같이 정의한다면 훈련 데이터를 가장 잘 나타내는 직선은 Cost(W,b)를 최소로 만드는 직선이다.

 

옵티마이저 - 경사 하강법(Gradient Descent)

- 위에서 어떤 경우가 가장 훈련 데이터를 잘 나타내는 경우인지 정의함 (cost function을 정의)

- 그럼 이제 그 최적의 cost function을 어떻게 찾을 것인가? => 옵티마이저(Optimizer) 알고리즘

 

가장 기본적인 옵티마이저 알고리즘인 경사 하강법

우선 b를 고려하지 않고 y = Wx와 같은 기준으로 진행. 해당 선형 방정식에서 W는 기울기임.

위 그림서 주황색선은 W=20, 초록색선은 W=1일 때의 case.

-> 기울기가 너무 커도 오차가 커지고, 너무 작아도 오차가 커짐.

x축 : Weight, y축 : cost

=> 그럼 그 중간 어딘가에서 오차가 제일 작아지는 지점이 있을 것임. (위 사진 파란선 참조)

=> 함수의 극소점의 향해 가도록 해야함. -> 이것이 경사 하강법의 아이디어.

 

경사 하강법의 아이디어는 비용 함수를 미분하여 현재 W에서 접선의 기울기를 구하고, 접선의 기울기가 낮은 방향으로 W값을 변경하는 작업을 반복하는 것. 이 반복 작업서 특정 숫자 알파를 곱해 새로운 W로 사용하는 방식.

 

Case 1. 만일 기울기가 음수라면

=> W값이 증가하여 기울기가 0인 방향으로 조정 됨.

 

Case 2. 만일 기울기가 음수라면 

=> W값이 감소하여 기울기가 0인 방향으로 조정 됨.

 

여기서 '알파'라고 하는 곱해지는 특정 숫자는 학습률(learning rate)라고 한다. 이를 먼저 생각해보면 W에 gradient 값을 빼가며 반복 작업을 할 것인데, 어느 정도의 scale로써 점차 맞춰 나갈지에 대한 변수이다. 만일 너무 크다면 수렴하지 않고 발산하거나, 추후 특정 구간 내에서 반복한다고 하여도 이 구간이 충분히 작지 못할 수 있다. 만일 learning rate가 너무 작다면 수렴을 하는 것에 있어선 안정적일 수 있지만 학습에 시간이 많이 소요되고 불필요하게 리소스를 낭비할 수 있다. 이런 적당한 learning rate을 찾아내는 것도 중요할 것 같다.

 

파이토치로 선형 회귀 구현하기

필요한 라이브러리들을 import 한 후 추후 동일하게 실습을 하더라도 같은 결과 출력을 위해 random seed를 준다.

(110은 내 생일이다.)

 

Float type tensor에 train dataset을 만든 후 shape을 print해보자.

x train set과 y train set의 size는 모두 3X1임을 알 수 있다.

 

가중치와 편향의 초기화

선형 회귀는 학습 데이터와 가장 잘 맞는 하나의 직선을 찾는 일

그리고 가장 잘 맞는 직선을 정의하는 것이 바로 W와 b이다.

(여기서 하나 빠뜨린 점이 있는데 '가장 잘 맞는'이라는 것 또한 정의가 되어야 한다. 맥락상으론 MSE가 최소일때를 말한다.)

 

가중치 W를 0으로 초기화 하고 requires_grad=True가 인자를 통해 학습을 통해 값이 변경되는 변수임을 명시.

마찬가지로 편향 b도 0으로 동일하게 세팅.

현재 세팅에선 다음과 같은 직선이 표현되고 있으며 x에 어떤 값이 들어가도 0을 예측하게 됨.

 

파이토치 코드 상으로 직선의 방정식에 해당하는 가설 선언

 

가설(직선의 방정식)을 선언하였으니 비용 함수 또한 선언 필요.

다음과 같은 MSE를 나타내는 cost function을 선언.

(여기서 좋은 점. Summation의 과정이 필요 없이 각 torch에 대한 element-wise하게 뺄셈이 수행되고 각 element에 대해 square값이 적용된다. 또한 그렇게 만들어진 하나의 tensor(사실 아직도 array로 생각하는게 편하긴 하다)에 대해 mean값을 구해주게 되어 cost function의 value가 print out 되는 듯. 만일 이게 아니라면 for-loop를 통해 n에 대한 loop를 돌려주며 각 개체에 value를 update하는 식으로 했어야되지 않을까 ... 그렇지만 torch가 없었다면 np를 사용하면 torch와 사실상 동일하긴 했을듯)

 

지금까지 한 일.

x, y train set 정의, hypothesis 선언, cost function 선언.

train set의 dataset에 대해 현재의 방정식은 이정도의 loss를 가짐. 까지 왔다.

그럼 이제 이 크나큰 loss를 점차 이런 방법으로 개선하여 hypothesis를 수정하는 방식을 반복 필요.

 

경사 하강법 구현

SGD는 경사 하강법의 일종.

(찾아보니 BGD(배치 경사 하강법), SGD(확률적 경사 하강법), MGD(미니배치 경사 하강법)이 있다. 이 부분도 추가적 공부 필요)

출처 : https://bruders.tistory.com/91

 

학습 대상인 W와 b가 SGD의 입력이 되며 lr은 Learning Rate를 의미.

 

zero_grad()를 실행하여 미분을 통해 얻은 기울기를 0으로 초기화 한다는데 이부분은 뒤에서 추가 설명.

cost.backward() 함수 호출을 통해 가중치 W와 b에 대한 기울기가 계산 된다는데 어떤 의미인지 모르겠음.

(이 부분 추가 공부 필요. 참고 1. : https://hongl.tistory.com/158#google_vignette , 참고 2. https://deepdata.tistory.com/1166#google_vignette 이해한 바로는 computational graph에서 역전파(backpropagation) 관한 내용인 것 같은데 CS231n에서 나와서 이해가 조금 편했던 것 같다. 근데 우선은 가볍게 이정도가 있다 ~ 로만 알고 넘어가도록 하자. 알고보니 뒤쪽에서 별도 정리가 되어있다고한다.)

 

전체 코드

  1. 데이터 train set을 정의하고
  2. 기본 hypothesis를 W=0, b=0으로 세팅을 해두고
  3. optimizer에 대해 SGD의 method를 사용할 것이며, W와 b에 대해 최적화를 이룰 것이다. lr은 0.01로 할 것이다.
  4. 그 후 epoch를 설정해주며 얼마나 반복할 것인지를 정한다.
  5. for문에서 해당 횟수만큼 반복을 하며 점차 W와 b의 값을 변화시켜 나간다.

좌측부터 순서대로 lr = 0.01일때, 0.0001일때, 0.5일때

동일한 횟수의 epoch를 기준으로 lr을 다르게 설정함에 따라 최적화 되는 정도가 다르거나 아예 발산해버림을 알 수 있다.

이는 learning rate의 적당한 수치 조절이 중요함을 알 수 있다.

 

실 정답은 H(x) = 2x이므로 0.01일때 가장 근접한 정답을 알 수 있다.

(혹은 epoch를 1만회 이상으로 증가시키면 더 근접한 정답을 얻을 수 있다.)

 

optimizer.zero_grad()가 필요한 이유

미분값인 2가 계속 누적되는 것을 볼 수 있음.

따라서 zero_grad()를 통해 계속 0으로 초기화 시켜주어야함.

 

torch.manual_seed()를 하는 이유

 
좌측부터 순서대로 실행한 코드.
이는 random하게 난수를 발생시켜도 seed 값을 동일하게 유지하면 이전과 동일한 값을 얻을 수 있음.
 
 
 
 

자동 미분(Autograd) 실습

requires_grad = True가 적용된 텐서에 연산을 하면 계산 그래프가 생성된다 !

backward 함수를 호출하면 그래프로부터 자동으로 미분 계산

 

원래는 8이 나오는 과정은 다음과 같다.

각 변수에 대한 미분과 Chain Rule을 이용하면 다음의 순서를 따른다.

(이전에 CS231n 강의에서 다음과 비슷한 compuatational Graph가 있을 때 그냥 수학적 계산을 하면 안되는 것인가 물어본 학생이 있었는데 각 node끼리 연결된 과정에서 multiple or addition만 적용된 경우 단순 연산의 차원을 쭉 내려 연산하는 것이 수행 능력이 더 좋았다고 들었던 것 같다.)

 

 

다중 선형 회귀(Multivariable Linear Regression)

위에서 진행한 것은 모두 1개의 x에 의해 y를 추론하는 단순 선형 회귀 였음.

하지만 x가 1개가 아닌 여러개가 될 경우 사용하게 될 다중 선형 회귀에 대해 이해해보자.

다음과 같이 3개의 퀴즈 점수로부터 최종 점수를 예측하는 모델을 만들어보자.

hypothesis는 다음과 같을 것이다.

 

다음과 같이 필요한 것들을 import 한 후 train data들을 정의해준다. 그 후 가중치와 bias에 대해 0으로 초기화 해준다.

(TMI : 일상생활에서 초기화는 어떤 존재하는 값을 없애는 느낌이 크지만, CS에서는 '무언인가를 초기에 준비시키는 것'이라는 의미로 쓴다. 따라서 초깃값 세팅의 의미로 생각하면 된다.)

 

다음과 같이 선언한 후 1만회 반복을 통해 최적값을 찾는다.

 

(Q. 여기서부터 갑자기 궁금한 점이 생겼다. zero_grad()를 통해 누적값이 안 생기도록 계속 0으로 초기화까진 이해 되었다. 다만, backward()를 통해 어떤 작업을 하는 것인가 ? GPT한테 물어보니 각 변수 w1, w1에 grad 속성에 기울기 값을 저장한다고 한다. 그 후 step()단계에서 파라미터 업데이트를 진행한다고 한다. 여기까지도 순서에 대한 이해 자체는 되었다. 다만, Single Variable이 아닌 Multi variable일 때 과연 Cost Function의 Optimize는 어떻게 진행되는가. 각 cost function에 대한 편미분으로 grad값이 진행되어 각 파라미터들은 자신의 grad값에만 영향을 받는가 ? => GPT피셜 Yes. (만일 그렇다면 learning rate를 변수마다 다르게 지정하여 더 최적화 할 수 있지 않을까). 또한 자신의 grad값으로만 update가 된다면 과연 그것이 정말 옳은 방향으로서 업데이트가 된다고 생각할 수 있을까. 이는 다시 gradient의 정의까지 가게 되는 것 같아서 나중에 다시 공부해봐야될듯.
참고자료 : https://www.humanunsupervised.com/post/linear-regression-multivariate-cost-function-hypothesis-gradient

=> 크게 각 Weight에 대한 update 방향을 1. 그래프로, 2. computational graph로, 3. Cost function의 정의로 이해하였지만 1, 2는 이해 완료. 3은 나중에 다시 생각해보자.)

 

 

벡터와 행렬 연산으로 바꾸기

 

여기서 issue 발생. 현재는 3개라는 매우 적은 controlable한 개수이지만 이가 만일 100만개와 같이 늘어나게 된다면 우리는 하나하나 변수 선언과 다 해줄 것인가 ? no. 또한 컴퓨터적 연산 계산에 있어서도 비효율적.

 

1) 벡터 연산으로 이해

의 가설은

와 같이 이해할 수 있고 각 벡터를 X와 W로 표현한다면 H(X) = XW로 나타낼 수 있다.

 

2) 행렬 연산으로 이해

와 같이 나타낼 수 있다.

 

 

파이토치로 재구현

다음과 같이 train set 작성 및 출력.

Weight Matrix는 결국 최종적으로 5X1이 나와야되니 3X1 size여야하고

b도 5X1이어야되는거 아닌가 ? 라고 생각했지만 어차피 브로드캐스팅이 잘 되겠구나 싶었다.

 

실제로 b의 크기를 정의 해주지 않았을 때와 정의를 해주었을 때 다음과 같이 차이가 난다. 왜 ?

 

기존의 hypothesis에서는 단순 상수의 b이다. 이를 Matirx연산을 위해 사이즈를 맞춰준 것이지 사실은 동일한 하나의 상수이다.

만일 b를 (5,1)로 두게 된다면 각 equation에서 b가 각각 optimized 되게 된다.

그래서 그냥 size를 두지 않는 것이 의도에 부합한 방법인듯.

 

이제 train된 model을 사용하여 어떤 Input이 들어왔을 때 output을 출력해보자.

 

사실 python에서 with문을 처음 보았다.

(물론 항상 쓰는 건 SQL이도 내용이 완전히 다르지만 ,,)

 

GPT피셜

 

  • 컨텍스트 관리자(Context Manager): with 문은 파이썬의 컨텍스트 관리자를 사용할 때 쓰입니다.
  • 목적: 특정 블록 내에서 리소스의 초기화 및 해제를 자동으로 관리합니다.

구문

 

with 컨텍스트_관리자:
    코드_블록

 

예시

with open('file.txt', 'r') as f:
    data = f.read()

와 같다고 한다.

 

with torch.no_grad(): 이 블록 안에서 수행되는 모든 연산에 대해 역전파(즉, 기울기 계산)를 비활성화

예측을 할 때는 가중치를 업데이트할 필요가 없기 때문에, 메모리와 계산 자원을 절약하기 위해 torch.no_grad()를 사용하는 것이 좋음.

 

nn.Module과 클래스로 구현하기

pytorch에서는 이미 구현되어져 제공되는 함수들이 많이 존재. 해당 제공되는 함수들을 불러와 구현해보자.

 

단순 선형 회귀

2개의 값이 출력되는데 첫번째가 W값이고 두번째가 b에 해당된다.

두 값 모두 현재는 랜덤 초기화가 되어 있다. 또한, 두 값 모두 학습의 대상이기에 requires_grad=True가 되어있음.

 

그 후, 옵티마이저를 정의. model.parameters()를 사용하여 W와 b를 옵티마이저에 전달.

W값이 2에 가깝고, b값이 0에 가까우며 학습이 잘 된 것을 확인 가능.

 

forward vs backward 연산

- H(x)식에 입력 x로부터 예측된 y를 얻는 것 -> forward 연산

- 학습 전, prediction 은 x_train으로부터 예측값을 리턴하므로 forward 연산

- 학습 후, pred_y 는 임의의 값 new_var로부터 예측값을 리턴하므로 forward 연산

- 학습 과정에서 cost function을 미분하여 기울기를 구하는 것 backward 연산

- cost.backward()는 비용함수로부터 기울기를 구하라는 의미이며 backward 연산

 

다중 선형 회귀 구현

train dataset 정의 및 model 선언.

model.parameters를 list로 변환했을 때 length는 2개인데 앞에 것에 들어있는 3개의 수는 w1~w3이며, 뒤의 것은 b이다.

모두 랜덤 초기화가 되어있으며 학습값이기에 requires_grad=True로 되어있음.

 

(learning rate은 0.00001로 정의. 만일 0.01로 하게 된다면 발산함)

-> 그럼 이 발산의 기준을 미리 파악할 수는 없을까 ? 적당한 learning rate을 설정하는 것의 기준이 있으려나.

 

또한 이하의 코드는 단순 선형 회귀와 동일.

x에 임의의 입력 [73, 80, 75]를 넣어 모델이 예측하는 y값 출력.

사실 이 데이터는 학습 데이터에 포함되어있는 값임. 실제 값은 152였는데 예측값이 151에 가까운 값이 나온 것을 통해 어느 정도 잘 최적화 된 것으로 보임.

 

모델을 클래스로 구현하기

 

단순 선형 회귀 모델

대부분의 파이토치 구현체에서 위와 같이 클래스를 사용한 모델 구현 형식은 많이 사용하기에 꼭 숙지해두기 !

 

위 코드 설명

class형태의 모델은 torch.nn.Module을 상속 받음.

__int__() : 클래스의 생성자. 객체가 생성될 때 호출됨.

super().__init__() : 부모(torch.nn.Module)의 생성자를 호출하여 그 기능을 상속 받음.

self.linear = nn.Linear(1,1) : 선형 변환 정의.

forward() : 입력 데이터를 받아 모델의 연산을 수행.

 

다중 선형 회귀 모델

다음과 같이 구현.

 

다중 선형 회귀 클래스로 구현

기본 세팅 및 class로 다중 선형 회귀 구현.

 

MultivariateLinearRegressionModel라는 클래스는 pytorch의 nn.Module을 상속 받음.

클래스 초기화 메서드(__init__)에서 super()를 통해 부모 클래스의 nn.Module을 상속받으며 nn.Linear 객체를 생성하여 모델의 선형층을 설정함. 이는 input dim = 3, output dim=1로 설정되어있음.

 

코드는 이전과 다른 바가 없음.

 

옵티마이저 설정을 해주며, 여러 경사 하강법 중 확률적 경사 하강법인 SGD로 설정. 학습률 또한 적당히 작은 수로 설정.

 

에포크를 2,000으로 설정하며 그만큼 for-loop를 돌게 됨.

학습은 x_train을 사용하여 예측 값을 계산하는 것으로 시작 됨.

 

모델이 처음 만들어 질때 초기화 된 값들은 임의의 값들이지만 어차피 학습을 하며 점차 나아진다.

계속 똑같은 문제를 가지고 돌리며 더 근접한 prediction으로 수렴하게 되는 것이다.

 

코드 입장에선 이러한 임의의 수로 맨 처음 prediction을 진행하게 되고 실제 목표값인 y_train과 얼마나 차이가 나는지를 계산하게 된다. 이 오차는 MSE로 계산하게 된다.

모델은 cost function 기준으로 각 파라미터에 대한 기울기를 계산하여 파라미터 값들을 점차 업데이트 해나간다.

이를 통해 더 정확한 예측을 하게 된다.

 

더 정확한 예측을 위해선 epoch값을 늘리거나, Learning rate을 더 작게 설정해주며 (이 때 epoch값은 늘어나야한다.)하면 될듯.

 

미니 배치와 데이터 로더

이 부분은 선형 회귀에 한정되는 부분은 아니고 데이터를 로드하는 방법과 미니 배치 경사 하강법에 대해서 공부.

 

이전에 다중 선형 회귀에서 사용한 데이터의 샘플의 개수는 5개이다.

이를 하나의 행렬로 선언하여 전체 데이터에 대해 경사 하강법을 수행함.

 

근데 만일 학습 데이터가 엄청 커지게 된다면 이를 하나의 행렬로 만들어 경사 하강법을 진행할 수 있을까 ?

-> 계산량이 많아지며 매우 느리고, 메모리의 한계로 계산이 불가할 수 있다.

출처 :  https://wikidocs.net/55580

 

이러한 문제를 대체하기 위해 더 작은 단위로 쪼개 학습하는 개념 출현.

이 단위를 미니 배치(Mini Batch)라고 함.

 

러프하게 생각해봤을 때 미니 배치 : 작은 단위로 쪼개어 학습하기 때문에 메모리 할당에 이슈는 없을 듯. 만일 존재한다면 더 작은 단위로 쪼개면 되기 때문.

 

미니 배치 학습에서는 미니 배치의 개수만큼 경사 하강법을 수행해야 전체 데이터가 한 번 전부 사용되어 1 에포크가 됨.

미니 배치의 크기를 배치 크기(batch size)라고 함.

 

 

- 전체 데이터에 대해 한 번에 경사 하강법 수행 : 배치 경사 하강법

- 미니 배치 단위로 경사 하강법 수행 : 미니 배치 경사 하강법

+ 배치 크기는 보통 2 제곱수를 사용. CPU와 GPU의 메모리가 2의 배수이므로 배치 크기가 2의 제곱수일 때 데이터 송수신의 효율을 높일 수 있기 때문.

 

이터레이션(Iteration)

출처 : https://wikidocs.net/55580

위 그림은 에포크와 batch size와 iteration의 관계를 보여줌.

이터레이션은 한 번의 에포크 내에서 이루어지는 매개변수인 Weight W와 bias b의 업데이트 횟수

전체 size가 2,000인데 batch size를 200으로한다면 iteration의 수는 10개.

-> 이는 한 번의 에포크 당 매개변수 업데이트가 10번 이루어짐을 의미함.

(사실 Mini batch로 쪼개며 update를 어느 시점에 하는가에 대한 의문이 있었는데 위 내용으로 풀림.)

 

데이터 로드하기(Data Load)

파이토치에서는 데이터셋(Dataset)과 데이터 로더(Data Loader)를 제공.

-> 이를 통해 미니 배치 학습, 데이터 셔플(shuffle), 병렬 처리까지 간단히 수행 가능.

TensorDataset은 텐서를 입력으로 받음. 이를 입력으로 정의 후 dataset으로 저장.

 

DataLoader는 기본적으로 2개의 인자를 입력 받음. 하나는 데이터셋, 하나는 미니 배치의 크기.

+  추가로 많이 사용되는 인자는 shuffle. shuffle=True로 사용하면 Epoch마다 데이터셋을 섞어 학습되는 순서를 바꿈.

모델과 옵티마이저를 정의 후 훈련 실행.

Cost 값이 점차 작아지긴 하지만 충분히 작지 않다 생각하여 epoch값을 늘려 다시 실행.

(이때 주의해야할 점이 모델과 옵티마이저를 다시 정의 해주고 가야지 아니면 이전 모델에 추가로 학습하는 꼴이다.)

 

epoch를 2,000까지 늘린 후 실행했을 때의 결과. 그 후 임의의 값을 넣어 예측.

 

커스텀 데이터셋(Custom Dataset) & 커스텀 데이터셋으로 선형 회귀 구현

torch.utils.data.Dataset을 상속받아 직접 커스텀 데이터셋(Custom Dataset)을 만드는 경우도 있음.

Dataset을 상속받아 메소드들을 오버라이드 하여 커스텀 데이터셋을 만들어보자.

가장 기본적인 뼈대

위 코드는 Pytorch의 Dataset 클래스를 상속받아 사용자 정의 데이터셋 클래스를 만드는 방법.

해당 클래스는 두 가지 데이터 포함(x_data, y_data).

 

클래스가 초기화될 때, 이 두 데이터를 내부 변수로 저장. 데이터 셋의 길이를 반환하는 메서드가 정의되어 있음, 이는 해당 개수를 반환.

(이는 x_data에서 sample의 개수를 의미함.)

 

가장 중요한 메서드는 (__getitem__) 인덱스를 입력으로 받아 해당 인덱스에 맵핑된 데이터를 반환하는 것.

이 메서드는 x_data와 y_data의 특정 인덱스에 해당하는 데이터를 torch.FloatTensor 형식으로 변환하여 반환.

(기존에 __init__에서 넣은 것은 단순한 list type이었음.)

 

위 코드는 Customdataset 클래스를 인스턴스화 하여 데이터셋 객체(dataset)를 만듦.

이 데이터셋으로 Pytorch의 DataLoader 객체를 생성하며 기타 param 결정.

 

그 후, input dim = 3, output dim = 1인 선형 회귀 모델을 정의.

(갑자기 든 생각인데 output dimension이 2 이상이면 어떨까. 또한 어떤 예시가 있을까)

또한, 옵티마이저 설정. 항상 하던대로 SGD 사용.

nb_epochs = 20
for epoch in range(nb_epochs + 1):
  for batch_idx, samples in enumerate(dataloader):
    # print(batch_idx)
    # print(samples)
    x_train, y_train = samples
    # H(x) 계산
    prediction = model(x_train)

    # cost 계산
    cost = F.mse_loss(prediction, y_train)

    # cost로 H(x) 계산
    optimizer.zero_grad()
    cost.backward()
    optimizer.step()

    print('Epoch {:4d}/{} Batch {}/{} Cost: {:.6f}'.format(
        epoch, nb_epochs, batch_idx+1, len(dataloader),
        cost.item()
        ))

그 후 이전과 동일한 코드로 학습.

 

nb_epochs : 전체 학습 횟수.

해당 횟수만큼 반복문을 돌리며, 그 내무에서 enumerate(dataloader):에서는 미니 배치 단위로 데이터를 가져와 모델을 학습 시킴.

데이터 로더에서 가져온 각 배치는 samples라는 변수에 저장되고 이를 x_train과 y_train으로 분리한다. samples는 길이가 2인 list type을 갖기에 다음과 같이 잘 찢어줄 수 있다.

 

모델은 prediction을 생성하고 해당 값의 MSE로 cost를 계산한다. 계산된 cost 값으 기반으로 모델의 가중치를 업데이트 하기 위해 기울기를 초기화 하고 (zero_grad), 손실에 대한 역전파를 수행하고(backward), 파라미터를 업데이트 한다.(step).

 

해당 for-loop 돌아가는 내부 데이터를 좀 더 확인해보자.

 

(enumerate() 함수는 기본적으로 인덱스와 원소로 이루어진 튜플(tuple)을 만들어줌. batch_idx는 torch나 기타 세팅에 의해 생성되는 것이 아닌 enumerate에서 tuple의 앞단 값을 찢어 만든 python 내장 함수 값임. 또한, samples가 shuffle=True로 했기에 잘 섞여들어가는 것을 볼 수 있다. 이 부분 False로 해보고 실행도 해보길..)

 

 

벡터와 행렬 연산 복습하기

이 단원도 써야할까 생각을 해보았지만, 항상 배우는 자세로 겸손하자.라 생각하여 쓴다.

 

배운 것 : 기본적인 epoch, mini batch, shuffle, customdataset class, dataload 등

 

벡터와 행렬과 텐서

추후 머신 러닝의 입, 출력이 복잡해지며 3차원 텐서에 대한 이해가 필수로 요구됨. (ex. RNN)

 

numpy로 텐서 설명.

 

0차원, 1차원 텐서

스칼라 : 0차원 텐서, 0D tensor

ndim의 값에 주목. 이때 나오는 값이 축의 개수 or tensor의 차원이라고 부름. (이 내용 중요하답니다.)

혼동 주의할 부분. 위 벡터는 4차원이지만, tensor의 차원에선 1차원임. (텐서의 차원으론 [] 개수로 세는 것이 편할듯. 혹은 데이터 구조를 그냥 그대로 받아들이는 정도..)

주의 : 벡터의 차원은 하나의 축에 놓인 원소의 개수를 의미하는 것, 텐서의 차원은 축의 개수를 의미

 

2차원 텐서(행렬)

'행과 열'의 두 축이 존재하니 해당 텐서의 차원은 2차원.

shape(텐서의 크기)란 각 축을 따라서 얼마나 많은 차원지를 나타냄.

(큰 방향에서 가보면 3개의 list내에 4개의 데이터로 이루어져있기에 -> 3X4)

(만일 주로 3차원(텐서 dim기준)까지만 쓰인다면 matrix로 뻐팅겨도 괜찮을듯.)

 

3차원 텐서

! 이 부분 중요하니 잘 이해하고 넘어갈 것

자연어 처리(NLP)에서 자주 보게 되는 것이 다음의 3D tensor. 이는 시퀀스 데이터 표현서 자주 사용되기 때문.

예시로 3D는 (samples, timesteps, word_dim)이 된다.

추후 배치 개념에 대해서도 배울 때 (batch_size, timesteps, word_dim)과 같이 볼 수도 있다.

 

그 이상의 텐서

다음과 같다.

 

 

벡터와 행렬의 연산

다음과 같은 두 벡터가 (혹은 3X1 행렬이) 있다고 생각해보자.

해당 벡터에 대한 덧셈과 뺄셈 연산은 다음과 같이 element-wise하게 이루어진다.

 

행렬 또한 마찬가지다.

 

벡터의 내적과 행렬의 곱셈

내적은 dot product 혹은 inner product라고도 한다.

벡터 내적의 결과값으로는 스칼라 값이 나온다.

 

행렬의 곱셉은 딥 러닝을 이해하는 데에 필수적인 개념임으로 반드시 숙지 필요 !

추가로 다음의 두 가지 조건 또한 기억

  • 두 행렬의 곱 A × B이 성립되기 위해서는 행렬 A의 열의 개수와 행렬 B의 행의 개수는 같아야 한다.
  • 두 행렬의 곱 A × B의 결과로 나온 행렬 AB의 크기는 A의 행의 개수와 B의 열의 개수를 가진다.

(위 내용은 matrix의 dimension을 보며 생각하면 되겠다.)

 

다중 선형 회귀 행렬 연산으로 이해하기

다중 선형 회귀는 곧 2개 이상의 독립변수로부터 창출되는 종속 변수 1개를 예측하는 문제.

이를 행렬 연산으로 표현한다면 ?

다음과 같이 weight vector와 input vector에 대한 inner product + bias의 덧셈으로 이해할 수 있다.

 

이는 하나의 test case에 대한 얘기이고 만일 input(test set)이 많다면 ?

 

다음과 같이 얘기할 수 있다.

 

샘플(Sample)과 특성(Feature)

출처 : https://wikidocs.net/217269

Feature은 '특성'이라 생각하며 되고 독립 변수의 개수이다.

Sample의 수는 sample의 개수이다. (너무 당연한가)

 

가중치와 편향 행렬의 크기 결정

Weight Matrix와 input Matrix이 각각 몇 by 몇 이어야 하는 지에 대한 얘기이다.

너무 당연한 부분이라 넘어가도록..

 

추가로

입력 행렬과 출력 행렬의 크기로부터 가중치 행렬과 편향 행렬의 크기를 추정할 수 있다면, 딥 러닝 모델을 구현하였을 때 해당 모델에 존재하는 총 매개변수의 개수를 계산하기 쉽습니다. 어떤 딥 러닝 모델의 총 매개변수의 개수는 해당 모델에 존재하는 가중치 행렬과 편향 행렬의 모든 원소의 수이기 때문입니다.

보통 딥 러닝 모델의 매개변수 수로 AI 모델의 크기가 얼마나 크냐를 의미하고, 성능 또한 대략적으로 알 수 있다.

+ Recent posts