다운로드
작성자: admin 작성일시: 2019-06-29 15:54:23 조회수: 344 다운로드: 22
카테고리: 기타 태그목록:

3.3 함수와 미분

이 절의 내용을 실행하려면 텐서플로우 2.0 버전을 임포트해야 한다.

In [1]:
# 코랩에서는 다음 코드로 베타버전을 설치한다.
# !pip install tensorflow-gpu==2.0.0-beta1
import tensorflow as tf
tf.__version__
Out:
'2.0.0-beta1'

함수

텐서틀로우 함수는 파이토치(PyTorch), 데아노(theano) 등의 딥러닝 라이브러리에 있는 함수 기능을 본따 텐서플로우 버전 2에서 새로 만들어진 방법이다. 함수를 사용하면 텐서플로우 버전 1에서처럼 플레이스홀더(placeholder)와 계산 그래프 등을 명시적으로 사용하지 않고 선언적으로 계산 과정을 구현할 수 있다.

텐서틀로우 함수는 일반 파이썬 함수에 @tf.function 데코레이터를 적용하여 구현한다.

In [2]:
@tf.function
def f(a, b):
  return tf.matmul(a, b)

f
Out:
In [3]:
a = tf.constant([[3, 4]], dtype=tf.float32)
b = tf.constant([[1], [2]], dtype=tf.float32)
c = f(a, b)
c.numpy()
Out:
array([[11.]], dtype=float32)

변수형 텐서

변수형 텐서는 텐서의 값이 바뀔 수 있다. Variable 클래스로 정의하며 항상 초기값을 지정해 주어야 한다. 자료형과 크기는 초기값으로부터 자동으로 유추한다.

In [4]:
# 실수 변수형 텐서
s = tf.Variable(1.0)
In [5]:
# 벡터 변수형 텐서
v = tf.Variable(tf.ones((2,)))
In [6]:
# 행렬 변수형 텐서
x = tf.Variable(tf.ones((2, 1)))

변수 텐서의 값을 바꿀 때는 assign, assign_add, assign_sub 메서드를 사용한다.

  • assign: 값을 완전히 할당. =에 해당
  • assign_add: 값을 증가. +=에 해당
  • assign_sub: 값을 감소. -=에 해당
In [7]:
# 다음과 같이 하면 안된다!. 변수헝 텐서가 상수형 텐서로 변한다!
# x = b
x.assign(b)
x.numpy()
Out:
array([[1.],
       [2.]], dtype=float32)

미분

변수 텐서 혹은 변수 텐서를 포함하는 연산의 결과로 만들어진 텐서를 입력으로 가지는 함수는 그 변수 텐서로 미분한 값을 계산할 수 있다.

  1. tf.GradientTape()로 만들어지는 gradient tape 컨텍스트 내에서 함수값 결과를 저장한 텐서 y를 만든다.
  2. tape.gradient(y, x) 명령으로 변수형 텐서 x에 대한 y의 미분값 계산
In [8]:
with tf.GradientTape() as tape:
    # x가 변수텐서
    y = f(a, x)

dy_dx = tape.gradient(y, x) 
dy_dx.numpy()
Out:
array([[3.],
       [4.]], dtype=float32)

이 때 미분하는 텐서가 상수형이면 결과로는 None이 출력된다.

In [9]:
with tf.GradientTape() as tape:
    y = f(a, x)

dy_da = tape.gradient(y, a) 
dy_da is None
Out:
True

만약 상수형 텐서에 대해 미분하고 싶으면 watch 명령을 사용하여 변수형처럼 바꾸어준다.

In [10]:
with tf.GradientTape() as tape:
    tape.watch(a)
    y = f(a, x)

dy_da = tape.gradient(y, a) 
dy_da.numpy()
Out:
array([[1., 2.]], dtype=float32)

선형회귀 모형

우선 데이터를 만든다.

In [11]:
# 정답인 가중치 벡터
w0 = tf.constant([[3.0], [5.0]])

# 입력 데이터 행렬
X = tf.concat([tf.ones((10, 1)), tf.random.normal((10, 1))], 1)

# 타겟 벡터
y_target = tf.matmul(X, w0) + tf.random.normal((10, 1))

손실함수를 정의하고 w의 초기값에 대해 손실함수 값을 구해본다. 4749.287이 나왔다.

In [12]:
# 손실함수 정의

@tf.function
def loss(y_predict, y_target):
    return tf.reduce_sum(tf.square(y_target - y_predict))

# 변수형으로 정의한 가중치 벡터 (초기값=영벡터)
w = tf.Variable([[0.0], [0.0]])

# 예측치: 변수형 입력을 가진다.
y_predict = tf.matmul(X, w)

loss(y_predict, y_target).numpy()
Out:
294.9298

loss_value를 계산하는 입력 중에 변수형 w가 있으므로 loss_value는 w로 미분할 수 있다. 미분연산으로 그레디언트를 구한다.

In [13]:
def gradient(w):
    with tf.GradientTape() as tape:
        y_predict = tf.matmul(X, w)
        loss_value = loss(y_predict, y_target)

    # loss_value 를 계산하는 입력 중에 변수형 w가 있으므로 미분가능
    g = tape.gradient(loss_value, w) 
    return g

g = gradient(w)
g.numpy()
Out:
array([[-92.34426 ],
       [-62.523922]], dtype=float32)

steepest gradient descent 방법에 따라 가중치를 업데이트 한다.

$$ w_{k+1} = w_k - \mu \cdot g $$
In [14]:
mu = 0.01
w.assign_sub(mu * g)
w.numpy()
Out:
array([[0.9234426],
       [0.6252392]], dtype=float32)

업데이트한 가중치로 다시 손실함수값을 구한다. 2073.6172로 감소하였다.

In [15]:
y_predict = tf.matmul(X, w)
loss(y_predict, y_target).numpy()
Out:
184.52951

이 과정을 반복한다.

In [16]:
loss_values = []
w_0s = []
w_1s = []
for i in range(30):
    # 그레디언트 계산
    g = gradient(w)
    
    # 가중치 업데이트
    w.assign_sub(mu * g)
    
    # 손실함수 계산
    y_predict = tf.matmul(X, w)
    loss_value = loss(y_predict, y_target).numpy()
    loss_values.append(loss_value)
    
    w_0 = w.numpy()[0][0]
    w_1 = w.numpy()[1][0]
    w_0s.append(w_0)
    w_1s.append(w_1)
    
    print(f"[{i:02d}] loss: {loss_value:7.3f}, w_0: {w_0:3.2f}, w_1: {w_1:3.2f}")
[00] loss: 118.011, w_0: 1.62, w_1: 1.13
[01] loss:  77.703, w_0: 2.15, w_1: 1.55
[02] loss:  53.081, w_0: 2.55, w_1: 1.89
[03] loss:  37.873, w_0: 2.85, w_1: 2.18
[04] loss:  28.336, w_0: 3.06, w_1: 2.42
[05] loss:  22.236, w_0: 3.22, w_1: 2.63
[06] loss:  18.235, w_0: 3.34, w_1: 2.80
[07] loss:  15.531, w_0: 3.42, w_1: 2.95
[08] loss:  13.639, w_0: 3.48, w_1: 3.09
[09] loss:  12.268, w_0: 3.51, w_1: 3.20
[10] loss:  11.237, w_0: 3.54, w_1: 3.30
[11] loss:  10.437, w_0: 3.55, w_1: 3.40
[12] loss:   9.798, w_0: 3.55, w_1: 3.48
[13] loss:   9.276, w_0: 3.55, w_1: 3.55
[14] loss:   8.841, w_0: 3.54, w_1: 3.62
[15] loss:   8.475, w_0: 3.53, w_1: 3.68
[16] loss:   8.163, w_0: 3.52, w_1: 3.73
[17] loss:   7.896, w_0: 3.51, w_1: 3.79
[18] loss:   7.665, w_0: 3.50, w_1: 3.83
[19] loss:   7.466, w_0: 3.48, w_1: 3.88
[20] loss:   7.293, w_0: 3.47, w_1: 3.92
[21] loss:   7.143, w_0: 3.46, w_1: 3.95
[22] loss:   7.013, w_0: 3.44, w_1: 3.99
[23] loss:   6.899, w_0: 3.43, w_1: 4.02
[24] loss:   6.801, w_0: 3.42, w_1: 4.05
[25] loss:   6.715, w_0: 3.41, w_1: 4.08
[26] loss:   6.640, w_0: 3.40, w_1: 4.10
[27] loss:   6.575, w_0: 3.39, w_1: 4.13
[28] loss:   6.518, w_0: 3.38, w_1: 4.15
[29] loss:   6.468, w_0: 3.37, w_1: 4.17
In [17]:
plt.subplot(311)
plt.plot(loss_values)
plt.title("loss")
plt.subplot(312)
plt.plot(w_0s)
plt.title("W0")
plt.subplot(313)
plt.plot(w_1s)
plt.title("W1")
plt.tight_layout()
plt.show()
In [18]:
plt.plot(X[:, 1], y_target, "ro")
plt.plot(X[:, 1], y_predict, "b-")
plt.show()

질문/덧글

아직 질문이나 덧글이 없습니다. 첫번째 글을 남겨주세요!