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]]이 된다.
참고자료
'IT > AI\ML' 카테고리의 다른 글
[python/Tensorflow2.x] Mixed Precision API (0) | 2022.01.24 |
---|---|
[논문] point cloud 와 딥러닝(PointNet 간단 해설) (2) | 2022.01.21 |
[python/Tensorflow1.x] 그래프, 세션의 기초와 디버그 방법 그리고 변수의 기초와 재사용 방법 (0) | 2022.01.13 |
[python/Tensorflow1.x] Tensorflow의 이름공간(NameSpace)와 공유 변수(Sharing Variable) (0) | 2022.01.12 |
[python] 결정계수 R2와 자유도 조정 결정 계수 R*2 (0) | 2021.11.28 |