본문 바로가기

파이썬 & 머신러닝과 딥러닝

오차역전파법, 미분과 체인룰, 브로드캐스팅, 텐서 조작

다층 퍼셉트론

 

오차역전파법을 수행할 때

로스에 w를 바로 미분할 수 없기 때문에

(예측값, 정답값 상수 취급되어 0으로 미분됨)

그래서 '체인롤'(L <-> 업데이트할 w사이의 전개)를 사용해서 접선의 기울기를 구한다

 

 

오차역전파법(Backpropagation)이란?

오차역전파법은 신경망의 학습 과정에서,

모델의 예측과 실제 정답 간의 오차(손실 또는 로스)를 기반으로 가중치 w를 업데이트하는 방법

이 방법은 신경망의 각 가중치에 대해 손실 함수의 기울기(미분값)를 계산하고,

이를 사용해 가중치를 조정하는 과정에서 매우 중요한 역할을 한다.

 

미분과 체인룰

1. 미분:

미분은 함수의 기울기, 즉 함수 값이 얼마나 변하는지를 나타낸다.

신경망에서는 손실 함수 을 가중치 에 대해 미분하여,

가중치가 변할 때 손실이 어떻게 변하는지를 알아내고 이를 기반으로 가중치를 조정한다.

2. 체인룰(Chain Rule):

체인룰은 합성 함수의 미분을 계산하는 규칙

신경망은 여러 층의 뉴런으로 구성되어 있기 때문에,

출력에서부터 입력까지 각 층을 거쳐 미분을 계산할 필요가 있다.

체인룰을 사용하면 복잡한 함수의 미분을 층별로 순차적으로 계산할 수 있다.

 

문제 설명

로스(Loss)에 대해 가중치 ww를 바로 미분할 수 없다는 설명은

신경망이 여러 층으로 구성되어 있기 때문에,

단순히 로스 L을 가중치 w에 대해 직접적으로 미분하는 것이 아니라는 의미

로스 함수는 출력층의 결과를 기반으로 계산되지만,

가중치 w는 중간층 또는 입력층에 존재할 수 있다.

따라서, 로스 L과 가중치 w 사이에 여러 중간 함수가 있기 때문에

체인룰을 사용해 로스를 가중치까지 연결하여 미분해야 한다.

체인룰 적용 예시

예를 들어, L은 네트워크의 출력 y에 따라 달라지고, y는 가중치 w에 따라 달라진다:

이 경우, w에 대한 L의 미분은 체인룰에 따라 다음과 같이 계산:

즉, 로스 Lw에 대해 직접적으로 미분할 수 없으므로,

먼저 Ly에 대해 미분한 후, 를 다시 에 대해 미분해야 한다.

이를 통해 가 로스에 미치는 영향을 계산할 수 있습니다.

요약

  • 직접 미분 불가: 로스를 가중치에 대해 바로 미분할 수 없는 이유는 로스와 가중치 사이에 여러 층(함수)이 있기 때문
  • 체인룰 사용: 체인룰을 통해 각 층을 따라 미분을 연쇄적으로 계산하여 최종적으로 가중치에 대한 로스의 미분을 구함.
  • 오차역전파법: 이 방법을 통해 계산된 미분값을 사용해 가중치를 업데이트하는 것이 오차역전파법

이렇게 하면 신경망의 학습이 이루어지며, 예측 성능이 점점 향상된다.

 


 

 

브로드캐스팅의 기본 개념

브로드캐스팅(Broadcasting)은 텐서 연산에서

서로 다른 크기의 텐서 간의 연산을 가능하게 해주는

PyTorch 및 다른 라이브러리에서 사용하는 강력한 기능

브로드캐스팅을 통해 서로 다른 크기의 텐서 간의 수학적 연산을 수행할 수 있으며,

이를 통해 코드의 간결함과 효율성을 높일 수 있다.

 

브로드캐스팅은 기본적으로 작은 텐서를 더 큰 텐서의 형태로 확장하여 연산을 수행

이 과정에서 작은 텐서는 자동으로 더 큰 텐서와 호환되는 형태로 복제되거나 확장된다.

브로드캐스팅은 텐서의 차원과 크기에 따라 서로 맞지 않는 텐서들이 일치하도록 변환한다.

 

브로드캐스팅 규칙

  1. 텐서 차원 수: 두 텐서의 차원 수가 다를 경우, 작은 차원의 텐서가 큰 차원의 텐서에 맞게 차원을 추가해야 함. 예를 들어, 1차원 텐서와 2차원 텐서 간의 연산을 수행할 때, 1차원 텐서가 2차원 텐서에 맞게 확장된다.
  2. 차원 크기: 두 텐서의 차원 크기가 서로 다를 경우, 차원 크기가 1인 쪽이 다른 쪽의 차원 크기에 맞게 확장된다. 만약 차원 크기가 1이 아닌 경우에는 브로드캐스팅이 불가능하다.
  3. 정렬: 두 텐서의 마지막 차원부터 시작하여 차원 크기를 비교한다. 각 차원에서 두 텐서의 크기가 같거나, 하나의 텐서의 크기가 1인 경우, 브로드캐스팅이 가능하다고 봄.

 

예제

 

1. 스칼라와 텐서의 브로드캐스팅

import torch

tensor = torch.tensor([[1, 2, 3], [4, 5, 6]])
scalar = 10

result = tensor + scalar
print(result)
# Output:
# tensor([[11, 12, 13],
#         [14, 15, 16]])

 

→ 이 경우, 스칼라 값 10이 텐서의 각 원소에 더해지며, 스칼라 값이 자동으로 텐서의 형태로 확장되어 연산이 수행

 

 2. 1차원과 2차원 텐서의 브로드캐스팅

import torch

tensor_2d = torch.tensor([[1, 2, 3], [4, 5, 6]])
tensor_1d = torch.tensor([10, 20, 30])

result = tensor_2d + tensor_1d
print(result)
# Output:
# tensor([[11, 22, 33],
#         [14, 25, 36]])

 

  tensor_1d가 tensor_2d의 각 행에 대해 자동으로 확장되어 연산이 수행

 

3.  2차원 텐서와 3차원 텐서의 브로드캐스팅

import torch

tensor_2d = torch.tensor([[1, 2, 3], [4, 5, 6]])
tensor_3d = torch.tensor([[[10, 20, 30]], [[40, 50, 60]]])

result = tensor_2d + tensor_3d
print(result)
# Output:
# tensor([[[11, 22, 33],
#         [14, 25, 36]],
#        [[44, 55, 66],
#         [48, 59, 70]]])

 

  tensor_2d가 tensor_3d의 첫 번째 차원에 맞게 자동으로 확장되어 연산이 수행

 


torch.argmax

  • 기능: 주어진 텐서에서 가장 큰 값을 가지는 인덱스를 반환
  • 사용 예시: torch.argmax(t, dim=1) ← dim = 1 은 행 기준, dim = 0은 열 기준
  • 출력: 텐서의 특정 차원에서 가장 큰 값의 인덱스들을 담고 있는 텐서를 반환

torch.max

  • 기능: 주어진 텐서에서 가장 큰 값을 반환하며, 추가적으로 각 차원에 대한 인덱스도 반환할 수 있다.
  • 사용 예시: torch.max(t, dim=1)
  • 출력: 주어진 텐서에서 가장 큰 값과, 해당 값의 인덱스를 포함한 두 개의 텐서를 반환

→ classification에 사용된다.


 

view 메서드

  • 목적: 텐서의 형상을 변경하여 새로운 형태의 텐서를 생성\. 이 과정에서 원본 데이터는 변경되지 않는다.
  • 중요한 점: 텐서의 데이터는 메모리에서 같은 위치를 공유하므로, 새로운 뷰를 통해 데이터를 변경하면 원본 텐서에도 영향을 미친다.

텐서 조작에서 "뷰(view)"는 텐서의 데이터를 수정하지 않고 다른 형태로 변환할 수 있는 방법을 제공한다. view는 PyTorch에서 텐서의 형상을 변경하는 데 사용되는 중요한 메서드이고 텐서의 데이터를 복사하지 않고 새로운 뷰를 생성하므로 메모리 효율성을 유지하면서 텐서의 형태를 조정할 수 있다.

 

사용 방법

  1. 형상 변경: view 메서드를 사용하여 텐서의 차원이나 크기를 변경할 수 있다. 변경된 텐서는 원본 데이터와 같은 메모리 위치를 공유한다.
  2. 형상 일치: 변경하려는 텐서의 총 원소 수는 변경 후의 형상에서의 총 원소 수와 일치해야 한다.

 

예제

  1. 1D 텐서를 2D 텐서로 변환하기
import numpy as np
import torch

# NumPy 배열 생성
t = np.array([[[0, 1, 2],
               [3, 4, 5]],
              [[6, 7, 8],
               [9, 10, 11]]])

# NumPy 배열을 PyTorch 텐서로 변환
ft = torch.FloatTensor(t)

print(ft.shape)

 

# 3차원 텐서에서 2차원 텐서로 변경
print(ft.view([-1, 3])) # ft라는 텐서를 (?, 3)의 크기로 변경
print(ft.view([-1, 3]).shape)

→ ft.view([-1, 3])는 ft 텐서를 두 번째 차원의 크기가 3인 2차원 텐서로 변경 (-1은 자동으로 계산되는 크기를 의미)

 

# 3차원 텐서의 크기 변경
print(ft.view([-1, 1, 3]))     # ? x 1 x 3 에 맞춰라, ?는 맞게 크기를 알아서 지정해라.
print(ft.view([-1, 1, 3]).shape)

→ 첫 번째 차원의 크기 -1은 자동으로 계산된다.

원본 텐서의 총 원소 수는 12개이며,

새로운 형상에서 두 번째 차원의 크기와 세 번째 차원의 크기는 각각 1과 3

따라서, 첫 번째 차원의 크기는 12 / (1 * 3) = 4로 계산

 


 

스퀴즈(squeeze) : 1인 차원을 제거하여 텐서를 더 간결하게 만들어 준다.

 

스퀴즈 (squeeze) 사용 방법

  • 목적: 텐서에서 크기가 1인 차원을 제거하여 텐서의 차원을 축소
  • 중요한 점: 스퀴즈를 사용해도 데이터의 메모리 위치는 변경되지 않으므로, 스퀴즈한 텐서와 원본 텐서는 같은 데이터를 참조한다.

스퀴즈(squeeze)는 PyTorch에서 텐서의 차원 중 크기가 1인 축을 제거하여 텐서를 더 간결하게 만드는 방법.

이 과정에서 텐서의 데이터는 수정되지 않으며, 새로운 형태의 텐서를 생성한다.

squeeze 메서드는 데이터의 메모리 위치를 변경하지 않고, 원본 텐서와 동일한 메모리 위치를 공유한다.

 

예제

ft = torch.FloatTensor([[0], [1], [2]])
print(ft)
print(ft.shape)

print(ft.squeeze())      # 2차원에서 1차원으로 줄여라
print(ft.squeeze().shape)

 

  • 원본 텐서 ft의 형상은 (3, 1) → 이 텐서는 2차원
  • ft.squeeze()를 호출하면, 차원 중 크기가 1인 차원이 제거된다. 결과적으로 형상은 (3,)으로 변경됨.
  • 스퀴즈(squeeze)는 1인 차원을 제거하여 텐서를 더 간결하게 만들어 준다.

 

→ 언스퀴지(Unsqueeze) - ★특정 위치★에 1인 차원을 추가한다.


]<학습 프로세스>

전처리 → 다층퍼셉트론 → 성능 측정 → 성능 강화 → 풀프로세스 실습


전처리 - 리스케일의 중요성

1) 파생변수 2) 리스케일링 3) 여러 Kfold

큰 숫자를 넣을 시, 시그모이드 계산값은 0에 가까운 값이나 1에 가까운 값으로 수렴

오차역전파 과정에서 계산된 기울기 역시 매우 작어지며, 이는 가중치 업데이트를 거의 불가능 (기울기 소실 현상)

 

다층퍼셉트론

1) 레이어 쌓기

입력데이터에서 점점 더 복잡한 특징을 추출 및 학습

초기 레이어들은 데이터의 저수준 특징(이미지에서 엣지, 색깔) 을 추출하고

깊은 레이어로 갈 수록 고수준의 복잡한 특징(이미지에서 특정 사물의 윤곽, 형태 등)을 추출

 

2) 히든 사이즈

: 레이어당 퍼셉트론 개수

가중치가 많아지기 때문에 더 복잡한 특징을 추출 및 학습

 

3) 활성화 함수 종류

  • 시그모이드, 하이퍼볼릭 탄젠트는 입력값이 매우 크거나 작을 때, 출력값이 0에 가까워져서 기울기가 매우 작아지는 Gradient Vanishing 문제 (기울기가 소실되어 학습이 안되는 현상 발생 가능 -> ReLU와 다양한 활성화 함수 사)
  • ReLU는 양수 입력에 대해 미분값이 1이기 때문에, 깊은 네트워크에서도 기울기 소실 없이 효과적으로 학습 진행

 

4) 옵티마이저 종류

<1> 확률적 경사하강법

: 딱 한개의 샘플을 무작위로 선택하고 그 하나의 기울기를 계산

(빠르고 가벼울 수 있지만 Local Minima에 빠질 가능성이 높다.)

 

<2> 미니 배치 경사하강법

: Train data의 일부 세트로 기울기를 계산

 

<이외> GD, SGD, Momentum, Adagrad, NAG, RMSProp, AdaDelta, Nadam, Adam

Adam - NAdam, RAdam, AdamW

Adam 은 기울기를 구할때 규제를 구하는 수식이 있는데, 기울기를 계산할 때 규제도 같이 계산하면 역전파가 잘 안된다는 문제가 있다.

AdamW는 기울기를 구하는 텀과 규제를 구하는 텀을 따로 계산해서 구하는 수식으로 개선하였다.

 

5) 학습률 decay

: 옵티마이저의 보폭(학습률) 학습마다 조절 가능하다.

 

스케줄러는 학습이 진행됨에 따라 학습률을 조절하여 더 나은 학습 성능을 얻도록 도와줌.

# 옵티마이저 설정

: Stochastic Gradient Descent with Momentum (SGD) optimizer = optim.SGD(model.parameters(), lr=0.001)

# 학습률 스케줄러 설정

: StepLR scheduler = torch.optim.lr_scheduler.StepLR(optimizer, step_size=2, gamma=0.1)

 

6) Early stop, Tqdm, Log

 

 

성능측정

1) 과학습 판단

 

 

성능강화

 

 

과적합 방지

1) Weight 초기화 2) 배치노말라이제이션 3) 드롭아웃 4) 규제

 

 

풀프로세스 실습


■ 전처리
1) 파생변수: kmean 파생변수
2) 리스케일링 : MinMax , Robust(이상치에 강함), Standardization(이상치에 강함)
3) Kfold, stratified kfold(클래스 비율에 따라 validation를 선별) , Time Serise(시간을 늘려가면서 validation을 선별)■  다층퍼셉트론
1) 레이어 쌓기: 모델을 데이터에 대해서 패턴을 더 잘 학습시키기위해, 단 과도하면 오버피팅
2) 히든 사이즈: 모델을 데이터에 대해서 패턴을 더 잘 학습시키기위해, 단 과도하면 오버피팅
3) 활성화함수 종류: 시그모이드(기울기소실 발생시킬 수 있음 → 학습이 안되는 현상), 그래서 나온게 ReLU와 다양한 활성화 함수.
4) 옵티마이저 종류: SGD(수렴속도 빠름, 다만 학습이 잘못될 가능성 O) , Adam과 AdamW, NAdam, Sophia 등 다양한 옵티마이저 생김.
5) 학습률 decay: 학습을 할 때마다 learning rate(보폭)을 줄여나가서, loss 곡선에서 튕겨져 나가는걸 방지.
6) Early stop: validation score가 n번 이상 개선되지 않을때, 학습을 멈추는 방법■ 성능측정
1) 과학습 판단: 오버피팅(학습데이터에 대해서 성능 ↑, validation 성능은 ↓)
               언더피팅(학습데이터에 대해서 성능 ↓, validation 성능은 ↓)
    최적피팅(학습데이터에 대해서 성능 ↑, validation 성능은 ↑)
■ 과학습 방지
1) Weight 초기화: 르쿤 초기화, 자비에 초기화, He 초기화 방법
2) 배치노말라이제이션: 모델 학습시 순전파(feed forward)시에 레이어 마다 계산되어 나오는 아웃풋 값의 분포를 치우쳐져 있는걸 zero centering하여
                               안정적인 분포로 만들기 위해, ※ shift 된 아웃풋 값의 분포는 뒤에 있는 레이어에서 활성화 함수 값이 1, 0으로 수렴시켜버림
3) 드롭 아웃: 노드(퍼셉트론)가 골고루 학습하게끔 매 학습마다 레이어의 노드를 랜덤하게 Off하는 기법
4) 규제: 모델이 학습을 할때, 데이터에 대해서 너무 과학습을 하지 않도록 제한 시키는 기법