머신러닝 교과서_파이토치편

[머신러닝 교과서] 1. 퍼셉트론과 아달린(완전 배치 경사하강법, 미니 배치 경사하강법)

cheorish 2024. 12. 8. 20:12

퍼셉트론이란?

  • 인간의 뇌신경 안에 존재하는 뉴런을 본따 만든 인공신경망의 기초 모델

출처 : https://compmath.korea.ac.kr/deeplearning/Perceptron.html

다수의 신호를 입력 받아 하나의 신호로 출력을 하는 구조, 인간의 뉴런 구조 중 수상돌기나 축색돌기 처럼 신호를 전달하는 역할 또한 퍼셉트론 안에 존재하는데, 그것을 가중치(Weight)라고 한다.

그렇게 입력 값과 가중치 값의 선형 조합을 통해, 결정 함수를 정의하는데, 입력 값이 정해진 임계값(θ; theta,세타)에 도달한다면 1, 도달하지 못한다면 0으로 예측한다. 이러한 퍼셉트론 알고리즘에서 결정 함수는 단위 계단 함수를 변형한 것이다.

출처 : https://cs231n.github.io/neural-networks-1/

생김새도 마치 인간의 뉴런과도 비슷하다.

그렇다면 여기서 뉴런과 퍼셉트론이 각각 어떤 부분이 유사한지 표로 나타내보겠다.

  • 퍼셉트론과 인간의 뉴런의 차이
생물학적 뉴런 인공 뉴런 설명
세포체 (Cell Body) 노드 (Node) 뉴런의 중심 부분으로, 입력 신호를 처리하고 출력 신호를 결정
가지돌기 (Dendrite) 입력 (Input) 다른 뉴런으로부터 신호를 받아들이는 역할을 함
시냅스 (Synapse) 가중치 (Weight) 뉴런 간의 연결부로, 신호 강도(중요도)를 조정하는 역할을 함
축삭 (Axon) 출력 (Output) 뉴런의 출력을 전달하는 부분으로, 다음 뉴런이나 근육, 기관으로 신호를 보냄

퍼셉트론의 연산 과정

  1. 선형 결합
    입력 값 (\(x_i\))과 가중치 (\(w_i\))의 선형 결합을 계산함:
    $$
    y = \sum_{i=1}^n w_i x_i + b
    $$

- \(x_i\): \(i\)번째 입력 값
- \(w_i\): \(i\)번째 가중치
- \(b\): 편향 (bias)

  1. 활성화 함수 적용
    계산된 \(y\) 값에 활성화 함수를 적용하여 최종 출력을 얻는다. 활성화 함수는 주로 **단위 계단 함수(Step Function)**를 사용함:
    $$
    f(y) =
    \begin{cases}
    1, & \text{if } y \geq 0 \\
    0, & \text{if } y < 0
    \end{cases}
    $$
  2. 전체 계산식
    결과적으로 퍼셉트론의 연산 과정은 다음과 같이 표현된다:
    $$
    \text{Output} = f\left( \sum_{i=1}^n w_i x_i + b \right)
    $$

활성화 함수

  • 신경망의 핵심이 되는 요소로, 입력 신호의 총합을 얼만큼의 출력 신호로 변화할지에 대한 결정을 내리는 함수
  • 대표적인 퍼셉트론의 활성화 함수로는 계단함수가 있음.

출처 : https://ncs10042.tistory.com/73

주요 특징

  • 입력 값이 임계값(0)을 넘기면 1, 그렇지 않으면 0으로 출력
  • 단순 이진분류(AND, OR 게이트는 설명이 가능함)
  • 그렇기에 임계값 지점에서 미분이 불가능 → 추후에 그래디언트 배니싱이 발생 및 XOR 게이트에서 자세하게 다룰 예정
  • 퍼셉트론 결론

출처 : https://thebook.io/080311/0051/


파이토치를 통한 퍼셉트론 구현

import numpy as np 

class Perceptron:
    """
    eta : float → 학습률 
    n_iter : int → 훈련 데이터셋 반복 학습 (에포크)
    random_state : int → 가중치 무작위 초기화 난수 생성기 

    w_ : 1d_array → 가중치 
    _b : 스칼라 → 학습된 절편 유닛(편향)
    errors_ : list → 에포크마다 누적된 분류 오류 
    """
    def __init__(self, eta=0.01, n_inter=50, random_state=1):
        self.eta = eta
        self.n_iter = n_iter
        self.random_state = random_state
    def fit(self, x, y):
        rgen = = np.random.RandomState(self.random_state)
        self.w_ = rgen.normal(loc=0.0, scale=0.01,
                                size=X.shape[1])
        self.b_ = np.float(0.)
        self.errors_ = [] 

        for _ in range(self.n_iter):
            errors = 0 
            for xi, target in zip(X, y):
                update = self.eta * (target - self.predict(xi))
                self.w_ += update * xi
                self.b_ += update
                errors += int(update != 0.0)
            self.errors_.append(errors)
        return self

    def net_input(self, X):
        """
           입력 계산
        """
        return np.dot(X, self.w_) + self.b_

    def predict(self, X):
        """
        계단 함수를 사용하고 난 뒤, 클래스 레이블 반환     
        """
        return np.where(self.net_input(X) >= 0.0, 1, 0)
  • 여기서 우리가 알아야 할 것은
n_iter # 반복 횟수 
eta # 학습률 
  • 퍼셉트론 구현시 학습률과 에포크 횟수(훈련 데이터셋을 반복하는 횟수)를 사용하여 새로운 퍼셉트론 객체를 반환한다는 것이다.
  1. fit 메서드에서 절편 self.b_를 0으로 초기화 하고 self.w_ 가중치를 벡터로 초기화한다
  2. 초기 가중치 벡터상기 변수 선언을 통해 표준편차가 0.01인 정규 분포에서 뽑은 랜덤한 작은 수를 담고 있다.
  3. rgen.normal(loc=0.0, scale=0.01, size=X.shape[1])

rgen.normal?

•    rgen은 np.random.RandomState로 생성된 난수 발생기이다. 이를 통해 재현 가능한 랜덤 값을 생성한다.
•    rgen.normal은 정규 분포에서 난수를 생성한다.
•    loc=0.0: 평균(Mean)이 0.
•    scale=0.01: 표준편차(Standard Deviation)가 0.01. 즉, 평균 0을 중심으로 매우 작은 값들을 생성함
•    size=X.shape[1]: 입력 데이터 X의 특성 개수에 맞는 길이의 가중치 벡터를 생성한다. 
  • fit 메서드를 통해 가중치를 초기화 시킨 후, 훈련 데이터셋에 있는 모든 개개의 샘플을 반복 순회 하면서 퍼셉트론 학습규칙에 따라, 가중치를 업데이트함.
  • 또한 fit에서 에포크마다 erorr 값을 담는 erorrs 리스트가 있는데, 잘못 분류된 횟수를 기록하는 기능이라고 생각하면 쉽다.
    추후, 훈련하는 동안 얼마나 예측을 잘 수행했는지 비교해보기 위한 수치값이라고 생각하면 된다.
  • predict 메서드는 그렇게 학습된 fit 메서드를 통해 클래스 레이블(정답)을 예측한다.

아달린 알고리즘과 경사하강법

여기서 경사 하강법이라는 개념이 나오는데

경사 하강법이란?

  • 학습을 진행하는데 있어서, 함수의 최솟값(Loss 값 최소화) 를 찾기 위해 사용되는 최적화 알고리즘
  • 입력 값에 대한 손실 함수의 기울기(그래디언트)를 계산하여, 최적의 가중치(파라미터)를 찾아가는 것이 특징!

출처 : https://velog.io/@sset2323/05-03.-%EB%B9%84%EC%9A%A9-%EC%B5%9C%EC%86%8C%ED%99%94%ED%95%98%EA%B8%B0-%EA%B2%BD%EC%82%AC-%ED%95%98%EA%B0%95%EB%B2%95Gradient-Descent-%EC%86%8C%EA%B0%9C

경사 하강법의 기본 원리

경사 하강법은 손실 함수 - \( J(w) \)의 최솟값을 찾기 위해, 기울기(Gradient)를 이용해 가중치를 업데이트하는 알고리즘임.

1. 손실 함수 정의

손실 함수 - \( J(w) \)는 모델의 예측 값과 실제 값 간의 오차를 나타내는데. 일반적으로 다음과 같은 형태를 가짐:
$$
J(w) = \frac{1}{n} \sum_{i=1}^n L(y_i, \hat{y}_i)
$$

- \( n \): 데이터의 개수
- \( y_i \): 실제 값
- \( \hat{y}_i \): 예측 값
- \( L(y_i, \hat{y}_i) \): 예측 값과 실제 값 간의 오차 (예: 평균 제곱 오차)

2. 그래디언트 계산

손실 함수 - \( J(w) \)를 가중치 - \( w \)에 대해 편미분하여 기울기를 계산함:
$$
- \nabla J(w\) = \frac{\partial J(w)}{\partial w}
$$

  • - \(\nabla J(w)\): 손실 함수 - \( J(w) \)의 기울기(Gradient)

3. 가중치 업데이트

기울기의 반대 방향으로 가중치를 업데이트하여 최솟값에 점차 가까워짐:
$$
w \leftarrow w - \eta \cdot \nabla J(w)
$$

  • - \( \eta \): 학습률 (Learning Rate)
  • - \( w \): 현재 가중치

4. 반복 (Iterative Process)

위 과정을 기울기가 0에 가까워질 때까지 반복함:

  1. 현재 가중치 - \( w \)에서 손실 함수 - \( J(w) \)의 그래디언트 계산.
  2. - \( w \)를 업데이트.
  3. - \( J(w) \)가 수렴할 때까지 반복.

전체 알고리즘 요약

$$
w_{t+1} = w_t - \eta \cdot \nabla J(w_t)
$$

  • -\( w_t \): 현재 시점 -\( t \)에서의 가중치
  • -\( w_{t+1} \): 다음 시점 -\( t+1 \)에서의 가중치

경사하강법의 종류

경사 하강법은 데이터 처리 방식에 따라 다음 세 가지로 나눌 수 있음

1. 완전 배치 경사 하강법 (Batch Gradient Descent)

  • 전체 데이터셋을 사용하여 손실 함수의 그래디언트를 계산하고 가중치를 업데이트함:
    $$
    w \leftarrow w - \eta \cdot \frac{1}{n} \sum_{i=1}^n \nabla J(w; x_i)
    $$
  • - \( w \): 가중치 벡터
  • - \( \eta \): 학습률 (Learning Rate)
  • - \( n \): 데이터의 총 개수
  • - \( x_i \): - \(i\)-번째 데이터 샘플
  • - \( \nabla J(w; x_i) \): 손실 함수의 그래디언트

2. 확률적 경사 하강법 (Stochastic Gradient Descent, SGD)

  • 단일 데이터 샘플을 사용하여 손실 함수의 그래디언트를 계산하고 가중치를 업데이트함:
    $$
    w \leftarrow w - \eta \cdot \nabla J(w; x_i)
    $$
  • - \( x_i \): 랜덤으로 선택된 데이터 샘플
  • 나머지 변수는 배치 경사 하강법과 동일.

3. 미니배치 경사 하강법 (Mini-Batch Gradient Descent)

  • 데이터를 작은 배치(Batch)로 나누어 배치 단위로 손실 함수의 그래디언트를 계산하고 가중치를 업데이트함:
    $$
    w \leftarrow w - \eta \cdot \frac{1}{B} \sum_{i=1}^B \nabla J(w; x_i)
    $$
  • - \( B \): 배치 크기 (Batch Size)
  • - \( x_i \): 배치에 포함된 데이터 샘플

비교

  • 완전 배치 경사 하강법: 계산 정확도가 높지만 느림.
  • 확률적 경사 하강법: 빠르지만 수렴 경로가 불안정.
  • 미니배치 경사 하강법: 두 방식의 절충안으로, 계산 효율성과 수렴 안정성을 모두 제공.

완전 배치 경사법의 대표적인 알고리즘인 아달린 알고리즘 구현 코드

import numpy as np

class AdalineGD:
    """
    아달린(Adaline) 구현 클래스: 완전 배치 경사 하강법 사용.

    Parameters:
    -----------
    eta : float
        학습률 (Learning Rate).
    n_iter : int
        훈련 데이터셋 반복 횟수 (에포크 수).
    random_state : int
        난수 생성 시드 (가중치 초기화용).

    Attributes:
    -----------
    w_ : 1d-array
        학습된 가중치 벡터.
    b_ : float
        학습된 절편 (bias).
    losses_ : list
        에포크마다 평균 제곱 오차 손실값(MSE)의 변화 기록.
    """
    def __init__(self, eta=0.01, n_iter=50, random_state=1):
        self.eta = eta
        self.n_iter = n_iter
        self.random_state = random_state

    def fit(self, X, y):
        """
        훈련 데이터에 기반하여 모델 학습.

        Parameters:
        -----------
        X : {array-like}, shape = [n_samples, n_features]
            입력 데이터 (샘플 수 x 특성 수).
        y : array-like, shape = [n_samples]
            목표 값 (실제 값).

        Returns:
        --------
        self : object
        """
        rgen = np.random.RandomState(self.random_state)
        self.w_ = rgen.normal(loc=0.0, scale=0.01, size=X.shape[1])
        self.b_ = 0.0
        self.losses_ = []

        for _ in range(self.n_iter):
            net_input = self.net_input(X)
            errors = (y - net_input)
            self.w_ += self.eta * X.T.dot(errors) / X.shape[0]  # 가중치 업데이트
            self.b_ += self.eta * errors.mean()  # 절편 업데이트
            loss = (errors**2).mean() / 2.0  # 평균 제곱 오차 계산
            self.losses_.append(loss)

        return self

    def net_input(self, X):
        """입력 계산: 선형 결합 결과 반환."""
        return np.dot(X, self.w_) + self.b_

    def predict(self, X):
        """예측 결과 반환."""
        return self.net_input(X)
  • 코드 설명
        1. 클래스 정의 (AdalineGD)
            • eta: 학습률(얼마나 빠르게 가중치를 업데이트할지 결정).
            • n_iter: 에포크 수(전체 데이터셋 반복 횟수).
            • random_state: 재현 가능한 난수 생성을 위한 시드 값.
        2. fit 메서드
            • 데이터를 입력받아 가중치와 절편을 학습.
            • 완전 배치 경사 하강법을 사용하여 모든 데이터 샘플에 대한 평균 그래디언트를 계산 후 업데이트.
            • 매 에포크마다 손실(MSE)을 계산하고 저장.
        3. net_input 메서드
            • 선형 결합 계산 ( w^T x + b ).
        4. predict 메서드
            • 모델이 예측한 값을 반환.

아달린 알고리즘이란?
아달린(Adaline)은 1960년대 버나드 위드로(Bernard Widrow)테드 호프(Ted Hoff)가 개발한 인공 신경망 모델로, 퍼셉트론의 개선된 버전

주요 특징

  1. 선형 뉴런 모델
    • 입력 데이터와 가중치의 선형 결합을 기반으로 작동
    • 퍼셉트론과 달리 활성화 함수 이전에 손실 함수 계산에 연속적인 출력 값을 사용함!(기존 퍼셉트론은 연속적인 값을 사용하지 않았음)
  2. 손실 함수
    • 출력과 실제 값 간의 오차를 측정하기 위해 평균 제곱 오차를 사용함
  3. 학습 알고리즘
    • 가중치 업데이트는 완전 배치 경사법 을 사용하여 모든 데이터 샘플에 대한 그래디언트(기울기)를 계산한 후 업데이트를 함

** 아달린의 핵심 아이디어**

  • 연속 적인 출력값을 통해 정교한 학습

출처 : https://benghak.github.io/2020-01-20-ML_chapter_02_03/


별책부록

학습률이란?

  • 학습률은 한 번의 업데이트 중 얼마나 많이 이동할지 결정하는 값
  • 학습률이 너무 크면 → 손실 함수의 값이 최소값을 지나쳐 발산할 가능성
  • 학습률이 너무 작으면 → 학습 속도가 매우 느림