TensorFlow

TensorFlow 기초 18 - 다항회귀(Polynomial Regression)

코딩탕탕 2022. 12. 2. 15:32

 

최소제곱법으로 회귀선 구하기 방법과 tf로 회귀선 구하기 방법2로 나눠서 작성했다.

# 다항회귀 : Polynomial Regression - 비선형 데이터인 경우 다항식을 이용하여 다항회귀 처리 가능

# tesnorflow를 이용하여 2차함수 회귀선 그리기

import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt
plt.rc('font', family='malgun gothic')
plt.rcParams['axes.unicode_minus'] = False
import random

"""
# 다항 회귀 연습용 데이터 : 지역별 인구증가율과 고령인구비율(통계청 시각화 자료에서 발췌)
x = [0.3, -0.78, 1.26, 0.03, 1.11, 0.24, -0.24, -0.47, -0.77, -0.37, -0.85, -0.41, -0.27, 0.02, -0.76, 2.66]
y = [12.27, 14.44, 11.87, 18.75, 17.52, 16.37, 19.78, 19.51, 12.65, 14.74, 10.72, 21.94, 12.83, 15.51, 17.14, 14.42]

# a, b, c 세 개의 변수 선언
a = tf.Variable(random.random())
b = tf.Variable(random.random())
c = tf.Variable(random.random())

# 잔차 제곱 평균 반환 함수
def compute_loss():
    y_pred = a * x * x + b * x + c # yhat = ax² + bx + c
    loss = tf.reduce_mean((y - y_pred) ** 2)
    return loss

optimizer = tf.keras.optimizers.Adam(learning_rate=0.05)

for i in range(1000):
    optimizer.minimize(compute_loss, var_list=[a,b,c])
    
    if i % 100 == 99:
        print(i, 'a :', a.numpy(), ', b :', b.numpy(), ', loss :', compute_loss().numpy())
        
line_x = np.arange(min(x), max(x), 0.01)
line_y = a * line_x *line_x + b * line_x + c

plt.plot(line_x, line_y, 'r-')
plt.plot(x, y, 'bo') # 실제값
plt.show()
"""

# 다항 회귀 연습용 데이터 : 지역별 인구증가율과 고령인구비율(통계청 시각화 자료에서 발췌)
plo_inc = [0.3, -0.78, 1.26, 0.03, 1.11, 0.24, -0.24, -0.47, -0.77, -0.37, -0.85, -0.41, -0.27, 0.02, -0.76, 2.66]
pop_old = [12.27, 14.44, 11.87, 18.75, 17.52, 16.37, 19.78, 19.51, 12.65, 14.74, 10.72, 21.94, 12.83, 15.51, 17.14, 14.42]

# plt.plot(plo_inc, pop_old, 'ro')
# plt.xlabel('지역별 인구증가율')
# plt.ylabel('고령인구비율')
# plt.show()

print('최소제곱법으로 회귀선 구하기')
x_mean = sum(plo_inc) / len(plo_inc)
y_mean = sum(pop_old) / len(pop_old)
# 기울기, 절편 계산 a = sum(x - mean(x)) * (y - mean(y)) / sum(x - mean(x))², b = mean(y) - mean(x) * a

a = sum([(y - y_mean) * (x - x_mean) for y, x in list(zip(pop_old, plo_inc))])
a /= sum([(x - x_mean)**2 for x in plo_inc])
b = y_mean - x_mean * a
print('a :', a, ', b :', b)

line_x = np.arange(min(plo_inc), max(plo_inc), 0.01)
line_y = a * line_x + b

plt.plot(plo_inc, pop_old, 'ro')
plt.plot(line_x, line_y, 'b-')
plt.xlabel('지역별 인구증가율')
plt.ylabel('고령인구비율')
plt.show()

print('최소제곱법 말고 tf로 회귀선 구하기')
a = tf.Variable(random.random()) # 기울기
b = tf.Variable(random.random()) # 절편

# y = ax + b
def compute_loss():
    ypred = a * plo_inc + b
    loss = tf.reduce_mean((pop_old - ypred)**2)
    return loss

optimizer = tf.keras.optimizers.Adam(learning_rate=0.05)

for i in range(1, 1001):
    optimizer.minimize(compute_loss, var_list=[a,b])
    
    if i % 100 == 0:
        print(i, 'a :', a.numpy(), ', b :', b.numpy(), ', loss :', compute_loss().numpy())

line_x = np.arange(min(plo_inc), max(plo_inc), 0.01)
line_y = a * line_x + b

plt.plot(plo_inc, pop_old, 'ro')
plt.plot(line_x, line_y, 'b-')
plt.xlabel('지역별 인구증가율')
plt.ylabel('고령인구비율')
plt.show()


<console>
최소제곱법으로 회귀선 구하기
a : -0.355834147915461 , b : 15.669317743971302
최소제곱법 말고 tf로 회귀선 구하기
100 a : 0.20552501 , b : 5.124719 , loss : 120.71144
200 a : 0.004973734 , b : 8.891983 , loss : 55.60652
300 a : -0.14282945 , b : 11.667388 , loss : 25.759094
400 a : -0.24142435 , b : 13.519631 , loss : 14.391236
500 a : -0.30027393 , b : 14.625358 , loss : 10.868128
600 a : -0.33152348 , b : 15.212525 , loss : 9.988979
700 a : -0.34627187 , b : 15.48965 , loss : 9.81301
800 a : -0.3524623 , b : 15.605964 , loss : 9.784808
900 a : -0.35477188 , b : 15.64936 , loss : 9.781202
1000 a : -0.35553664 , b : 15.663731 , loss : 9.780835

잔차 제곱 평균 반환 수식 : ax² + bx + c

기울기, 절편 계산 a = sum(x - mean(x)) * (y - mean(y)) / sum(x - mean(x))², b = mean(y) - mean(x) * a

 

선형일 경우 시각화

 

 

다항회귀 방법(비선형)

print('다항회귀 : 비선형일 경우 사용')
a = tf.Variable(random.random())
b = tf.Variable(random.random())
c = tf.Variable(random.random())

# 잔차 제곱 평균 반환 함수
def compute_loss2():
    y_pred = a * plo_inc * plo_inc + b * plo_inc + c # yhat = ax² + bx + c
    loss = tf.reduce_mean((pop_old - y_pred) ** 2)
    return loss

optimizer = tf.keras.optimizers.Adam(learning_rate=0.05)

for i in range(1000):
    optimizer.minimize(compute_loss2, var_list=[a,b,c])
    
    if i % 100 == 99:
        print(i, 'a :', a.numpy(), ', b :', b.numpy(), ', loss :', compute_loss().numpy())
        
line_x = np.arange(min(plo_inc), max(plo_inc), 0.01)
line_y = a * line_x *line_x + b * line_x + c

plt.plot(plo_inc, pop_old, 'ro')
plt.plot(line_x, line_y, 'b-')
plt.xlabel('지역별 인구증가율')
plt.ylabel('고령인구비율')
plt.show()


<console>
다항회귀 : 비선형일 경우 사용
99 a : 3.6720428 , b : -4.853299 , loss : 437.1123
199 a : 3.665368 , b : -5.6806293 , loss : 471.43127
299 a : 2.498158 , b : -4.0594373 , loss : 400.7987
399 a : 1.418247 , b : -2.4626667 , loss : 338.33197
499 a : 0.63158935 , b : -1.296304 , loss : 296.9518
599 a : 0.10912019 , b : -0.52199644 , loss : 271.4591
699 a : -0.21093011 , b : -0.04776672 , loss : 256.6256
799 a : -0.39185336 , b : 0.22029804 , loss : 248.50278
899 a : -0.4862474 , b : 0.36015433 , loss : 244.34001
999 a : -0.531682 , b : 0.42747155 , loss : 242.35472

 

 

다항회귀 : Polynomial Regression - 비선형 데이터인 경우 다항식을 이용하여 다항회귀 처리 가능

 

 

비선형일 경우 시각화

 

 

다항회귀 : 딥러닝 네트워크 사용(좋은 방법(간단))

print('다항회귀 : 딥러닝 네트워크 사용')
model = tf.keras.Sequential([
    tf.keras.layers.Dense(units=64, activation='relu', input_shape=(1,)),
    tf.keras.layers.Dense(units=32, activation='relu'),
    tf.keras.layers.Dense(units=1)
])

model.compile(optimizer='adam', loss='mse', metrics=['mse'])
model.summary()

model.fit(plo_inc, pop_old, epochs=100)
print(model.predict(plo_inc).flatten())

line_x = np.arange(min(plo_inc), max(plo_inc), 0.01)
line_y = model.predict(line_x)

plt.plot(plo_inc, pop_old, 'ro')
plt.plot(line_x, line_y, 'b--')
plt.xlabel('지역별 인구증가율(%)')
plt.ylabel('고령인구비율(%)')
plt.show()

딥러닝의 경우 알아서 비선형이면 비선형으로 계산되어 나오기 때문에 간단하고 편하다