본문 바로가기
2023-2/데이터 분석과 머신러닝

기계 학습

by 철없는민물장어 2023. 10. 21.
728x90
반응형

기계학습 개념

선형 회귀: 데이터를 잘 표현하는 직선 한개를 찾아, 새로운 상황에 대한 예측을 가능하게 함

로지스틱 회귀: 배경지식을 토대로 분류 작업을 할 수 있음

 

선형 회귀(Linear Regression)

주어진 학습 데이터의 추이에 대해 가장 적합한 직선 방정식을 추론해내는 것

학습 데이터의 예

: 공부시간에 대한 시험성적

 

공부시간(x) 시험성적(t)
9 74
14 81
21 86
27 90
32 88
37 92

위 데이터를 보면 공부시간에 비례해서 시험성적도 증가하는 경향을 보임.

 

점들의 추이를 가장 잘 나타내는 직선(y=Wx+b)을 찾아서, 주어지지 않은 공부시간에 대한 시험 성적을 예상할 수 있음.

 

선형회귀는 주어진 학습 데이터의 추이에 대해 가장 적합한 직선 방정식을 추론해내는것.

=> 주어진 데이터 값들과 임의의 각 직선들과의 정확한 수치적 오차 계산이 필요

 

오차 = t - y = t - (Wx+b)

모든 x1,x2,...와 실제 성적 t1,t2..에 대해 t1-y1, t2-y2,..의 차이들의 합.

 

임의의 한 직선(임의의 W,b)을 생성하고, 손실함수 개념을 이용하여 오차를 줄이는 방향으로 W와b를 반복적으로 변경시킴.

오차가 최소일 때 W,b로 만들어지는 직선이 주어진 데이터에 최적인 직선이 됨.

 

손실 함수(Loss function 또는 Cost function) = E(W,b)

오차 제곱 합의 평균(mean square error: MSE)

 

경사하강법

최소의 손실함수값을 갖는 (W,b)를 찾는 알고리즘

 

1. 현재 W,b값으로 손실함수의 기울기를 계산

=> 미분값을 구함. (여기서는 아주 작은 델타x값(0.0001)을 이용해 계산함)

 

2. 현재 W 또는 b값에서 학습률*기울기값을 뺀다.

(기울기가 감소하는 방향으로 가기 위해 뺌)

 

3. 이것을 반복

 

import numpy as np

#데이터 가져오기
loaded_data = np.loadtxt('data-01_1var.csv', delimiter=',', dtype=np.float32)
x_data = loaded_data[:, 0:-1] #모든행, 열은 0부터 맨끝 전까지...
t_data = loaded_data[:, [-1]] #모든행, 맨끝열만.

W = np.random.rand(1, 1) #랜덤 가중치와 편향 생성
b = np.random.rand(1)

def errFunc(x, t):     # E(W, b). x는 입력데이터, t는 정답
    y = np.dot(x,W) + b #x와w를 곱하고 b를 더함.
    return np.sum((t - y)**2) / len(x) #평균에러제곱

def errValue(x, t):      # 정답과 계산값과의 차이 --> 손실값(error value)
    return errFunc(x, t)
    
def numerical_derivative(e, x):     # 함수 e의 x에 대한 수치미분 --> E(W,b)를 W=[ ... ] 또는 b로 미분함.
    delta_x = 1e-4  # 0.0001        # 실제 미분함수를 구하지 않고 특정 값의 +- 0.0001 범위에서 기울기 구함
    grad = np.zeros_like(x)         # 주어진 각 값들에 대한 기울기 값 저장

    # print(grad)
    # print(e)
    # print(x)

    it = np.nditer(x, flags=['multi_index'], op_flags=['readwrite'])  # 다차원 배열 원소들의 순서참조 지원 함수
    while not it.finished: #이 반복문 한번밖에 안돎... 이 함수에 x가 W인데 여기서 W가 1행1열짜리 배열이라.
        idx = it.multi_index
        temp = x[idx]            # 주어진값(기울기 대상지점값)을 저장해두고 기울기 계산 후 다시 원상태로 되돌림
        x[idx] = temp + delta_x
        fx1 = e(x)                  # e(x+delta_x)   # 주어진 값의 약간의 증가분(+0.0001)에 대한 손실함수 값

        x[idx] = temp - delta_x
        fx2 = e(x)                   # e(x-delta_x)   # 주어진 값의 약간의 감소분(-0.0001)에 대한 손실함수 값
        grad[idx] = (fx1 - fx2) / (2*delta_x)    # 주어진 값은 미세한 구간에서의 손실함수값의 변화량 --> 기울기

        x[idx] = temp            # 기울기값 구하기에 사용된 주어진 값의 사용이 끝난뒤 원래 값으로 되돌린다.

        it.iternext()
    return grad

# 학습을 마친 후, 임의의 데이터에 대해 미래 값 예측 함수
def predict(x):         # 입력변수 x : numpy type
    y = np.dot(x,W) + b
    return y
    
learning_rate = 1e-3
f = lambda x : errFunc(x_data,t_data)   # errFunc()함수를 다른 함수의 인자로 넘기기 위해

for step in range(150001):        # 적당한 횟수만큼 경사하강 반복
    W -= learning_rate * numerical_derivative(f, W)
    b -= learning_rate * numerical_derivative(f, b)

    if (step % 1000 == 0):       # 경사하강 반복 중간마다 손실값 출력
        print("Step = {:<5d}".format(step), "Error Value = {:.4f}".format(errValue(x_data, t_data)),
              "W =", W.reshape(1,-1).round(3), " b = ", b.round(3))

위 예시에서 numerical_derivative함수 내부를 보면..

인자는 e = 에러함수(데이터배열,정답배열)

x=가중치배열이다.

 

위 예시에서는 W 가중치가 1개밖에 없지만, 가중치가 여러개일 상황을 대비해 grad도 배열로 하고, x도 순회하며 반복하고있다.

 

다변수 선형회귀

loaded_data = np.loadtxt('data-01_3var.csv', delimiter=',', dtype=np.float32)
x_data = loaded_data[:, 0:-1] #모든행, 열은 0부터 맨끝 전까지...
t_data = loaded_data[:, [-1]] #모든행, 맨끝열만.

W = np.random.rand(3, 1) #랜덤 가중치와 편향 생성
b = np.random.rand(1)


print('W = ', W, ", W.shape = ", W.shape, ", b = ", b, ", b.shape = ", b.shape)

def errFunc(x, t):     # E(W, b). x는 입력데이터, t는 정답
    y = np.dot(x,W) + b #x와w를 곱하고 b를 더함.
    return np.sum((t - y)**2) / len(x) #평균에러제곱


def errValue(x, t):      # 정답과 계산값과의 차이 --> 손실값(error value)
    return errFunc(x, t)

def numerical_derivative(e, x):     # 함수 e의 x에 대한 수치미분 --> E(W,b)를 W=[ ... ] 또는 b로 미분함.
    delta_x = 1e-4  # 0.0001        # 실제 미분함수를 구하지 않고 특정 값의 +- 0.0001 범위에서 기울기 구함
    grad = np.zeros_like(x)         # 주어진 각 값들에 대한 기울기 값 저장

    it = np.nditer(x, flags=['multi_index'], op_flags=['readwrite'])  # 다차원 배열 원소들의 순서참조 지원 함수
    while not it.finished: # 이 함수에 x가 W인데 여기서 W가 1행1열짜리 배열이라.
        idx = it.multi_index
        temp = x[idx]            # 주어진값(기울기 대상지점값)을 저장해두고 기울기 계산 후 다시 원상태로 되돌림
        x[idx] = temp + delta_x
        fx1 = e(x)                  # e(x+delta_x)   # 주어진 값의 약간의 증가분(+0.0001)에 대한 손실함수 값

        x[idx] = temp - delta_x
        fx2 = e(x)                   # e(x-delta_x)   # 주어진 값의 약간의 감소분(-0.0001)에 대한 손실함수 값
        grad[idx] = (fx1 - fx2) / (2*delta_x)    # 주어진 값은 미세한 구간에서의 손실함수값의 변화량 --> 기울기

        x[idx] = temp            # 기울기값 구하기에 사용된 주어진 값의 사용이 끝난뒤 원래 값으로 되돌린다.

        it.iternext()

    return grad

numerical_derivative(lambda x:errValue(x_data,t_data),W)

learning_rate = 1e-6
f = lambda x : errValue(x_data,t_data)   # errFunc()함수를 다른 함수의 인자로 넘기기 위해

for step in range(400001):        # 적당한 횟수만큼 경사하강 반복
    W -= learning_rate * numerical_derivative(f, W)
    b -= learning_rate * numerical_derivative(f, b)

    if (step % 1000 == 0):       # 경사하강 반복 중간마다 손실값 출력
        print("Step = {:<5d}".format(step), "Error Value = {:.4f}".format(errValue(x_data, t_data)),
              "W =", W.reshape(1,-1).round(3), " b = ", b.round(3))

처음에 데이터만 바꾸고 그대로 실행했는데,

실행은 잘 되는데 에러가 줄어들지를 않았다.

단순히 코드가 잘못됐을거라 생각하고 계속 고치려 했는데 문제를 찾지 못했다.

 

문제는 학습률이었다.

기존 학습률은 1e-3이었는데, 학습률이 너무 커서 에러를 줄이지 못하고 발산하는 것이었다.

1e-5정도로 학습률을 줄이니 서서히 에러가 줄어드는것을 볼 수 있었다.

 

728x90
반응형

'2023-2 > 데이터 분석과 머신러닝' 카테고리의 다른 글

Python summary  (2) 2023.10.18
머신러닝 개요  (0) 2023.10.18

댓글