hwijin97 2021. 11. 24. 20:56

텐서플로를 사용한 사용자 정의 모델과 훈련

자신만의 손실함수, 지표, 층, 모델, 초기화, 규제, 가중치 규제 등을 텐서플로우 훈련에 적용하는 방법을 알아본다.

 

 

12.1 텐서플로 훌어보기

- 넘파이와 동일한 핵심구조 + GPU지원

- 여러 장치와 서버에 분산 컴퓨팅

-  JIT 컴파일러포함으로 속도와 메모리 사용량 최적화를 수행한다. + 파이썬 함수에서 계산그래프computation graph 를 추출해 효율적으로 수행힌다.

- 계산 그래프의 플랫폼 중립적인 포맷으로 여러 환경에서 훈련, 추론이 가능하다

- 자동 미분, RMSProp, Nadam 고성능 옵티마이저를 지원한다.

 

고수준 딥러닝 API 자동 미분 텐서보드 시각화 tf.xla tf.signal
tf.keras tf.GradientTape tf.summary 특수한 데이터 구조 tf.random
tf.estimator tf.gradients() 배포와 최적화 tf.lookup tf.bitwise
저수준 딥러닝 API 입출력과 전처리 tf.distribute tf.nest 그 외
tf.nn tf.data tf.saved_model tf.sets tf.compat
tf.losses tf.feature_column tf.autograph tf.sparse tf.config
tf.metrics tf.audio tf.graph_util tf.strings ...
tf.optimizers tf.image tf.lite 선형 대수와 신호처리  
tf.train tf.io tf.quantization tf.math  
tf.initializers tf.queue tf.tpu tf.linalg  

 

텐서플로는 윈도우, 리눅스, 맥os, ios, android 에서도 실행 가능하고,

C++, 자바, Go, 스위프트, JS 으로도 사용가능하다.

 

시각화를 위한 텐서보드

텐서플로 제품화를 위한 라이브러리 TFX tensorflow extended( https://tensorflow.org/tfx )

 

TensorFlow Extended(TFX) | ML 프로덕션 파이프라인

엔드 투 엔드 프로덕션 ML 파이프라인을 빌드하고 관리합니다. TFX 구성요소가 확장 가능한 고성능 데이터 처리, 모델 학습 및 배포를 지원합니다.

www.tensorflow.org

다양한 텐서플로 모델 저장소 및 사전훈련 신경망 저장소 텐서플로 허브 Tensorflow Hub ( https://github.com/tensorflow/models )

 

GitHub - tensorflow/models: Models and examples built with TensorFlow

Models and examples built with TensorFlow. Contribute to tensorflow/models development by creating an account on GitHub.

github.com

텐서플로 리소스 페이지 ( https://www.tensorflow.org/resources )

 

모델 및 데이터 세트  |  TensorFlow

저장소와 기타 리소스를 탐색하여 TensorFlow 커뮤니티에서 만든 모델 및 데이터 세트를 찾아보세요.

www.tensorflow.org

텐서플로 기반 프로젝트 https://www.github.com/jtoy/awesome-tensorflow 

 

GitHub - jtoy/awesome-tensorflow: TensorFlow - A curated list of dedicated resources http://tensorflow.org

TensorFlow - A curated list of dedicated resources http://tensorflow.org - GitHub - jtoy/awesome-tensorflow: TensorFlow - A curated list of dedicated resources http://tensorflow.org

github.com

 

논문 + 구현 https://paperswithcode.com 

 

Papers with Code - The latest in Machine Learning

Papers With Code highlights trending Machine Learning research and the code to implement it.

paperswithcode.com

 

12.2 넘파이처럼 텐서플로 사용하기

텐서플로 api는 텐서를 순환시켜 연산을 진행한다.

 

12.2.1 텐서와 연산

tf.Tensor 은 변경 불가능한 객체이다. 

기본 정의 -> 넘파이와 비슷함

t = tf.constant(1)
t = tf.constant([[1,2,3], [4,5,6]])
t.shape
t.dtype
'''
tf.Tensor(
[[1. 2. 3.]  
 [4. 5. 6.]], shape=(2, 3), dtype=float32)
tf.Tensor(42, shape=(), dtype=int32)
(2, 3)
<dtype: 'float32'>
'''

인덱스 참조

t[:, 1:]
t[..., 1, tf.newaxis]
'''
tf.Tensor( 
[[2. 3.] 
 [5. 6.]], shape=(2, 2), dtype=float32)
tf.Tensor(
[[2.]
 [5.]], shape=(2, 1), dtype=float32)
 '''

 

텐서를 이용해 모든 종류의 연산이 가능하다

t + 10 #tf.add(t, 10)
t.square(t)
t @ tf.transpose(t)  # tf.malmat(t, tf.transpose(t))
'''
tf.Tensor(
[[11. 12. 13.]
 [14. 15. 16.]], shape=(2, 3), dtype=float32)
tf.Tensor(
[[ 1.  4.  9.]
 [16. 25. 36.]], shape=(2, 3), dtype=float32)
tf.Tensor(
[[14. 32.]
 [32. 77.]], shape=(2, 2), dtype=float32)
 '''

 

케라스 저수준 API

keras.backend 에서 사용되는 연산들은 tf.keras를 사용할경우 tf  에 상응하는  연산을 호출한다.

 

12.2.2 텐서와 넘파이

텐서를 넘파이에, 넘파이 배열을 텐서플로에 적용가능하다.

n = np.array([2., 3., 4.])
tf.constant(n)
t.numpy()
tf.square(n)
np.square(t)
'''
tf.Tensor([2. 3. 4.], shape=(3,), dtype=float64)
[[1. 2. 3.]
 [4. 5. 6.]]
tf.Tensor([ 4.  9. 16.], shape=(3,), dtype=float64)
[[ 1.  4.  9.]
 [16. 25. 36.]]
 '''

넘파이는 기본적으로 float64 를 사용하고 텐서플로에서는 float32를 사용한다. 32도 정밀도와 메모리 효율이 좋기 때문에 변경하려면 dtype=tf.float32 로 변경한다.

 

 

12.2.3 타입 변환

텐서플로는 타입을 자동으로 변환하지 않고 예외를 발생시킨다. 타입변환은 성능을 감소시킬수 있고, 자동 변환시 사용자가 눈치채지 못할 수 있기 때문이다.

정수와 실수, 32비트 실수와 64비트 실수 등을 연산할수 없다.

tf.constant(2.) + tf.constant(40)
'''
InvalidArgumentError: cannot compute AddV2 as input 
#1(zero-based) was expected to be a float tensor but is a int32 tensor [Op:AddV2]
'''

타입변환에는 tf.cast 를 사용한다.

t2 = tf.constant(40., dtype=tf.float64)
tf.constant(2.) + tf.cast(t2, tf.float32)
'''
<tf.Tensor: shape=(), dtype=float32, numpy=42.0>
'''

 

12.2.4 변수

tf.Tensor 은 변경 불가능한 객체이다.  일반적으로 역전파로 변경되는 가중치로 사용할 수 없다.

tf.Variable([[1., 2., 3.], [4., 5., 6.]])
'''
<tf.Variable 'Variable:0' shape=(2, 3) dtype=float32, numpy=
array([[1., 2., 3.],
       [4., 5., 6.]], dtype=float32)>
'''

tf.Variable은 tf.Tensor 와 비슷하게 동작하는데, assign() 메서드를 이용해 변경된다. 

인덱싱은 scatter_update , scatter_nd_update 를 이용해 업데이트 한다.

v
v.assign(v * 2)
v[0, 1].assign(42)
v[:, 2].assign([0., 1.])
v.scatter_nd_update(indices=[[0, 0], [1,2]], updates=[100., 200.])
'''
<tf.Variable 'Variable:0' shape=(2, 3) dtype=float32, numpy=
array([[1., 2., 3.],
       [4., 5., 6.]], dtype=float32)>
<tf.Variable 'Variable:0' shape=(2, 3) dtype=float32, numpy=
array([[ 2.,  4.,  6.],
       [ 8., 10., 12.]], dtype=float32)>
<tf.Variable 'Variable:0' shape=(2, 3) dtype=float32, numpy=
array([[ 2., 42.,  6.],
       [ 8., 10., 12.]], dtype=float32)>
<tf.Variable 'Variable:0' shape=(2, 3) dtype=float32, numpy=
array([[ 2., 42.,  0.],
       [ 8., 10.,  1.]], dtype=float32)>
<tf.Variable 'Variable:0' shape=(2, 3) dtype=float32, numpy=
array([[100.,  42.,   0.],
       [  8.,  10., 200.]], dtype=float32)>
'''

실전에서는 add_weight() 메서드를 사용해서 변수 생성을 대신 처리한다. 또한 모델파라미터는 옵티마이저가 자동으로 업데이트 한다.

 

 

12.2.5 다른 데이터 구조

 

- 희소  텐서 sparse tensor ( tf.SparseTensor )

대부분 0 으로 채워진 텐서, tf.sparse 는 희소텐서를 위한 연산을 지원한다.

https://colab.research.google.com/drive/1RmsihCnizpSmKaQDKj6ZadOMAOP7QaOM#scrollTo=Sparse_Tensor

 

- 텐서 배열 tensor array ( tf.TensorArray )

텐서의 리스트, 고정된 길이를 가지지만 동적으로 변경 가능. 리스트의 모든 텐서의 크기와 타입이 동일해야 한다.

 

 

- 래그드 텐서 ragged tensor ( tf.RaggedTensor ) 

리스트의 리스트, 각 리스트의 길이는 달라도됨. tf.ragged 에 연산 지원

https://colab.research.google.com/drive/1RmsihCnizpSmKaQDKj6ZadOMAOP7QaOM#scrollTo=String_Array_Ragged_Tensor

 

- 문자열 텐서 string tensor

tf.string 타입의 텐서, 바이트 문자열. 바이트 문자열이기 때문에, tf.int32 으로 유니코드 문자열로 표현 가능하다.

tf.strings 패키지에 바이트 문자열과 유니코드 문자열 변환 연산을 지원한다.

https://colab.research.google.com/drive/1RmsihCnizpSmKaQDKj6ZadOMAOP7QaOM#scrollTo=String

 

- 집합 set

일반적인 텐서 or 희소 텐서로 나타내고, 마지막 축의 벡터는 각 집합으로 표현된다.

https://colab.research.google.com/drive/1RmsihCnizpSmKaQDKj6ZadOMAOP7QaOM#scrollTo=Set

 

- 큐 queue

단계별로 텐서를 저장한다. FIFO, PriorityQueue, RandomShuffleQueue, PaddingFIFOQueue, 등을 tf.queue에서 지원한다.

 

 

12.3 사용자 정의 모델과 훈련 알고리즘

12.3.1 사용자 정의 손실함수

훈련 세트에 잡음이 좀 있고, 회귀 모델을 훈련하는 예시에서 손실함수를 정의해본다.

- 평균 제곱오차는 큰 오차에 너무 과한 벌칙을 가한다 -> 잡음에 너무 강한 벌칙을 가해 정확하지 않을 수 있다.

- 평균 절댓값 오차는 이상치에 관대하고, 훈련이 수렴까지 오래걸리고, 모델이 정밀하지 않다.

-> 둘을 조합한 후버 손실을 적용해본다.

https://colab.research.google.com/drive/1RmsihCnizpSmKaQDKj6ZadOMAOP7QaOM#scrollTo=User_Define_Loss_Function

 

 

12.3.2 사용자 정의 요소를 가진 모델을 저장하고 로드하기

 

https://colab.research.google.com/drive/1RmsihCnizpSmKaQDKj6ZadOMAOP7QaOM#scrollTo=User_Define_Model_Save_And_Load

케라스는 모델을 저장할때, 함수이름을 저장해서 문제되지 않지만, 로드할때 저장된 함수이름과 실제 함수를 맵핑하는데 문제가 생긴다.

따라서 로드할때, 저장된 함수이름과 실제 함수를 매핑해주는 딕셔너리를 추가로 넣어준다.

 

사용자 정의 손실함수에 매개변수를 지정하는 방법 : 

매개변수를 받으면서 정의한 손실함수를 반환하는 껍데기 함수를 만들어 호출한다.

 

사용자 정의 손실함수 클래스를 만드는 방법 :

keras.losses.Loss 함수를 상속받아 init 에 지정할 매개변수를 설정하고, call 에서 원하는 연산을 수행한다.

지정할 매개변수는 get_config 함수를 오버라이딩해서 부모의 config 딕셔너리에 추가하여 반환한다.

 

 

12.3.3 활성화 함수, 초기화, 규제, 제한을 커스터마이징 하기

 

https://colab.research.google.com/drive/1RmsihCnizpSmKaQDKj6ZadOMAOP7QaOM#scrollTo=User_Define_Activation_Initialization_Regularization_Constraint

활성화, 초기화, 규제, 제한 함수에 사용하는 매개변수는 정의하는 함수 종류에 따라 다르다.

 

layer에 커스텀한 함수들을 적용하면,

처음에 가중치초기화를 수행하고,

층의 출력에 활성화 함수를 적용하고, 그 결과를 다음층에 전달한다.

훈련스텝이 끝날때마다, 가중치가 규제함수에 전달되고, 규제 손실을 계산해 전체 손실에 추가되어 훈련을 위한 최종 손실을 만든다.

가중치가 업데이트되면 마지막으로 훈련스템마다 제한함수가 호출되어 층의 가중치를 제한한 가중치 값으로 바꾼다.

 

함수에 모델과 함께 저장해야할 하이퍼파라미터가 존재하면 , 

keras.regularizers.Regularizer, keras.constraints.Constraint, keras.initializers.Initializer, keras.layers.Layer 와 같이 적절한 클래스를 상속받아서 위와같이 config 에 업데이트한다.

 

 

12.3.4 사용자 정의 지표

 

https://colab.research.google.com/drive/1RmsihCnizpSmKaQDKj6ZadOMAOP7QaOM#scrollTo=User_Define_Metrics

 

손실과 지표는 개념적으로 다르지않다. 손실은 모델을 훈련하기위해 경사하강법에서 사용하기 때문에, 그레디언트가 연속적이어야 하고, 모든 곳에서 0 이아닌게 좋다. 이는 조건만 만족되면 사람의 이해가 필요하지않다. 

하지만 지표는 모델을 평가하는데 사용한다. 훨씬 이해하기 쉬워야하고, 미분 가능하지않거나 0 여도 상관없다.

 

 지표를 계산하기 위해서 케라스는 에포크가 시작할 때부터 평균을 기록하지만, 이 방식이 올바르게 적용되지 않을 때가 있다.

예를들어 정밀도를 계산할때 평균을 계산하는게 아니라 모델의 양성예측을 기록하고 정밀도를 계산하는 객체가 필요하다.

keras.metrics.Precision()의 경우 배치마다 점진적으로 값이 업데이트 되기 때문에, 스트리밍 지표streaming metric( 상태가 있는 지표stateful metric ) 이라고 부른다.

 

result() 메서드로 현재 지표값

variables 속성으로 기록중인 변수

reset_state() 메서드로 변수 초기화

를 수행가능하다.



Precision 같은 스트리밍 지표를 만들기위해서 keras.metrics.Metric 클래스를 상속받아 만든다.

 

init 에서 add_weights 으로 변수를 생성하고, update_state 에서 변수(텐서 변수)에 assign_add 메서드로 값을 더한다.

result 메서드에서 지표에 맞는 연산을 반환한다.

 

 

12.3.5 사용자 정의 층 

 

https://colab.research.google.com/drive/1RmsihCnizpSmKaQDKj6ZadOMAOP7QaOM#scrollTo=User_Define_Layer

텐서플로에는 없는 특이한 층을 만들기 위해서 사용자 정의 층을 만든다. 혹은 반복되는 층들을 하나의 사용자 정의 층으로 다뤄 모델을 간단하게 만들거나 할때  편리하다.

 

가중치가 없는 층

Flatten , Reshape, ReLU 같은 가중치가 없는 층들은 파이썬 함수를 만들후에 layers.Lamda 함수로 감싸는게 가장 간단하다.

이렇게 만든 층을 시퀀셜, 함수형, 서브클래싱 API 의 층으로 사용가능하다.

 

가중치가 있는 층

가중치가 있는 층은 keras.layers.Layer 을 상속해서 만든다.

init 에서 이 층이 어떤 역학을 하는지에따라 받는 매개변수가 달라진다. Dense 일경우 units ... 

build 메서드에서 변수를 생성 및 초기화 하고

call 메서드에서 연산을 수행하고,

compute_output_shape 메서드에서 층의 출력 크기를  tf.TensorShape 타입으로 반환한다.

 

여러개의 입력과 출력을 가진 층

여러개의 입력을 받는 층의경우 call 메서드에서 튜플로 값을 전달하고 여러개의 출력을 반환할경우 리스트로 묶어 반환한다. 또한 compute_output_shape 에서도 튜플로 값을 전달받아 출력하는데 여러개의 출력이라면 동일하게 리스트로 묶어서 반환한다.

 

훈련과 테스트에서 다르게 동작하는 층

call 메서드의 training 매개변수를 추가하여 구분한다. 이외는 동일함

 

 

12.3.6 사용자 정의 모델

 

https://colab.research.google.com/drive/1RmsihCnizpSmKaQDKj6ZadOMAOP7QaOM#scrollTo=User_Define_Model_SubClassAPI

서브클레싱 API 는 keras.Model 을 상속받고 작업을 call 에 구현하는 방식이다.

 

1개의 완전연결 - 2개의 완전연결과 스킵연결로 이루어진 잔차 블럭residual block 을 3번 반복후에 완전연결된 출력층에 도달하는 모델을 정의해보자.

 

잔차블럭 정의

Layer 을 상속받고, 2개의 완전연결 레이어를 속성으로 지닌다.

call 메서드에서 2개의 완전연결을 통과한 값과 입력을 더한 값을 반환한다.

여기서 잔차블럭이 완전연결층을 포함하기때문에 케라스에게 추적해야 할 객체가 있음을 알려야한다. 완전연결 층을 hidden 속성을 통해리스트로 지정하면 keras가 알아서 이를 추적한다.

 

서브클래싱 Residual 모델 정의

keras.Model을 상속받아서 

init 에 사용할 속성과 층을 정의하고

call 메서드에 적용할 연산을 수행한다.

 

keras.Model 은 keras.layers.Layer 의 서브클래스이므로, 모델을 층처럼 사용할 수 있다. 하지만 추가적인 compile fit evaluate predict get_layers save 와같은 메서드를 포함한다.

 

 

12.3.7 모델 구성 요소에 기반한 손실과 지표

https://colab.research.google.com/drive/1RmsihCnizpSmKaQDKj6ZadOMAOP7QaOM#scrollTo=User_Define_Model_Activation_Loss

앞서 사용한 손실과 지표는 레이블과 예측을 기반으로 하지만, 은닉층의 가중치나 활성화 함수 등 모델의 구성요소에 기반한 손실을 정의할 수 있다. 모델 구성 요소에 기반한 손실을 정의하고 계산해서 add_loss() 메서드에 결과를 전달한다.

 

회귀모델에 재구성하는 층을 추가해 재구성 오차를 통해 생성한 재구성 손실을 주 손실에 추가한다. 

사용자 정의 지표객체를 만들어서, 매번 call할 때마다 재구성손실의 평균을 저장하고, 재구성 손실에 작은 값을 곱해서 주 손실이 무시되지 않도록한다. 그래서 에폭마다 주 손실과 재구성 손실에 작은 값을 곱해진 평균 손실과 재구성 손실을 출력하고, 나중에 재구성 층을 이용해 재구성도 가능하다.

 

 

12.3.8 자동 미분을 사용하여 그레디언트 계산하기

https://colab.research.google.com/drive/1RmsihCnizpSmKaQDKj6ZadOMAOP7QaOM#scrollTo=Auto_Diff_Gradient_Tape

파라미터가 2개의 그레디언트 벡터를 구하기 위해선 2번의 편미분과 대입이 필요하다. 파라미터 개수가 많아질수록 수작업은 불가능하다. 

하지만 도함수를 직접 계산하는 것말고, 각 파라미터가 아주작은 값이 바뀔 때마다 함수의 출력이 얼마나 변하는지 측정해서 기울기를 측정해 도함수의 근사값을 계산하는 방법이 있다.

대신 함수를 파라미터 개수만큼 수행해야해서 대규모 신경망에서는 사용하기 어렵다.

 

그래서 텐서플로의 자동미분을 사용한다.

 

tf.GradientTape() 블럭을 만들어서 변수와 관련된 연산을 자동으로 기록하게 만든다.

그리고 객체에 gradient( z,  [ w1 , w2 ] ) 메서드를 이용해서 파라미터의 그레디언트 벡터를 요청한다.

변수가 얼마나 많든지 계산 한번만에 그레디언트 벡터를 구할 수 있다.

메모리를 절약하기 위해서는 tf.GradientTape() 블럭 안에 최소한의 연산만 담는게 좋다. 또는 tape.stop_recording() 블럭으로 계산을 기록하지 않는 방법도 있다.

 

gradient() 메서드를 사용하면 자동으로 테이브가 지워진다. 따라서 persistent=True 를 입력하면 한번이상 호출 가능하고, 사용이 끝나면 리소스를 해제시켜야 한다.

tape 에 변수가 아닌 다른 객체에 대한 그레디언트를 계산하려면 None 이 반환된다.

그런데 필요한 어떤 텐서라도 감시해서 관련된 모든 연산을 기록해 그레디언트를 계산할 수 있다.

tape.watch(tensor) 을 이용한다.

 

대부분 사용되는 여러 값(모델 파라미터) 에 대한 값(손실 함수)의 그레언트를 계산하는데 후진 모드 자동 미분reverse-mode autodiff 를 사용하는데, 손실합을 이용해 그레디언트를 구한다. 이때 각각의 손실에 따른 그레디언트를 찾고싶으면 tape.jacobian() 메서드를 이용한다. 각 손실마다 후진 자동미분을 수행하고, 이계도함수도 계산할 수 있다.

 

어떤 경우에는 그레디언트의 역전파를 막을 필요성이 있다. tf.stop_gradient() 함수를 사용해 정방향 계산만 수행하고 역전파시 그레디언트를 전파하지 않는다.

 

그레디언트 계산시에 수치적인 이슈가 발생할 수 있다. 소프트플러스 활성화함수의 그레디언트를 계산할때, 부동소수점의 정밀도 오류로 무한으로 나눠 nan 이 발생한다. 이러한 수치적 이슈를 도함수를 직접 구현함으로 해결할 수 있다. 그리고 @tf.custom_gradient 데코레이터를 사용해서 텐서플로가 그레디언트 계산에 사용하도록 할 수 있다.

 

12.3.9 사용자 정의 훈련 반복

https://colab.research.google.com/drive/1RmsihCnizpSmKaQDKj6ZadOMAOP7QaOM#scrollTo=User_Define_Train_Step

fit 메서드의 유연성이 부족할때, 예를들어 옵티마이저를 여러개 사용해야 하는상황일때, 훈련 반복을 직접 구현해야 한다. 혹은 의도한 대로 잘 작동하는지 확신을 갖기 위해 사용자 정의 훈련반복을 사용할 수도 있다. 그런데 직접 구현하면 유지보수가 어렵고, 길고, 버그가 쉬운 코드가 만들어진다.

 

모델 생성, 랜덤 배치 추출 함수, 반복 상태 출력 함수

훈련 에폭, 배치 사이즈, 옵티마이저, 손실함수, 손실 평균 지표, 지표

훈련 반복 - 배치 추출, 예측 만들기, 손실 구하기, 손실 기록, 손실에 훈련가능한 변수 그레디언트 벡터 구하기, 옵티마이저에 업데이트, 각종 지표 업데이트 

 

정의 훈련 반복은 훈련과 테스트에서 다르게 동작하는 층을 다루지 못한다. 따라서 training=True로 모든 층에 전파되도록한다.

 

 

12.4 텐서플로 함수와 그래프

https://colab.research.google.com/drive/1RmsihCnizpSmKaQDKj6ZadOMAOP7QaOM#scrollTo=Tensorflow_Function_Graph

텐서플로 1에서 그래프는 핵심적인 내용이다.

일반적인 파이썬 함수를 tf.function(cube) 로 텐서플로 함수로 변환할 수 있다. 또는 @tf.function 데코레이터로도 가능하다. 

텐서플로 함수는 수행되는 계산을 분석하고 동일한 작업을 수행하는 계산 그래프를 생성한다. 이는 노드를 제거하고, 표현을 단순화하고, 효율적으로 계산 그래프를 최적화한다. 이렇게 준비된 그래프는 복잡할 수록 파이썬 함수보다 더 빠르게 수행된다.

 

위에서 지정한 사용자 정의 함수를 모델에 적용할 때 케라스가 자동으로 함수를 텐서플로 함수로 변환하고, 이것을 막기 위해서는 층이나 모델을 만들 때 dynamic=True, compile 할때 run_eagerly=True로 지정한다.

 

텐서플로 함수에 텐서를 매개변수로 지정하면 입력 크기, 타입에 맞춰 새로운 그래프가 생성된다. 일반 파이썬값은 고유값마다 그래프가 생성된다. 이는 메모리를 많이사용하게되고 느려지는 주범이 될 수 있다. 그래프를 삭제하기 위해서는 텐서플로 함수를 삭제해야 한다.

 

12.4.1 오토그래프와 트레이싱

 

 

텐서플로는 함수의 제어문 for while if break continue return 등을 찾아(오토그래프autograph) 텐서플로 연산으로 변환한다.

이렇게 업그레이드된 함수를 호출하는데 심볼릭 텐서symbolic tensor ( 이름, 데이터 타입, 크기만 가진, 값은 없음 ) 을 전달해 그래프 모드graph mode 로 실행된다. (즉시 실행eager execution , 즉시 실행 모드eager mode 의 일반적인 모드의 반대 )

텐서플로 연산은 아무 계싼도 수행하지 않고 그래프만 생성한다.

 

12.4.2 텐서플로 함수 사용 방법

텐서플로 함수로 변환하는건 간단하지만 지켜야 할 규칙이 있다.

1. 다른 라이브러리를 호출하면 트레이싱 과정에서 호출 될 수 있다. 따라서 코드가 실행되는것을 원하지않으면 텐서플로 구성요소만 포함하는게 좋다.

- np.random.rand() 를 반환하는 함수를 포함하면 트레이싱될 때마다 호출되고, 이를 tf.random.uniform([])으로 바꾸면 이게 그래프의 일부분이 되어서 호출할 때마다 난수를 생성하게 된다.

- 텐서플로에서 지원하지 않는코드가 포함되면, 트레이싱할 때만 수행되고 호출될 때는 이게 실행되지 않는다.

2. 다른 파이썬 함수나 텐서플로 함수를 호출할수 있다. 이 함수들은 tf.function으로 적용할 필요는 없다.

3. 함수에서 텐서플로 변수 등을 만들면 처음 호출될 때만 수행되어야 한다. 텐서플로에서 관리하기때문에 호출할 때마다 생성되면 중복생성됨. 따라서 함수밖에서 변수를 생성하는것이 좋다.

4. 파이썬함수의 코드는 텐서플로에서 사용 가능해야한다.

5. 텐서플로는 텐서나 데이터셋을 순회하는 for문만 감지해서 for _ in range 대신 for _ in tf.range 를 사용한다. 대신 층을 반복문으로 만드는 상황에서는 일부로 텐서플로함수를 사용하지 않고 만들 수있다.

6. 성능면에서 반복문보다 벡터화구현이 더 좋다.