IT/AI\ML

[python/TensorFlow2.x] TensorFlow의 GradientTape에 대한 간단한 설명

개발자 두더지 2022. 1. 15. 23:29
728x90

 TensorFlow에는 GradientTape이라는 클래스가 있다. 이 클래스는 기울기를 구하기 위한 클래스이다. 정밀도가 좋은 예측 모델을 만들기 위해서는 적당히 선택된 파라미터로 예측과 실제 결과를 비교하여 그 차이가 작아지도록 파라미터를 반복해서 조정한다. 이때, 파라미터를 어느정도 줄이거나 늘려야 좋을지를 결정하는 것이 "기울기"이다.  이번 포스팅에서는 코드를 살펴보며 사용하는 방법에 대해 알아보고자한다.

 

import tensorflow as tf

tf.enable_eager_execution() # 계산 그래프를 즉시 실행하는 Eager execution를 사용한다.
x = tf.constant(3.0) # TensorFlow에 정수를 작성
with tf.GradientTape() as g: # with내부에 계산을 기록
  g.watch(x) # x를 기록
  y = 2 * x # 미분식

g.gradient(y, x).numpy() # x = 3때의 y를 구한다. numpy()는 출력값을 보기 쉽게 하기 위함이다.
2.0

 g.gradient(y, x)는 미분을 하며 x = 3일 때의 y 값을 반환한다. 2 * x를 미분하면 2이므로, 이 경우에는 x가 어떤 값이어도 2가 반환된다.

 이러한 프로그램적 미분 방법은 Automatic differentiation이라고 부르는듯하다. 

  • tf.constant(2.0) 이라는 것은 TensorFlow 세계에서 사용하는 데이터 구조이다. 이것을 Tensor이라고 부른다. numpy와 같이 행렬연산을 간략한 구문으로 작성하는 것이 가능하다. numpy와 다른 점은 GPU의 메모리에 데이터를 유지한다. 
  • with tf.GradientTape() as g : 이라는 구문에 대해서 설명하자면, 알다시피 기본적으로 with 문은 Python에서 자동적으로 파일을 닫는 문법을 작성할 때 자주 사용된다. 코드 블록의 처음에는 지정한 오브젝트의 __enter___()가 호출되고, 나올 때는 __exit__()가 호출된다. as로 지정한 변수는 __enter__()의 반환 값으로, target이라고 부른다.

 위의 예에서는 g.watch(x)를 사용하여 명시적으로 기록하는 변수를 지정하고 있지만, 문서에 의하면 tf.Variable 등은 자동적으로 기록 대상에 포함되는 듯하여 시험해보았다.

x = tf.Variable(3.0) # TensorFlow에 변수를 작성
with tf.GradientTape() as g:
  y = x * x # 미분하는 식

g.gradient(y, x).numpy()
6.0

 x*x 를 미분하면 2*x이므로, x=3일 때는 6이 반환된다. 그럼 조금 더 복잡한 예를 살펴보자. 아래의 예에서 볼 수 있는 if식 등이 들어가도 문제없이 미분된다.

def with_if(x):
    if x <= 0:
        return x * 2
    else:
        return x * x * x

def with_if_gradient(n):
    x = tf.Variable(n)
    with tf.GradientTape() as g:
      y = with_if(x)
    print('n = %.1f 때의: gradient = %.1f' % (n, g.gradient(y, x)))

with_if_gradient(-3.0)
with_if_gradient(0.0)
with_if_gradient(3.0)
n = -3.0 때의: gradient = 2.0
n = 0.0 때의: gradient = 2.0
n = 3.0 때의: gradient = 27.0

 이 예에서는 

  •  x <= 0일 때는 x*2 이므로 미분하면 항상 2가 된다.
  • x > 0일 때는 x * x * x 이므로 미분하면 3 * x * x가 된다.

 실행하면 제대로된 값이 반환되고 있다. tf.Variable가 단순한 값이 아닌 미분에 필요한 식의 정보를 보유하고 있다고 생각된다.

 g.gradient(y, x)의 두 번째 인수는 y안에 사용되고 있는 변수라면 어떠한 것이라도 어떤 순서대로 쓸 수 있다. 예를 들면 아래와 같이 다변수 식의 미분을 생각해보자.

a = tf.Variable(3.0)
b = tf.Variable(4.0)

with tf.GradientTape() as g:
    f = a ** 2 + 5 * b

g.gradient(f, [a, b])

 

[<tf.Tensor: id=2783, shape=(), dtype=float32, numpy=6.0>,
 <tf.Tensor: id=2772, shape=(), dtype=float32, numpy=5.0>]

 [a, b]의 순으로 gradient에 전달되면 [3, 4]일 때는 [6. 5]이 반환된다([2 *3, 5]).

 gradient의 반환값은 두 번째 인수의 구조에 따라 그대로 반환된다. 예를 들어, 트릭이지만 [b, [a]]와 같이 전달하면,

a = tf.Variable(3.0)
b = tf.Variable(4.0)

with tf.GradientTape() as g:
    f = a ** 2 + 5 * b

g.gradient(f, [b, [a]])

[5, [6]]이 된다.


참고자료

https://qiita.com/propella/items/5b2182b3d6a13d20fefd

728x90