[AI] day4. 다중 선형 회귀 (Multi Variable Linear Regression)



다중 선형 회귀 (Multi Variable Linear Regression)



단순 선형 회귀 분석 (Single Variable Linear Regression) 은 독립변수 1개, 종속변수 1개였지만, Multi Variable Linear Regression 은 다수의 독립변수와 1개의 종속변수를 가진다.



import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import cm

trainDataNumber = 100
learningRate = 0.01
totalStep = 1001

np.random.seed(321)

x1TrainData = list()
x2TrainData = list()
yTrainData = list()

x1TrainData = np.random.normal(0.0, 1.0, size=trainDataNumber)
x2TrainData = np.random.normal(0.0, 1.0, size=trainDataNumber)

for i in range(0, trainDataNumber):
    x1 = x2TrainData[i]
    x2 = x2TrainData[i]
    y = 10 * x1 + 5.5 * x2 + np.random.normal(0.0, 3)
    yTrainData .append(y)

fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.plot(x1TrainData, x2TrainData, yTrainData, linestyle="none", marker="o", mfc="none", markeredgecolor="red")
plt.show()


다중 선형 회귀를 세팅하는 것은 x 데이터가 x1, x2로 추가되어 그래프에 적용되었다는 점 빼고는 단순 선형 회귀와 크게 다를 바 없다. 이를 3차원 공간에서 출력하면 아래와 같다.

직접 작성해서 실행시켜보면 알겠지만, 그래프를 이리저리 돌려볼 수 있다!

y = 10 * x1 + 5.5 * x2 + 3 + np.random.normal(0.03) 에서 x1, x2, y가 x축, y축, z축의 역할을 한다고 볼 수 있으며 이렇게 3차원 공간이 만들어지게 된다. 그런데 여기서 np.random.normal(0.03), 즉 난수를 생성해서 더해주지 않으면 어떻게 될까?



위의 3차원은 이리저리 돌려봐도 한 직선을 이루는 형태가 나오지 않지만, 난수를 제거하면 위와같이 직선 형태가 나오는 구간이 있다. 직선이 나온다는 것은 결국 한 평면에서만 데이터가 세팅 되어 있다는 것이며, 난수를 생성해서 더해주게 되면 데이터가 한 평면 위에서만 나타나지 않고 여러 평면에 걸쳐서 퍼지게 된다.
다음 코드를 보자.


import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import cm

# <step 1>
trainDataNumber = 100
learningRate = 0.01
totalStep = 1001

np.random.seed(321)

x1TrainData = list()
x2TrainData = list()
yTrainData = list()

x1TrainData = np.random.normal(0.0, 1.0, size=trainDataNumber)
x2TrainData = np.random.normal(0.0, 1.0, size=trainDataNumber)

for i in range(0, trainDataNumber):
    x1 = x2TrainData[i]
    x2 = x2TrainData[i]
    y = 10 * x1 + 5.5 * x2
    yTrainData .append(y)


# <step 2>
W1 = tf.Variable(tf.random_uniform([1]))
W2 = tf.Variable(tf.random_uniform([1]))
b = tf.Variable(tf.random_uniform([1]))

X1 = tf.placeholder(tf.float32)
X2 = tf.placeholder(tf.float32)
Y = tf.placeholder(tf.float32)

# <step 3>
hypothesis = W1 * X1 + W2 * X2 + b
costfunction = tf.reduce_mean(tf.square(hypothesis - Y))

optimizer = tf.train.GradientDescentOptimizer(learning_rate=learningRate)
train = optimizer.minimize(costfunction)

# fig = plt.figure()
# ax = fig.add_subplot(111, projection='3d')
# ax.plot(x1TrainData, x2TrainData, yTrainData, linestyle="none", marker="o", mfc="none", markeredgecolor="red")
# plt.show()


역시 단순 선형 회귀와 별 차이 없다. 생각해 볼 만한 부분은 가설함수인데, 가설함수는 W1, W2, b를 각각 세팅해놓고 X1TrainData, X2TrainData, YTrainData가 들어갈 플레이스홀더 X1, X2, Y와 곱연산을 적용해서 만들어진다. 즉 X1TrainData의 모든 원소들과 W1의 모든 원소들을 곱한 것과 X2TrainData와 W2의 모든 원소들의 곱과 Y를 더하는 수식인데, X1 * W1 + X2 * W2의 경우 아래와 같은 식이 나온다.


종속변수의 수가 많지 않으면 위와 같이 직접 식을 만들 수 있지만, 종속변수가 몇만개가 넘어갈 때, 그걸 일일이 다 입력할 수 없다. (하려면 할 수 있지만 그럴 필요가 없다.)

tensorflow에선 tf.matmul()이라는 함수로 행렬 곱의 연산을 할 수 있다.



import tensorflow as tf
import numpy as np

matrix1 = tf.constant([[1, 2, 3, 4], [3, 4, 5, 6]]) # 2 * 4 행렬 (shape(2, 4))
matrix2 = tf.constant([[1, 2], [3, 4], [5, 6], [7, 8]]) # 4 * 2 행렬 (shape(4, 2))

res = tf.matmul(matrix1, matrix2).eval(session=tf.Session())

print(res)


output :
[[ 50  60]
 [ 82 100]]

이 때, 행렬의 연산은 차원이 맞아야 학습이 가능한데, 위의 경우 2 * 4 행렬과 4 * 2 행렬의 연산이기 때문에 가능했지만, 가령 1 * 3 행렬과 1 * 3 행렬은 계산이 불가능하다.

따라서 Broadcasting 이라는 기능으로 행렬을 늘려 차원을 강제로 맞춰준다. 

broadcasting은 곱 연산에만 적용되는 것이 아닌 모든 연산에 적용되는데, 예를 들어 아래와 같은 연산을 한다고 생각해보자.



broadcasting으로 1차원 행렬인 b를 강제로 늘려주면 아래와 같이 모든 원소가 b인 2 * 2 행렬이 만들어지면서 연산이 가능하게 된다.



행렬 곱셈 (Matrix Mutilplication)과 Broadcasting을 적절하게 이용하면 일일이 다 적을 필요 없이 가설함수를 쉽게 만들어줄 수 있다.
그래프 생성이 끝났으면 이제 학습 결과를 확인해보자.


import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import cm

# <step 1>
trainDataNumber = 200
learningRate = 0.01
totalStep = 1001

np.random.seed(321)

x1TrainData = list()
x2TrainData = list()
yTrainData = list()

x1TrainData = np.random.normal(0.0, 1.0, size=trainDataNumber)
x2TrainData = np.random.normal(0.0, 1.0, size=trainDataNumber)

for i in range(0, trainDataNumber):
    x1 = x1TrainData[i]
    x2 = x2TrainData[i]
    y = 10 * x1 + 5.5 * x2 + 3 + np.random.normal(0.0, 3)
    yTrainData.append(y)


# <step 2>
W1 = tf.Variable(tf.random_uniform([1]))
W2 = tf.Variable(tf.random_uniform([1]))
b = tf.Variable(tf.random_uniform([1]))

X1 = tf.placeholder(tf.float32)
X2 = tf.placeholder(tf.float32)
Y = tf.placeholder(tf.float32)

# <step 3>
hypothesis = W1 * X1 + W2 * X2 + b
costfunction = tf.reduce_mean(tf.square(hypothesis - Y))

optimizer = tf.train.GradientDescentOptimizer(learning_rate=learningRate)
train = optimizer.minimize(costfunction)

# <step 4>
sess = tf.Session()
sess.run(tf.global_variables_initializer())

fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.plot(x1TrainData, x2TrainData, yTrainData, linestyle="none", marker="o", mfc="none", markeredgecolor="red")

Xs = np.arange(min(x1TrainData), max(x1TrainData), 0.05)
Ys = np.arange(min(x2TrainData), max(x2TrainData), 0.05)
Xs, Ys = np.meshgrid(Xs, Ys)

print("------------------------------------------------------------------------------------")
print("Train(Optimization Start")
for step in range(totalStep):
    cost_val, W1_val, W2_val, b_val, _ = sess.run([costfunction, W1, W2, b, train], feed_dict={X1 : x1TrainData, X2 : x2TrainData, Y : yTrainData})

    if step % 50 == 0:
        print("Step : {}, cost : {}, W1 : {}, W2 : {}, b : {}".format(step, cost_val, W1_val, W2_val, b_val))

    if step % 100  == 0:
        ax.plot_surface(Xs, Ys, W1_val * Xs + W2_val * Ys + b_val, rstride=4, cstride=4, alpha=0.2, cmap=cm.jet)
print("Train Finished")
print("------------------------------------------------------------------------------------")
plt.show()

sess.close()


학습 결과
학습 결과 3차원 그래프 (1)
학습 결과 3차원 그래프 (2)
3차원 그래프를 조금 돌려보면 학습 결과 3차원 그래프 (2)에서 보는 것과 같이 평면이 바닥에 평행하다가 학습 결과에서 W1 = 10, W2 = 5.5, b = 3 인 부분에서부터 점점 결과값으로 수렴하게 된다. 이 때 alpha값을 0.02로 설정하여 평면이 투명하게 나오게 했는데, 학습 결과에 수렴할수록 점점 진해지는 것을 확인할 수 있다. 즉 평면 그래프가 학습 결과에 수렴하는 형태가 많아질수록 평면 그래프가 겹쳐져서 진해지게 되는 것이다.

댓글