기계학습 개념
선형 회귀: 데이터를 잘 표현하는 직선 한개를 찾아, 새로운 상황에 대한 예측을 가능하게 함
로지스틱 회귀: 배경지식을 토대로 분류 작업을 할 수 있음
선형 회귀(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정도로 학습률을 줄이니 서서히 에러가 줄어드는것을 볼 수 있었다.
'2023-2 > 데이터 분석과 머신러닝' 카테고리의 다른 글
Python summary (2) | 2023.10.18 |
---|---|
머신러닝 개요 (0) | 2023.10.18 |
댓글