매직코드
article thumbnail

단순회기분석을 텐서플로우로 구현해보는 코드다. 나는 쥬피터랩에서 코드를 구현했다.

나도 데이터분석을 시작한 지 얼마 되지 않기 때문에 최대한 자세히 설명하려고 한다.

#필요한 라이브러리 가져오기
import tensorflow as tf
import numpy as np

라이브러리는 우리라 코드를 작성할 때 필요한 함수식을 모아둔 곳이라고 생각하면 좋다.

각각의 명령어에 기능들이 포함되어있으니 자주 사용하는 numpy와 pandas에는 어떤 기능이 있는지 알아두면 좋다.

 

 

 

import numpy as np

import 라이브러리 이름 as 사용할 약자

라이브러리는 기본적으로 import를 통해 불러오고, as를 통해 약자를 정해준다.

코드에서 numpy라고 다 적어줘도 상관은 없지만 빠른 코딩을 위해 np라고 약자로 적기 위함이다.

 

# 데이터 세트 만들기
x_data = [1, 2, 3, 4, 5]
y_data = [1, 2, 3, 4, 5]

지금은 단순선형회귀를 보기 위해 데이터세트를 만들었지만 보통은 공공데이터(.csv) 등을 불러와서 사용한다.

여기서는 간단하게 만들기 위해 x와 y에 들어갈 데이터를 ndarray형식으로 만들어줬다.

DataFrame도 많이 쓰는데 ndarray는 numpy소속이고 DataFrame은 pandas소속이라서 형태를 잘 맞춰줘야한다.

 

# 시각화
import matplotlib.pyplot as plt
plt.plot(x_data, y_data, 'o')
plt.ylim(0, 8)

위에서 만든 데이터세트가 어떻게 생격는지 눈을 확인하기 위해 시각화를 해준다.

시각화에서 가장 많이 사용하는 라이브러리 중 하나인 matplotlib를 불러오고, 그 중에서도 우리는 pyplot을 사용할 예정이다.

위에서 numpy처럼 모든걸 불러오지 않고 .pyplot으로 일부분만 불러온 이유는 사용하지 않을 기능을 모두 불러오면 코드가 무거워지기 때문이다.

 

 

 

plt.plot(x_data, y_data, 'o')

plt.plot(x값, y값, '그래프에 데이터를 표시할 모양')

우리가 사용하고자 하는 pyplot을 plt라는 약자를 이용해서 불러오고 .plot을 통해 그래프를 불러온다.

x값을 넣어줘야 하는 자리에 x_data, y값을 넣어줘야 하는 자리에 y_data, 마지막으로 데이터를 표현할 모양을 'o'로 지정해준다.

 

지금까지의 코드를 실행해보면 위와 같은 그래프가 나온다.

이제 이 그래프에 회귀선을 그려주려고 한다. 데이터가 많은 경우에는 LinearRegression()을 사용하여 회귀식을 구하는데, 회귀선이 어떻게 그려지는지 이해를 돕기 위해 회귀선이 생기는 과정을 하나씩 나타내보려고한다.

 


 

가설함수

함수 H(x) 는 가설함수, 예측함수 라고도 부르기도 하고 위 식에서 W는 가중치, b는 편향을 나타내는 일차식이다.

이 함수를 기반으로 cost값을 찾는다.

여기서 cost값이란 회귀함수에 쓰이는 W, b값을 말한다.

식을 풀이해보자면 (H(x) : 예측값 - y : 실제값)제곱한 값의 평균을 구한것인데, 우리는 이 식에서 (예측값 - 실제값)을 오류값이라고 부른다. 실제로 cost는 오류값의 제곱한 값들의 평균을 의미한다. 머신러닝에서는 이 cost값이 가장 작아지는 값이 되도록 W, b값을 찾는다.

cost가 작아지기 위해서는 오류값이 작아져야 가능하다. 따라서 cost가 작다는 의미는 오류가 최소화 된다는 뜻이다.

 

위의 코드에 이어서 작성한다. 먼저 가설함수 H를 만들어준다.

# 가설함수 만들기
W = tf.Variable(2.9)
b = tf.Variable(0.5)

hypothesis = W * x_data + b

위에 설명을 읽다보면 이상함을 느낄 수 있는데, H함수를 통해 cost, 즉 W와 b값을 발견하겠다고 했지만 실제로 H함수에도 W와 b값이 사용되기 때문에 처음에는 W와 b에 임의의 수를 설정해줘야한다.

 

tf.Variable()

위 코드에서 W에 임의의 숫자 2.9를 넣어주었는데 W = 2.9라고 작성하지 않고 W = tf.Variable(2.9)라고 작성한 이유는 W값은 cost를 계산할수록 변경되어 최적의 값을 찾아가야하기 때문에 변경이 가능하다는 의미인 tf.Variable()에 넣어주었다. b도 마찬가지로 임의의 숫자 0.5를 입력할 예정이고, cost 계산을 여러번 하면서 점차 최적의 값으로 변경되어야 하기 때문에 tf.Variable()를 사용했다.

 

가설함수 H는 일차식이기 때문에 우리가 임의로 넣어준 숫자에 의해서 그래프에 나타낼 수 있다.

# 기존 그래프에 가설함수 시각화
plt.plot(x_data, y_data, 'o')
plt.plot(x_data, hypothesis.numpy(), 'r-')
plt.ylim(0, 8)
plt.show()

위에서 한 번 사용해봤던 시각화 명령어인 plt.plot(x_data, y_data, 'o')을 이용해 기존에 만들었었던 그래프를 가져온다. 그리고 plt.plot()을 한번 더 사용해 그리고 싶은 데이터를 가져오면 하나의 그래프에 두 개의 plot이 그려진다.

 

plt.plot(x_data, hypothesis.numpy(), 'r-')

plt.plot(x값, y값, r-:빨간색선)

여기서 hypothesis는 형태가 tensor로 되어있는데 .numpy()를 통해서 넘파이형태로 변경해줬다. 나의 경우 넘파이로 변경하지 않아도 그래프에 잘 표기 되었는데 혹시 그래프에 나타나지 않는다면 .numpy()를 통해 형태 변형을 해주면 된다.

 


 

이제 cost계산식을 만들어준다.

cost = tf.reduce_mean(tf.square(hypothesis - y_data))

tf.square()

괄호 안에 있는 값을 제곱한다는 의미다.

여기서는 예측값인 hypothesis에서 실제값인 y_data를 뺀 값을 제곱했다.

 

tf.reduce_mean()

괄호 안에 있는 값들의 평균을 구해주는 식이다.

원래 mean()이라는 평균을 구해주는 식이 있는데 여기서 사용된 reduce_mean()은 차원이 축소되면서 평균을 구해준다는 의미로 아직 자세히 이해하지 못해도 상관없다. 여기서는 그냥 평균을 구해줬다고만 생각해도 된다.

 


 

경사하강법

위에서 설명한 바와 같이 cost를 계산하는 이유는 오류값을 최소 출력하는 W와 b값을 찾기 위함이다. 이 때 최솟값을 찾게하는 공식이 경사하강법이다.

# 경사하강법 공식
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_grad.numpy(), b_grad.numpy()

텐서플로우에서는 경사하강법을 이용할 수 있게 메서드를 제공한다. with를 이용해 tf.GradientTape()을 불러온다. 

with구문 안에 있는 여러 식들을 계산하고, 그 계산된 변수를 tape안에 저장한다.

 

tape.gradient(cost, [W, b])

tape.gradient(함수, [변수1, 변수2])

tape에 저장되어 있는 값들 중 gradient를 이용했던 변수값들을 불러오라는 명령어다.

gradient에서는 변수를 구하는 함수식이 cost였기 때문에 함수를 넣는 부분에 cost를 넣어주고, 변수인 W, b을 넣어준다.

이 때 tape가 저장하고 있던 값은 우리가 변수를 적어준 순서대로 튜플형식으로 반환된다.

즉, tape에 저장되어있던 W 값은 W_grad에 튜플형태로 입력되고, b값은 b_grad에 튜플형태로 입력된다.

우리는 그 값을 보기 위해서 뒤에 .numpy()를 붙여 넘파이 형식으로 변환하여 값을 확인한다.

 

# 파라미터 업데이트
learning_rate = 0.01

W.assign_sub(learning_rate * W_grad)
b.assign_sub(learning_rate * b_grad)

W.numpy(), b.numpy()

경사하강법을 이용해서 구하게 된 W_grad와 b_grad값을 실제 W와 b값에 얼마나 적용할 지 정해주는 learning_rate를 통해 최적의 W, b값을 찾는다. 결국 최적의 W, b값을 찾는 순서는 임의의 값 W, b --> 경사하강법 공식을 통해 좀 더 괜찮은 W, b값 발견, learning_rate를 적용해 임의의 값 W, b를 새로운 값으로 업데이트 --> 최적의 W, b값을 찾을 때까지 반복이다.

 

W.assign_sub(learning_rate * W_grad)

데이터.assign_sub(계산값)

assign_sub는 지금 데이터값에 계산한 데이터 값은 덮어쓰기 하라는 의미이다.

여기서는 W값에 learning_rate*W_grad를 계산한 값을 넣어주라는 의미이다.

 

예를들어 A.assign_sub(A+B), A = 10, B = 10인 경우

첫번째 계산하면 A = 10(A) + 10(B) = 20

두번째 계산하면 A = 20(A) + 10(B) = 30

세번째 계산하면 A = 30(A) + 10(B) = 40....이런식으로 계산이 된다.

 

지금까지 작성했던 코드를 100번 반복하는 반복문을 작성해보자.

# 경사하강법으로 최적의 W, b값을 찾아 회귀선 그리기

for i in range(100):
    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)
    
    if i % 10 == 0:
      print("{:5}|{:10.4f}|{:10.4f}|{:10.6f}".format(i, W.numpy(), b.numpy(), cost))

plt.plot(x_data, y_data, 'o')
plt.plot(x_data, hypothesis.numpy(), 'r-')
plt.ylim(0, 8)

 

for i in range(100):

for : 반복해라

i : 임의의 숫자 (이 임의의 숫자는 for문 안에 있는 함수식에서 사용된다)

in: ~안에서

range(100) : 숫자범위 1~100까지 (총 100번 반복한다는 의미)

 

 

if i % 10 == 0:

if :만약에 i를 10으로 나눴을 때 나머지가 0과 같다면:

 

print("{:5}|{:10.4f}|{:10.4f}|{:10.6f}".format(i, W.numpy(), b.numpy(), cost))

print() : 출력해달라

"" : 문자열

문자열 안쪽의 {}괄호마다 .format()순서에 맞춰서 입력이 된다.

{:5}같은 경우에는 i값을 출력할껀데 칸의 넓이를 5칸으로 하라는 의미이고

두번째 {:10.4f}는 W.numpy()를 10칸의 넓이로 출력하면서 .4f가 소수점 아래 4개까지 표현해달라는 의미다.

세번재 {:10.4f} 역시 b.numpy()를 10칸의 넓이로 출력하면서 소수점 아래 4개까지 표현하고

마지막 {:10.6f}는 cost값을 10칸의 넓이로 소수점 아래 6자까지 출력하라는 의미다.

 

위 코드를 실행하면 아래와 같은 출력값이 나온다.

총 100번을 반복실행 하는 중에 i번째의 | W값 | b값 | cost값과 시각화 결과이다.

이렇게 학습시킨 모델을 통해 예측을 해보자.

사실 이 그래프는 x가 1일때 y가 1, x가 2일때 y가 2....이런식으로 증가하는 y=x 일차식이다.

H함수식으로 본다면 W값은 1에 가까운 값이고, b는 0에 가까운 값이 되어서 H = Wx + b 가 H = x가 되었다고 보면 된다.

그렇다면 우리가 예측하고 기대하는 것은 이 함수식에 x값에 6을 넣었을 때 y값도 6에 가까운 값이 나오는가 확인하는 것이다.

# 예측 확인 x=6일 때 y값?
print(W * 6 + b)

나의 경우 결과값이 6.0115094로 6에 가까운 값이 출력되었다. 이건 모델이라기 보다는 함수식을 하나 만들어서 x값을 넣어본 것인데 나중에는 엄청 많은 데이터에 대하여 학습시킨 모델을 만들어 predict()를 통해 예측하는 알고리즘을 만들게 된다.

 


코드실습 다른 글

 

 

 

 

profile

매직코드

@개발법사

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!