2. 경사 하강법(Gradient Descent), Tensorflow 선형 회귀 구현
이전 포스트에서 이어집니다.
선형 회귀 보러 가기 https://godute.tistory.com/39
위 그림은, 이전에 임의로 지었던 가설의 예측값과 실제 값의 차이를 나타낸 그래프입니다.
가설의 가중치 W를 9/5로 설정했을 때, 112.75의 손실(loss, cost)가 발생했었습니다.
손실을 줄이기 위해선, 가중치 W가 이차 함수의 최저점으로 가야 합니다. 미분이 필요하겠군요
Cost function을 W에 대해 Cost(W)라고 표현할게요,
Cost(W) = 1/N* Σ(H(x) - y)2 = 1/N * Σ(W·x + b - y)2 였죠~
W를 업데이트하는 표기는,
W := W - a·(∂Cost(W)/∂W) 입니다. cost(W)를 W에 대해 미분했을 때, cost가 감소하는 방향으로 업데이트를 해준다,, 라는 뜻인데요.
여기서 a는 일단 0보다 큰 실수라고 알고 계시면 됩니다.
만약 그림의 그래프처럼 기울기가 양수일 경우, cost(W)의 미분은 양수입니다. 그렇다면 W는, 감소하면서 업데이트될 것이고요
반대로 초기 W가 최저점 왼쪽에 있었다고 한다면, 미분 값은 음수가 되겠죠! W는 증가하면서 업데이트될 것입니다.
a는 업데이트할 때, 얼마나 보폭을 크게 하면서 갈지를 나타내는 상수입니다. learning rate라고 불립니다.
a가 크다면, W가 업데이트될 때 크게 크게 될 것이고요, 반대로 너무 작다면 업데이트는 기어갈 겁니다.
좋은 그림이 있네요, Big learning rate면 저렇게 보폭이 너무 커져 튕겨나갑니다. 반대로 너무 작아도 기어갈 것이구요, 이것을 적절하게 설정하는 게 중요합니다.
선형 회귀 같은 단순한 모델은 이렇게 2차 함수로 나타날 수 있지만,,
모델이 복잡할수록 위 그림처럼 cost function 형태가 난잡할 수도 있습니다. 왼쪽 그래프의 경우, 초기 시작 지점에 따라 gradient descent 할 경우 진행 방향이 달라지는데, 오른쪽 그래프는 어디서 시작하던 하나의 목표지점으로 수렴하는 걸 볼 수 있습니다.
오른쪽 그래프 같은 형태를 convex function이라고 합니다. 어느 점에서 시작해도 항상 답을 찾는 것을 보장합니다. cost function의 모양이 convex 한 지 확인해야 합니다.
Tensorflow를 이용한 실습
1. 모듈 불러오기
import tensorflow as tf
2. 데이터 설정
x_data = [[44], [12], [20] ,[4]] # shape = ( , 1)
y_data = [[90], [35], [48], [12]] # shape = ( , 1)
x_data : 공부 시간입니다.
y_data : 그에 따른 성적을 의미합니다.
3. placeholder, Variable
X = tf.placeholder(tf.float32, shape = [None, 1])
Y = tf.placeholder(tf.float32, shape = [None, 1])
W = tf.Variable(tf.random_normal([1, 1]), name = 'weight')
b = tf.Variable(tf.random_normal([1]), name = 'bias')
placeholder : 실제 데이터인 x_data를 전달할 때, placeholder에 넘겨서 전달한다고 보시면 되겠습니다.
Variable : 텐서플로우가 학습하면서 자체적으로 변경하는 변수입니다. random_normal은 초기 값을 임의로 설정해 주는걸 의미합니다.
shape는 차원을 의미합니다. X,Y는 2차원이 됩니다. 그리고 None은 어떤 값이 와도 상관 없다는 의미입니다. 여기선 데이터 개수를 의미합니다.
즉, shape=[None, 1]의 의미는 데이터 개수는 상관없지만 변수의 개수는 하나여야 한다는 의미입니다.
wx + b 이렇게 단일 변수로요,
그렇다면 여러개라면? 그땐 multi-variable Linear Regression이 됩니다.
H(x) = w1x1 + w2x2 + .... + b 이런 식으로요,
4. hypothesis, cost function, Gradient Descent
hypothesis = tf.matmul(X, W) + b #H(x) = wx + b
cost = tf.reduce_mean(tf.square(hypothesis - Y)) # cost function = (1/m)Σ (H(x) - y)^2
optimizer = tf.train.GradientDescentOptimizer(learning_rate = 1e-3)
train = optimizer.minimize(cost)
matmul은 행렬곱을 의미합니다.
optimizer에서 보이는 learning_rate가 바로 위에서 설명했던 얼마나 보폭을 크게 할 지에 대한 값입니다.
지금까지 텐서플로우로 그린 그래프는 다음과 같습니다. 왠지 모르지만 제일 나중에 만든 train은 그래프상에 나오지 않네요,,
하여튼 그래프를 이런식으로 다 만들었습니다! 이제 수로를 완성했으니, 물이 흘러가게 하면 될것 같습니다.
5. graph 실행 (session)
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
writer = tf.summary.FileWriter('./log/linear_regression', sess.graph)
for i in range(10001):
c_ , _ , summary= sess.run([cost, train, merged], feed_dict={X:x_data, Y:y_data})
writer.add_summary(summary, i)
if i % 100 == 0:
print("iteration {} cost : {}".format(i, c_))
print(sess.run([hypothesis, W, b], feed_dict={X:[[30]]}))
먼저 학습 전 W, b같이 Variable로 설정한 변수가 있으면 global_variable_initializer를 실행시켜 초기화해야 합니다. feed_dict를 통해 placeholder로 실제 데이터를 전달합니다. 실제 W, b를 업데이트 하는 과정은 train을 실행시켰을 때 해줍니다.
그렇다면 그래프 상에서 제일 위의 노드를 실행시켰는데 어떻게 W까지 전달될까요, 다~~~음에 역전파(backpropagation)에 대해 설명하고, 우선은 Logistic Regression에 대해 먼저 말해볼까 합니다.