본문 바로가기
DB 공부하기

230329_DB복습

by 보랏 2023. 3. 29.

안녕하세요. 보랏입니다.

 

오늘은 머신러닝 기법 중 확률적 경사 하강법, 오차역전파와

트리 구조에 대해서 배웠습니다. 

 

바로 복습 시작하겠습니다. 

 

 

 

1. 확률적 경사 하강법

  • 경사 = 기울기 / 하강법 : 내려가는 방법
  • 아래 그림처럼 내려오는 보폭이 너무 크면 원하는 지점을 지나쳐 갈 수 있음

  • 확률적이라는 말은 실제로는 훈련 데이터 셋을 사용해 모델을 훈련하여 경사 하강법도 당연히 훈련 세트를 사용하여 가장 가파른 길을 찾는 방법
  • 훈련 세트에서 랜덤하게 하나의 샘플을 선택하여 경사를 내려가고, 그 다음 훈련 세트에서 랜덤하게 또 다른 샘플을 하나 선택하여 경사를 조금 내려가는 방법
    • 이런 식으로 전체 샘플을 모두 사용할 때까지 계속해서 진행
    • 이렇게 해서 답을 찾지 못하면 다시 처음부터 시작합니다. 그 다음 다시 랜덤하게 하나의 샘플을 선택해 이어서 경사를 내려갑니다.
    • 에포크(epoch)  :  확률적 경사 하강법에서 훈련 세트를 모두 사용하는 과정
  • 미니배치 경사 하강법(minibatch gradient descent) : 1개의 데이터셋을 사용하여 내려오는 것보다 여러 개를 한 번에 묶어 사용하면 시간이 더 절약될 수 있음
  • 배치 경사 하강법 : 전체 샘플을 사용하여 진행

 

2. 손실함수 

  • loss(cost) function  는 어떤 문제에서 머신러닝 알고리즘이 얼마나 엉터리인지를 측정하는 기준
    • 값이 작을수록 좋지만 어떤 값이 최솟값인지는 알지 못함
    • 손실 함수는 샘플 하나에 대한 손실을 정의
    • 비용 함수는 훈련 세트에 있는 모든 샘플에 대한 손실 함수의 합을 의미

 

3. 로지스틱 손실 함수

  • 예측 확률에 로그 함수를 적용하면 더 좋아지며, 예측 확률의 범위는 0~1사이로 로그 함수는 이 사이에서 음수가 되므로 최종 손실 값은 양수가 됨
  • 손실이 양수가 되면 이해하기 더 쉽습니다. 또 로그 함수는 0에 가까울수록 아주 큰 음수가 되기 때문에 손실을 아주 크게 만들어 모델에 큰 영향을 미칠수 있습니다.

 

4. 확률적 경사 하강법 예제 

from sklearn.datasets import load_diabetes
diabetes = load_diabetes()
diabetes

### diabetes 산점도
import matplotlib.pyplot as plt
plt.scatter(diabetes.data[:, 2], diabetes.target)
plt.show()

  • 훈련 데이터와 잘 맞는 w와 b를 찾는 법
    • 무작위로 w와 b를 정하기 (무작위 모델 만들기)
    • x에서 샘플 하나를 선택하여 y^를 계산 (무작위 모델 예측)
    • y^과 선택한 샘플의 진짜 y를 비교 (예측값과 진짜 정답 비교)
    • y^이 y와 더 가까워 지도록 w, b를 조정 (모델 조정)
    • 모든 샘플을 처리할 때까지 위의 항목 반복
y = wx + b

w = 1.0
b = 1.0

### 예측값 
y_hat = x[0] * w +b
y_hat
-> 1.0616962065186886

### 정답 
### 정답과 예측값과의 차이가 크다 -> w 값을 증가시켜야함
y[0]
-> 151.0

w_inc = w + 0.1 #w 값을 증가시킴
y_hat_inc = x[0] * w_inc + b
y_hat_inc
-> 1.0678658271705574

### w값 조정한 후 예측값이 얼마나 변했는지 변화값 확인
w_new = w + w_rate
w_new 
-> 1.0616962065186888

### 변화율로 bias 업데이트
b_inc = b + 0.1
y_hat_inc = x[0] * w_inc + b_inc
print(y_hat_inc)
-> 1.1678658271705575

b_rate = (y_hat_inc-y_hat) / (b_inc-b)
print(b_rate)
-> 1.0616962065186888

 

 

5. 오차역전파 

  • 결과값을 통해서 다시 역으로 input 방향으로 오차를 다시 보내며 가중치를 재업데이트 하는 것
  • 위의 그림을 보면 input이 들어오는 방향(순전파)으로 output layer 에서 결과 값이 나오며, 결과값은 오차 (error)를 가지게 되는데 역전파는 이 오차(error)를 다시 역방향으로 hidden layer와 input layer로 오차를 다시 보내면서 가중치를 계산하면서 output에서 발생했던 오차를 적용시키는 것
# 실제예측된값과 실제정답과의 차이를 w값을 반영한 값 
err = y[0] - y_hat
w_new = w + w_rate * err
b_new = b + 1 * err

# 결과값이 더욱 커짐
print(w_new, b_new)
-> 10.250624555904514 150.9383037934813

y_hat = x[1] * w_new + b_new
err = y[1] - y_hat
w_rate = x[1]
w_new = w_new + w_rate * err
b_new = b_new + 1 * err
print(w_new, b_new)
-> 14.122059942557254 75.72691977830128

for x_i, y_i in zip(x , y ):
    y_hat = x_i * w + b
    err = y_i - y_hat
    w_rate = x_i
    w = w + w_rate * err
    b = b + 1 * err
print(w, b)
-> 913.5973364345901 123.39414383177201

import matplotlib.pyplot as plt
plt.scatter(diabetes.data[:, 2], diabetes.target)
plt.plot([-0.1, 0.15 ], [-0.1*w+b, 0.15*w+b ], color='r')
plt.show()

 

 

6. 트리(Tree) 구조

  • 노드들이 나무가지처럼 연결된 비선형 계층적 자료구조
  • 트리는 다른 하위 트리가 있으며 하위 트리 안에는 또 다른 트리가 있는 재귀적 자료구조

  • 노드(Node) 
    • 트리를 구성하고 있는 기본 요소
    • 노드에는 키 또는 값과 하위 노드에 대한 포인터를 가지고 있음
    • 노드와 노드 간에는 간선(Edge)으로 연결
  • 깊이(depth) : 루트에서 어떤 노드까지의 간선 수
  • 높이(height) : 어떤 노드에서 리프 노드까지 가자 긴 경로의 간선 수
  • Level : 루트에서 어떤 노드까지의 간선 수

  • 트리 특징
    • 하나의 루트노드와 0개 이상의 하위 트리로 구성
    • 데이터를 순차적으로 저장하지 않기 때문에 비선형 자료구조
    • 재귀적 자료구조
    • 단순순환(Loop)을 갖지 않고, 연결된 무방향 그래프
    • 노드 간에 부모 자식 관계를 갖고 있는 계층형 자료구조로, 모든 자식 노드는 부모 노드만 가지게 됨
    • 노드가 n개인 트리는 항상 n-1개의 간선을 가짐

 

 

7. 이진탐색 트리(Binary search tree)

  • 순서화된 이진 트리(각 노드의 차수(자식노드)가 2이하인 트리)
  • 노드의 왼쪽 자식은 부모의 값보다 작은 값을 가져야 하며 오른쪽 자식은 부모의 값보다 큰 값을 가져야 함

  • 사용 사례
    • 계층적 데이터 저장 : 트리는 데이터를 계층 구조로 저장하는 데 사용 ex) 파일 및 폴더
    • 효율적인 검색 속도 : 효율적인 삽입, 삭제 및 검색을 위해 트리 구조 사용
    • 힙(Heap)
    • 데이터베이스 인덱싱 ex) B-Tree
  • 코드 구현
class Node : 
    
    def __init__(self, data) : 
        self.left = None
        self.right = None
        self.data = data
  # 루트 노드에서 자식노드가 없으므로 left,right에 None을 넣기      
        
    def __repr__(self) : 
        return str(self.data)
# 노드 삽입 클래스 구현
class BinarySearchTree : 
    # root노드는 None값을 가지고 있음
    def __init__(self) : 
        self.__root = None
    # method의 기본값을 'iteractive'로 가지고, method가 'recursion'값이 아니기 때문에 _insert_iter메서드로 이동
    def insert(self, data, method = 'iterative') : 
        if method in 'recursion' : 
            self.__root = self._insert_rec(self.__root, data)
        else : 
            self._insert_iter(data) 
    
		# node값이 None이기 때문에 if not = None으로 되어 True
		# 1번째 값은 루트노드를 생성하고 다음값부터는 data(입력변수)가 루트노드보다 크면 오른쪽, 작으면 왼쪽
    def _insert_rec(self, node, data):
        if not node : 
            node = Node(data)
        else : 
            if node.data > data : 
                node.left = self._insert_rec(node.left, data)
            else : 
                node.right = self._inser_rec(node.right, data)
    
    def _insert_iter(self, data) : 
        if not self.__root : #self.__root에서 None값이 때문에 true
            self.__root = Node(data) #루트노드 생성
            return 
        
        new_node = Node(data) #new_node 값은 Node클래스 입력변수
        
        curr = self.__root
        parent = None
        
			# 이 반복문은 새로운 값의 위치를 찾는 구문 -> level 따라 내려오기
        while (curr != None) : # 그 후 현재 루트노드 값이 존재하면 
            parent = curr      # 부모노드는 curr 
            if curr.data > data :  # 이 후 입력변수가 부모노드보다 작은면 left, 크면 right로 이동
                curr = curr.left
            else : 
                curr = curr.right

      # 찾은 위치의 새로운 값을 입력하는 구문         
        if parent.data > data : 
            parent.left = new_node
        else : 
            parent.right = new_node

 

오늘은 이렇게 이론 내용 위주로 공부하였습니다. 

오늘 복습 마무리 하겠습니다. 

 

감사합니다. 

'DB 공부하기' 카테고리의 다른 글

230404_DB복습  (0) 2023.04.04
230403_DB복습  (0) 2023.04.03
230327_DB복습  (0) 2023.03.27
230323_DB복습  (0) 2023.03.23
230322_DB복습  (0) 2023.03.22