[week 1] Part 1: Basic Machine Learning (1)
Machine Learning
개발자가 어떤 룰이 있는지를 직접 정하는 것이 아닌 기계가 학습해서 룰을 따르는 것이다.
Supervised/Unsupervised learning
- Supervised: 정해져 있는 데이터(training set)으로 학습하는 것, 레이블에 해당하는 데이터를 주고 학습시키는 것
- ex. cat, dog 이미지를 맞추는 것
- Unsupervised
- Google news grouping -> 미리 레이블을 정하기 어렵다 = 미리 정해져 있는 데이터셋을 가지고 학습시키기 힘들다.
Training Data Set
label과 label에 해당하는 데이터를 가지고 ML이 학습 -> 어떤 데이터를 주었을 때, label을 지정해주는 것이 머신러닝이다.
Types of Supervised learning
- predicting final exam score: 성적은 0~100까지 범위가 넓기 때문에 이를 예측하는 것을 regression이라고 한다.
- Pass/Non-pass exam: 이런 경우는 둘 중 하나를 고르는 것이기 때문에 binary classification이라고 한다.
- Letter Grade(A, B, C, D, and F): 이런 경우는 multi-label classification이라고 한다.
Simple Linear Regression
Regression
회귀란 Regression toward the mean이라는 뜻이다. 어떤 데이터들은 전체의 평균으로 되돌아가게 되어있다.
Linear Regression
선형 회귀란 데이터를 가장 잘 대변하는 직선의 방정식을 찾는 것이라고 할 수 있다.
H(x) = Wx + b라고 할 때, 점들을 가장 잘 대변하는 W와 b를 찾아내는 것이다.
- Cost, Loss, Error: {H(x)(가설에 해당하는 직선, 내가 만든 직선) - y(실제 데이터 값)}의 합이 작을수록 직선이 데이터를 잘 대변하고 있다고 볼 수 있다.
근데 이 값을 그냥 더하게 되면 +값과 -값이 상쇄되어서 cost가 무의미 해질 수 있다. 그래서 Cost를 제곱해서 평균을 내는 것이 비용 함수 이다.
결국 이 비용함수의 값을 최소화 하는 것이 딥러닝의 목표이다.
Linear Regression 구현
Gradient descent
cost가 최소화 되도록하는 W, b를 찾는 minimization 알고리즘은 여러가지가 있는데 그 중 하나가 Gradient descent 이다. 경사 하강법이라고 말하기도 한다. 경사를 내려가면서 cost가 최소가 되는 W, b를 찾는 알고리즘이다.
cost 함수에서 예측과 실제 데이터의 차이를 hypothesis - y_data
로 표현하였다.
# Gradient descent
with tf.GradientTape() as tape:
hypothesis = W * x_data + b
cost = tf.reduce_mean(tf.square(hypothesis - y_data))
W_grad, b_grad = tape.gradient(cost, [W, b])
W.assign_sub(learning_rate * W_grad)
b.assign_sub(learning_rate * b_grad)
해당 코드를 tape에 기록을 하는 식으로 진행된다. 그러면서 tape.gradient 메서드를 통해서 미분값(경사)를 구한다. 즉 한번 경사를 내려오는 코드는 위와 같고, 이를 여러 번 돌리면서 데이터에 맞는 W와 b값을 찾아나가는 것이다.
tape.gradient 메서드는 cost 함수에 대해서 변수들에 대한 개별 기울기값을 튜플로 반환해준다.
assign_sub 메서드의 경우는 -= 와 같은 연산을 해준다. 즉, W -= learning_rate * W*grad, b -= learning_rate * b_grad의 연산을 진행해준다. 여기서 learning_rate는 W_grad, b_grad가 W, b에 얼마나 영향을 미칠지 결정을 해주며, 보통 작은값(0.01, 0.001)을 사용한다.
해당 전체 코드
import tensorflow as tf
x_data = [1, 2, 3, 4, 5]
y_data = [1, 2, 3, 4, 5]
# W, b initialize
W = tf.Variable(2.9)
b = tf.Variable(0.5)
# learning_rate initialize
learning_rate = 0.01
for i in range(100):
# Gradient descent
with tf.GradientTape() as tape:
hypothesis = W * x_data + b
cost = tf.reduce_mean(tf.square(hypothesis - y_data))
# cost 함수에 대해서 변수들에 대한 개별 기울기값을 튜플로 반환
W_grad, b_grad = tape.gradient(cost, [W, b])
W.assign_sub(learning_rate * W_grad)
b.assign_sub(learning_rate * b_grad)
if i % 10 == 0:
print("{:5}|{:10.4f}|{:10.4}|{:10.6f}".format(i, W.numpy(), b.numpy(), cost))
>>>
0| 2.4520| 0.376| 45.660004
10| 1.1036| 0.003398| 0.206336
20| 1.0128| -0.02091| 0.001026
30| 1.0065| -0.02184| 0.000093
40| 1.0059| -0.02123| 0.000083
50| 1.0057| -0.02053| 0.000077
60| 1.0055| -0.01984| 0.000072
70| 1.0053| -0.01918| 0.000067
80| 1.0051| -0.01854| 0.000063
90| 1.0050| -0.01793| 0.000059
W는 1로 수렴, b는 0으로 수렴을 기대
cost는 작을수록 좋다 = 모델이 실제 데이터와 유사하다.
predict
_H(x) = W _ x + b* 라는 가설에서 x 데이터가 주어졌을 경우, H(x)를 예측하도록 만들어보았다.
print(W * 5 + b)
print(W * 2.5 + b)
>>>
tf.Tensor(5.0066934, shape=(), dtype=float32)
tf.Tensor(2.4946523, shape=(), dtype=float32)
각각 5, 2.5에 근접하게 나온것으로 보아서 Simple Linear 모델이 잘 만들어진 것을 확인할 수 있다.
cost 함수 진행과정
초기 W를 0이라고 해두었을 때, cost 함수가 첫 번째 줄처럼 진행이 된다. 계속 진행이 되면서 W는 1씩 증가, 그에 따른 cost 값이 나타난다. (여기서 간략화하기 위해서 일단은 b 생략)
위 과정에서 W값을 실수형태로 해서 그래프를 그려보면 다음과 같다.
우리의 목표는 우리의 가설을 통해서 그려진 cost가 최소화되는 W값을 구하는 것이다. 그래프상 최소점을 찾는 알고리즘을 Gradient descent algorithm이다. cost가 최소화 되는 변수를 구할 수 있고, 위의 예시로는 (W, b) 또는 (W)만 가지고 진행했었지만, 변수로는 여러 개의 값이 들어갈 수 있다.
cost 함수의 진행과정이란 즉, gradient 함수의 진행과정과 같다. gradient를 통해서 cost값을 구할 수 있기 때문이다.
진행 과정
- 초기화된 W를 기준으로 기울기(gradient)를 구하고, W값에서 빼준다. 여기서 경사가 급할수록, W는 더 많이 이동하게 된다.
- 1번 과정을 통해서 만든 새로운 W를 가지고 다시 1번을 수행한다.
즉, W를 계속 업데이트를 한다는 것인데, cost 함수의 평균을 W로 미분한 값을 뺀다. 여기서 alpha란 W에 얼만큼 영향을 미칠 것인지 결정하는 상수이다. alpha가 클수록 W에 많이 빠지게 되면서 경사 하강되는 폭이 커지고, 작아질수록 W가 적게 빠지면서 경사 하강되는 폭이 작아진다.
❓근데 처음부터 cost 함수가 그려진 것이 아닌데 특정 W를 기준으로 기울기를 어떻게 구한다는 거지? cost는 x, y에 따라서 값이 변하는게 아니라 하나의 평균이 되는 것 아닌가? => 비용 함수의 개형은 유사하기 때문에 W에 따른 미분이 가능한 것 같다.
Cost Function in pure Python
import numpy as np
import matplotlib.pyplot as plt
X = np.array([1, 2, 3])
Y = np.array([1, 2, 3])
W = []
COST = []
# 비용 함수를 파이썬 코드로 구현
def cost_func(W, X, Y):
c = 0
for i in range(len(X)):
c += (W * X[i] - Y[i]) ** 2
return c / len(X)
# linspace란 -3부터 5를 15개 구간으로 나누어서 리턴해준다.
for feed_W in np.linspace(-3, 5, num = 15):
curr_cost = cost_func(feed_W, X, Y)
W.append(feed_W)
COST.append(curr_cost)
print("{:6.3f} | {:10.5f}".format(feed_W, curr_cost))
plt.plot(W, COST)
plt.title("Cost Function in pure Python")
plt.xlabel("W")
plt.ylabel("cost(W)")
plt.show()
>>>
-3.000 | 74.66667
-2.429 | 54.85714
-1.857 | 38.09524
-1.286 | 24.38095
-0.714 | 13.71429
-0.143 | 6.09524
0.429 | 1.52381
1.000 | 0.00000
1.571 | 1.52381
2.143 | 6.09524
2.714 | 13.71429
3.286 | 24.38095
3.857 | 38.09524
4.429 | 54.85714
5.000 | 74.66667
Cost Function in TensorFlow
import numpy as np
import matplotlib.pyplot as plt
import tensorflow as tf
X = np.array([1, 2, 3])
Y = np.array([1, 2, 3])
W = []
# 비용 함수를 텐서플로 코드로 구현
def cost_func(W, X, Y):
hypothesis = X * W
return tf.reduce_mean(tf.square(hypothesis - Y))
W_values = np.linspace(-3, 5, num = 15)
cost_value = []
# linspace란 -3부터 5를 15개 구간으로 나누어서 리턴해준다.
for feed_W in np.linspace(-3, 5, num = 15):
curr_cost = cost_func(feed_W, X, Y)
W.append(feed_W)
cost_value.append(curr_cost)
print("{:6.3f} | {:10.5f}".format(feed_W, curr_cost))
plt.plot(W, cost_value)
plt.title("Cost Function in TensorFlow")
plt.xlabel("W")
plt.ylabel("cost(W)")
plt.show()
>>>
-3.000 | 74.66667
-2.429 | 54.85714
-1.857 | 38.09524
-1.286 | 24.38095
-0.714 | 13.71429
-0.143 | 6.09524
0.429 | 1.52381
1.000 | 0.00000
1.571 | 1.52381
2.143 | 6.09524
2.714 | 13.71429
3.286 | 24.38095
3.857 | 38.09524
4.429 | 54.85714
5.000 | 74.66667
Gradient Decent in TensorFlow
위 수식을 구현할 예정이다. Cost 함수와 Gradient의 관계는 다음과 같다. Cost 함수에서 최적의 W와 그외의 매개 변수들의 값, 즉, 데이터들을 잘 반영하고 있는 W를 찾게 된다면 Cost가 적은 값을 찾을 수 있다. 이때, 데이터를 잘 반영하는 W를 발견하기 위해서 Gradient Decent를 사용한 것이다.
import platform # pyplot 한글 깨짐 문제
tf.random.set_seed(0) # for reproducibility
X_data = [1., 2., 3., 4.]
Y_data = [1., 3., 5., 7.]
# random으로 [1] 크기의 숫자를 만들어서 W로 전달
# Variable은 변수를 생성할 때, 생성자의 초기값으로 Tensor를 전달받게 된다.
W = tf.Variable(tf.random.normal([1], -100., 100.))
# W = tf.Variable([-100.0])
w_grad = []
cost_grad = []
for step in range(300):
hypothesis = W * X
cost = tf.reduce_mean(tf.square(hypothesis - Y))
alpha = 0.01
gradient = tf.reduce_mean(tf.multiply(tf.multiply(W, X) - Y, X))
decent = W - tf.multiply(alpha, gradient)
W.assign(decent)
w_grad.append(W.numpy()[0])
cost_grad.append(cost)
if (step % 10 == 0):
print('{:5} | {:10.4f} | {:10.6f}'.format(step, cost.numpy(), W.numpy()[0]))
# cost는 0으로, W는 특정값으로 수렴해가는 것을 확인할 수 있다.
step = [i for i in range(300)]
plt.rc('font', family='Malgun Gothic')
plt.plot(step, w_grad)
plt.title("step에 따른 W값 변화")
plt.xlabel("step")
plt.ylabel("W")
plt.show()
plt.plot(step, cost_grad)
plt.title("step에 따른 cost값 변화")
plt.xlabel("step")
plt.ylabel("cost")
plt.show()
>>>
0 | 47604.6680 | -95.286667
10 | 18303.9629 | -58.705444
20 | 7037.8608 | -36.022156
30 | 2706.0520 | -21.956699
40 | 1040.4752 | -13.234994
50 | 400.0621 | -7.826837
60 | 153.8236 | -4.473346
70 | 59.1451 | -2.393913
80 | 22.7412 | -1.104498
90 | 8.7440 | -0.304957
100 | 3.3621 | 0.190822
110 | 1.2927 | 0.498245
120 | 0.4970 | 0.688872
130 | 0.1911 | 0.807075
140 | 0.0735 | 0.880371
150 | 0.0283 | 0.925821
160 | 0.0109 | 0.954003
170 | 0.0042 | 0.971478
180 | 0.0016 | 0.982314
190 | 0.0006 | 0.989033
200 | 0.0002 | 0.993200
210 | 0.0001 | 0.995783
220 | 0.0000 | 0.997385
230 | 0.0000 | 0.998379
240 | 0.0000 | 0.998995
250 | 0.0000 | 0.999377
260 | 0.0000 | 0.999613
270 | 0.0000 | 0.999760
280 | 0.0000 | 0.999851
290 | 0.0000 | 0.999908
HW
경사 하강법 더 찾아보기
댓글남기기