본문 바로가기
Artificial Intelligence/Keras

[Tensorflow 2][Keras][Custom and Distributed Training with TensorFlow] Week1 - Gradient Tape, Gradient Descent using Gradient Tape

by 개발자J의일상 2021. 7. 27.
반응형

본 포스팅은 다음 과정을 정리 한 글입니다.

 

Custom and Distributed Training with TensorFlow

https://www.coursera.org/learn/custom-distributed-training-with-tensorflow?specialization=tensorflow-advanced-techniques 

 

Custom and Distributed Training with TensorFlow

deeplearning.ai에서 제공합니다. In this course, you will: • Learn about Tensor objects, the fundamental building blocks of TensorFlow, understand the ... 무료로 등록하십시오.

www.coursera.org

 

지난 시간 리뷰

 

2021.06.22 - [Artificial Intelligence/Keras] - [Tensorflow 2][Keras][Custom and Distributed Training with TensorFlow] Week 1 - Gradient Tape

 

[Tensorflow 2][Keras][Custom and Distributed Training with TensorFlow] Week 1 - Gradient Tape

모든 기계 학습의 핵심은 매개 변수를 조정하여 특징들(Features)을 Label들과 일치시키는 데 사용되는 함수를 최적화 하는 것입니다. 이러한 함수는 경사 하강 법(Gradient Descent)의 원리에 따라 작동

mypark.tistory.com

 

안녕하세요~ 이번 포스팅은 Gradient Tape 두번째 시간입니다.

지난시간에는 GradientTape를 사용하여 기초적인 optimizer를 구현하여 gradients를 구하고, 정말 간단한 Training과정을 수행하는 방법에 대해 살펴 보았습니다. 이번시간에는 좀 더 구체적으로 설명을 드리고자 합니다.

 

다음은 TensorFlow의 GradientTape API를 사용하여 학습 알고리즘의 Training 단계를 구현하는 방법에 대한 코드입니다. Training을 수행하려면 GradientTape의 context내에서 학습 알고리즘의 두 가지 중요한 단계를 수행해야 합니다. 

첫 번째는 모델의 forward pass를 호출하는 것입니다. (model 호출)

이 예에서는 모델을 호출하고 logits이라는 변수에 예측(output)을 저장합니다.

기계 학습에서 logits은 각 범주 및 다중 클래스 분류에 대한 예측값의 Vector를 나타냅니다. 쉽게말하면 그냥 model의 output인거죠!

이러한 logits은 아직 1이 되도록 합산되지 않았으므로 logits은 일반적으로 softmax 함수에 입력되어 각 범주에 대한 확률로 변환됩니다. 

또 계산해야 할 것은 loss입니다. 우리는 loss를 계산하기 위해 logits과 실제 label vector를 받는 loss_object라는 함수를 호출합니다.  

loss 값을 사용하여 모델의 예측 오류를 줄이기 위해 모델을 업데이트 할 수 있게 됩니다.

 

loss_history라는 list에 loss의 평균을 저장하여 Traning step마다 loss를 저장합니다.

또한 모델 변수와 관련하여 gradient(기울기)를 계산해야 합니다. 

Gradients를 계산하기 위해 tape.gradient를 호출하는데 loss_value와 model중에 train이 가능한 변수들만 update하기 위해 model.trainable_variables를 인자로 넣어줍니다.

grads라는 변수에 저장된 결과에는 학습 가능한 각 변수에 대한 손실의 기울기가 포함됩니다.

마지막으로 optimizer.apply_gradients를 호출하여 grads를 적용합니다.

결국 사용자 지정 훈련 루프에서 이 훈련 단계를 실행하게 됩니다. 

 

 

이제 GradientTape를 사용하여 간단한 방정식의 기울기를 계산하는 방법을 살펴보겠습니다.

이 경우 loss를 w곱하기 w로 설정합니다.

w^2의 도함수는 2w라는 것을 미적분학을 통해 배우셨을 겁니다. 따라서 w^2에 대한 gradient는 2w입니다.

이전 예에서 GradientTape를 사용하여 모델의 forward pass가 계산되는 방법을 보았습니다. 여기에서도 동일하게 수행하지만 대신 w^2 방정식을 계산합니다.

 

 

python을 구문으로 사용하고 tf.GradientTape를 tape로 사용하여 context내에서 실행된 이 작업의 값을 기록해야 합니다. 

그런 다음 TensorFlow의 GradientTape context관리자가 context내에서 실행되는 모든 작업을 tape로 추적하도록 할 수 있습니다. 

여기에서 tape의 개념은 gradient가 context에 있는 동안 기억되고 저장된다는 것입니다. 

마치 테이프의 음악과 비슷하며 context가 완료되면 삭제됩니다. 

이 시나리오의 경우 실제로 저장할 필요는 없지만 좀 더 고차원적인 시나리오에서는 해당 context에서 이전 값을 추적해야 할 수 있습니다.

이런 이유로 이 API에는 Python의 with... as... 문법을 사용하는 건텍스트 기반 객체가 선택되었습니다.

 

 

다음으로 미적분학에서 함수의 미분을 계산하는 방법과 유사하게 tape.gradient를 호출하여 입력 값 w에 대한 loss의 기울기를 계산할 수 있습니다.

우리는 loss에 대한 간단한 표현식 예제를 선택하여 기울기를 계산할 수 있었습니다. (w^2의 미분값)

직접 계산한 값과 GradientTape에서 계산한 값을 비교해봅시다.

 

손실이 w^2이면 w에 대한 기울기는 2w입니다. 

w를 1로 설정하면 기울기는 1의 2배, 즉 2가 됩니다.

GradientTape를 사용하여 동일한 작업을 수행하고 W를 값이 1인 텐서로 정의하고 loss와 w를 전달하는 tape.gradient를 호출하면 2를 가지는 Tensor를 얻을 수 있었습니다. 

 

결국 직접 미분값을 계산할 필요 없이, GradientTape를 사용하는 방법만 터득하면 어려운 loss도 계산할 수 있습니다.

 

TensorFlow 연산을 사용하여 하나 이상의 차원을 갖는 텐서인 고차 텐서에 대한 gradient 계산을 해보겠습니다.

이 예는 변수 z를 y의 함수로 정의한 다음 y를 x의 함수로 정의하여 z가 간접적으로 x의 함수가 되도록 합니다.

변수 x를 모든 1을 포함하는 2x2 행렬로 정의하는 것으로 시작해봅시다.

변수 중 하나 이상이 관찰되는 경우 gradient tape 범위 내의 작업이 기록됩니다.

변수 x를 watch하면 테이블은 with안에 나머지 작업을 관찰합니다.

변수를 관찰하려면 범위 이름에 watch method를 사용합니다.

예를 들어 scope를 t라고 하면 여기에서 t.watch(x)라고 쓸 수 있습니다.

그 다음 y를 x의 함수로 설정합니다.

이 예에서 y는 x의 reduced_sum입니다. x의 이 특정 값에 대해 y는 1 + 1 + 1 + 1, 즉 4가 됩니다.

그런 다음 z를 y의 함수로 설정합니다. 

여기서 z는 y 제곱입니다. x의 이 특정 입력에 대해 y는 4이고 z는 4의 제곱이므로 16입니다.

그런 다음 원래 텐서 x에 대해 z를 미분하면 어떤일이 발생할까요?

z는 4의 제곱이므로 미분은 2*4 = 8입니다. 

원래의 2x2행렬에 대해 미분할 때 8로 채워집니다. 

Gradient Tape가 없으면 손으로 직접 계산을 해야합니다ㅠ

 

 

미적분을 오래전에 배워서 기억 하시지 못하셔도 괜찮습니다. 

이 모든과정을 Gradient Tape가 자동으로 계산해주기 때문입니다 ㅎㅎ

어떻게 gradient를 계산하는지를 알면 좋기 때문에 한번 살펴봅시다.

4개의 값으로 구선된 텐서인 x가 있습니다. 

행렬에 (x1,1), (x1,2), (x2,1), (x2,2)가 있습니다. 

y는 x의 reduced_sum이기 때문에 모든 행렬의 인자를 더합니다. 

또한 z는 y의 제곱입니다. 

우리의 목표는 x에 대한 z의 gradient를 계산하는 것이므로 

위와 같이 식을 적을 수 있습니다. chain rule을 통해 z,y,x의 gradient로 식을 분할 할 수 있습니다.

y에 대한 z의 gradient를 계산한 다음x에 대한 y의 gradient를 계산할 수 있습니다. 

이 둘을 곱하면 x에 대한 z의 gradient를 구할 수 있습니다.

y에 대한 z의 기울기는 z가 y의 제곱이기 때문에 2xy가 됩니다. 

x에 대한 y의 기울기는 1인데 (x1,1)에 대해 y를 미분하면 나머지는 상수가 되고 (x1,1)를 미분하면 결국 1이됩니다. 나머지도 동일합니다.

 

x에 대한 z의 기울기는 실제로 (x1,1), (x1,2), (x2,1), (x2,2)에 대한 z의 기울기입니다. 

위와 같이 식으로 표현할 수 있고 chain rule로 풀어서 쓰면 다음과 같은 식으로 풀어 쓸수 있습니다. 

이제 위에서 구한 값을 넣어봅시다.

이제 이것들을 곱해주면 x에 대한 z의 gradient가 8이라는 것이 나옵니다. 

결국 z는 2x2의 8의 값을 가지는 행렬이됩니다. 

이 값은 t.gradient(z,x)와 동일한 값이라는 것을 확인 할 수 있습니다!

WoW! 이제 우리는 손으로 계산할 필요 없이 t.gradient 함수를 사용하여 손쉽게 gradient를 구할 수 있습니다.

 

GradientTape를 사용하면 gradient method가 호출되는 즉시 tape에 보관된 리소스가 해제됩니다.

다시 사용하려면 persistent = true를 매개변수로 설정하여 GradientTape를 영구적으로 설정할 수 있습니다.

예를 들어 이 경우 GradientTape를 두 번 호출 합니다. 

일반적으로 첫 번째 호출 후에 Gradient가 삭제되지만 이번에는 persistent = true로 설정하므로 Tape를 여러 번 사용할 수 있습니다.

여기 예제에서는 x를 3으로 설정합니다.

GradientTape를 t라고 했을때 tape에 x를 watch하도록 설정하겠습니다.

 

 

y는 x의 제곱으로 설정하고 z는 y의 제곱으로 설정합니다. 결국 z는 x의 4승이 됩니다.

 

z에 대한 x의 미분은 4*x^3이 됩니다. x가 3이므로 결국 108입니다.

일반적으로 t는 이 호출후에 없어지지만 persistent=true이므로 계속 t를 호출할 수 있게 됩니다.

이제 dy_dx를 계산 하게 되는데 dy_dx는 2x이므로 x가 3이니 6이됩니다. 

 

 

tape를 persistent=true로 설정했기 때문에 garbage가 수집되지 않으므로 직접 삭제 명령을 해줘야 합니다.

del t로 tape를 삭제해 줍니다.

이제 여러분은 고차 gradient와 다중 미분을 구할 수 있게 되었습니다~

 

즉, 방정식이 있고 y는 x의 세제곱과 같고 x에 대한 y의 도함수를 취하면 3x^2이 됩니다. 

그런 다음 3x^2의 미분을 취하면 6x가 됩니다. 

이는 도함수의 도함수를 취하기 때문에 고차 gradient 또는 다중 미분의 예입니다.

위 예는 두개의 GradientTape 인스턴스인 tape1과 tape2가 있습니다.

여기서 tape1은 tape2의 블록 내부에 중첩됩니다.

내부 tape에서 y를 x^3으로 설정하고 y의 기울기를 계산할 수 있습니다. 

x와 관련하여 dy_dx에 저장합니다.

그런 다음 외부 tape를 사용하여 x에 대한 d2y_d2x를 계산합니다.

d2y_d2x는 3x^2의 미분 값인 6x가 됩니다. 

이러한 중첩 구조에 대해서 잘 기억해 두시면 될 것같습니다.

이제 미분을 위한 tensor 및 GradientTape 사용법에 대해 핵심 내용만 설명을 드렸습니다. 

잘 소화가 되셨는지 모르겠습니다 ㅎㅎ 

감사합니다.

300x250

댓글