https://www.youtube.com/watch?v=3seWxHGnDqM&list=PLQY2H8rRoyvzuJw20FG82Lgm2SZjTdIXU&index=3


문제를 해결하기 위해 모델을 탐색하는 것이 매우 어렵고 힘든 작업인 것은 모두 알고 있을 것입니다.

엄청나게 쏟아지고 있는 모델 중에서 적절한 모델을 찾기란 매우 어렵습니다.

 

적절한 모델을 찾았다면, 깃허브나 모델의 세부 사항을 통해 적용하는 단계로 넘어가죠. 이 과정에서 우리는 제일 먼저 pre-trained 모델을 찾을지도 모릅니다.
이러한 pre-trained 모델은 운이 좋게도 깃허브 저장소에서 다운받을 수 있거나 다른 곳에서 접근할 수도 있습니다.

이때 사용하는 모델에 대한 의심은 우리를 떠나가질 않습니다.

  • 안전한 방법인가?
  • 최신 버전인가?
  • 어떻게 사용하는가?

이를 위해 텐서플로우에서는 TensorFlow Hub를 제공합니다.

TensorFlow Hub는 우리가 원하는 모델을 쉽게 찾을 수 있도록 도와주는 공간입니다.
또, 다양한 분야에서의 모델을 제공하죠.

  • 이미지
  • 텍스트
  • 비디오
  • 오디오

이미지를 예로 들면, 분류, 탐지, 변형, 생성에 적합한 pre-trained 모델을 제공하고 있습니다.
또, 텍스트에서는 우리가 흔히 아는 Bert를 포함해서 AlBert, 다양한 Encoder와 Embedding을 제공합니다.

TensorFlow Hub를 사용하면 다양한 디바이스(TF Lite, TF .JS etc.)에서 쉽게 transfer learning을 수행할 수 있습니다.
이를 위해 무려 1,000개가 넘는 모델을 제공합니다. 간단한 데모뿐만 아니라 code snippet까지 제공합니다.

사용하는 방법도 매우 쉽습니다. TensorFlow Hub에서 우리의 문제에 적합한 키워드로 검색만 하면 됩니다. 또, 다음 그림과 같이 CoLab과 연동해서 바로 사용해볼 수 있도록 준비가 되어 있습니다.(설치할 필요도 없어요)


style transfer에서 허브 사용해보기

import tensorflow_hub as hub
hub_handle = 'https://tfhub.dev/google/magenta/arbitrary-image-stylization-v1-256/1'
hub_module = hub.load(hub_handle)
stylized_image = hub_module(tf.constant(content_image), tf.constant(style_image))[0]
tensor_to_image(stylized_image)

허브에 존재하는 모델 링크를 통해 바로 사용할 수 있습니다.

 

Text Classification에서 허브 사용해보기

텍스트 분야에서는 여러 가지 용어를 숫자로 표현할 수 있는 임베딩이 매우 중요한 역할을 수행합니다. 하지만 이를 위해서는 많은 시간과 데이터가 투자되어야 하죠.
TensorFlow Hub를 사용하면 코드 한줄로 임베딩을 불러와서 사용할 수 있습니다.

import tensorflow as tf
import tensorflow_hub as hub

embedding = "https://tfhub.dev/google/tf2-preview/gnews-swive1-20dim/1"
hub_layer = hub.KerasLayer(embedding, input_shape=[], dtype = tf.string, trainable=True)

model = tf.keras.Sequential()
model.add(hub_layer)
model.add(tf.keras.layers.Dense(16, activation = 'relu'))
model.add(tf.keras.layers.Dense(1, activation = 'sigmoid'))

두 가지 예제에서 볼 수 있듯이, 그저 불러서 사용하기만 하면 됩니다. 설치나 기타 과정이 필요하지 않습니다.(매우 편리하죠?)

추가로...

  • TensorFlow Hub는 우리가 모델을 좀 더 쉽게 찾을 수 있도록 검색에서 필터 기능을 제공합니다.
  • TensorFlow Lite에서는 metadata를 수월하게 볼 수 있도록 하고, 연동하기 쉽도록 해두었습니다.
  • TensorFlow JS에서는 face and hand tracking 모델을 추가하고, web app를 위한 text model을 제공합니다.

 

https://www.youtube.com/watch?v=aNrqaOAt5P4&list=PLQY2H8rRoyvzuJw20FG82Lgm2SZjTdIXU&index=2


- 이번 동영상은 모델이 글을 읽는 학습 과정을 케라스를 통해 보여줍니다.

- 사람이 유치원을 다닐 때 글을 배우는 과정이 신경망이 글을 배우는 과정과 동일하다면서 영상을 시작합니다.

- NLP는 계속해서 진화하는 중이고, 사람 수준을 뛰어넘었습니다.


데이터 준비하기

- 신경망이 학습하기 위해선 데이터가 필요합니다. corpus와 같은 데이터말이죠.
먼저, 데이터를 확인해보겠습니다.
(데이터는 영상으로 대체합니다.)

lines = tf.data.TextLineDataset('./CBTest/data/cbt_train.txt')

for line in lines.take(3):
  print(line)

이 데이터를 그대로 사용하면 위험합니다. 데이터를 클린하게 만들어 줄 필요가 있습니다.

lines = lines.filter(
    lambda x: not tf.strings.regex_full_match(x, "_BOOK_TITLE_.*")
)

punctuation = r'[!"#$%&()\*\+,-\./:;<=>?@\[\\\]^_'{|}~\']'
lines = lines.map(lambda x: tf.strings.regex_replace(x, punctuation, ' '))

위의 결과와 비교해보세요. 클린해진 것을 한눈에 알 수 있습니다.

이제 모델에 데이터를 입력하기 위해 window 형태로 만들어야 합니다. 모든 문장의 길이는 같기 때문에 단순히 split만 해주면 되겠군요.

words = lines.map(tf.strings.split)
wordsets = words.unbatch().batch(11)

for row in wordsets.take(3):
  print(row)

모델이 정답은 알아야겠죠? 데이터의 마지막 단어를 레이블로 만들고 싶습니다.

def get_example_label(row):
  example = tf.strings.reduce_join(row[:-1], separator = ' ')
  example = tf.expand_dims(example, axis = 0)
  label = row[-1:]
  return example, label

data = wordsets.map(get_example_label)
data = data.shuffle(1000)

결과는 다음과 같습니다. 한 개의 긴 문장(학습용 데이터), 한 개의 레이블로 이루어져 있네요.

하지만 신경망 모델이 직접 언어를 배울 수는 없습니다. 오로지 숫자를 배울뿐이죠.
그래서 위와 같은 데이터를 다시 숫자로 변환해야 합니다.

vocab_size = 5000 # Maximum vocab size
vectorize_layer = tf.keras.layers.experimental.preprocessing.TextVectorization(max_tokens=vocab_size,
                                                                               output_sequence_length = 10)

이와 같은 전처리 레이어는 다양하게 keras에서 제공하고 있으며, tf.data.Datasets와 활용하면 매우 효과적입니다. 다음 그림에서 전처리 레이어가 얼마나 다양하게 존재하는지 알 수 있습니다.

 

모델 준비하기

이제 모델을 구성하면 됩니다.(seq2seq 모델)
케라스와 TensorFlow AddOnes를 활용하면 쉽게 구성할 수 있습니다. seq2seq 모델은 상당히 복잡한데, Encoder는 keras layer를 통해 쉽게 구성할 수 있고,  Decoder는 AddOnes를 활용하여 한번에 구성할 수 있는 것을 영상에서 보여줍니다.

TF 2.x에서는 학습 과정을 클린하고 모듈화된 과정을 제공하기 위해 노력한다고 합니다.
train_step을 직접 만들고, Gradient_Tape()를 활용하여 모델을 업데이트시키죠.

학습 단계를 정의했다면, 기존의 케라스와 동일하게 compile() 함수를 활용해서 과정을 설정합니다.
그래서 위의 train_step을 직접 정의해야 되냐고요?
model.fit() 함수를 사용하면 위의 과정이 전부 포함되어 있습니다.

model = MyModel()
model.compile(...)
model.fit(...)

케라스를 사용해서 우리는 만족할 수준의 모델 성능을 쉽게 얻을 수 있었습니다.
하지만 아직 하이퍼파라미터 튜닝을 진행해보지 않았군요.

 

하이퍼파라미터 튜닝하기

KerasTuner를 활용한다면 매우 쉽습니다!
코드에서 hp(hyperparameter 객체) 인자를 사용해 튜닝을 준비하고 있습니다.

import kerastuner as kt

def build_model(hp):
  model = EncoderDecoder(
    			rnn_units = hp.Int(
                			'units', min_value = 256, max_value = 1100, step = 256))
  model.compile(...)
  model.vectorize_layer.adapt(lines.batch(256))

  return model

케라스 튜너는 다음과 같이 사용합니다.

tuner = kt.tuners.RandomSearch(
  build_model,
  objective = 'accuracy',
  ...
  project_name = 'text_generation')
  
tuner.search(
  data.batch(256),
  epochs = 45,
  callbacks = [tf.keras.callbacks.ModelCheckpoint('text_gen')])

학습이 끝났으니 이제 모델이 잘 동작하는지 확인해야 합니다.
train_step처럼 predict_step을 정의해야 하지만, 케라스를 활용하면 model.predict()로 끝입니다!

마지막으로 텐서플로우 2.x는 large-scale의 text processing을 위해 다음 모듈을 제공합니다.

  • tf.text
  • KerasBert
  • TFHub text modules

Dev Summit을 여는 첫 번째 동영상 Kenote는 이번 Dev Summit에서 텐서플로우가 무엇을 이야기할지 간략하게 정리해준다.

https://www.youtube.com/watch?v=_lsjCH3fd00&t=687s


- 코로나의 영향으로 온라인 라이브로 진행합니다.

- 2019년은 텐서플로우에게 매우 중요한 해였습니다. 바로 텐서플로우 2.x를 배포하였기 때문이죠. 또, 배포함과 동시에 사용자의 접근성을 계속해서 고려하고 있습니다.(아마 Pytorch를 의식하지 않았을까요?, Keras, Eager Execution을 통해 텐서플로우의 편리함을 강조하고 있습니다)

- 이제금 TensorFlow 2.2 버전이 제공됩니다. 좀 더 Stable한 버전이 될 것으로 기대합니다.

- 영상에서는 TensorFlow Ecosystem을 언급하는데, 다음 그림을 보면 무엇인지 바로 확인할 수 있습니다.
(JAX도 포함되어 있고, CoLab 등 우리에게 필요한 것이 전부 있습니다!)

- ecosystem은 연구자, 실제 환경에서의 사용자, 기타 사용자에게까지 모든 것을 편리하게 제공합니다.

- 텐서플로우는 Meena, T5와 같은 NLP 모델를 예시로 다양한 sub fields에서 활약하고 있다.
- 연구자는 텐서보드(Tensorboards)를 활용해서 많은 부분을 피드백받을 수 있도록 한다. 실제로 2019년에 많은 논문에서 텐서보드를 활용하여 실험 결과를 공유하고 있다고 합니다.

- 텐서플로우 2.x는 매우 단순하다.
(Eager Execution, NumPy array 지원, tf.data를 통한 data pipeline handling, TF Datasets 등을 통해 쉽게 접근할 수 있다)
- 또, JAX를 본격적으로 활용하면서 연구자들이 매우 환영할 것으로 기대한다(아마 traning의 시간에 매우 영향을 끼칠 것으로 판단됨).

- 머신러닝 모델을 실제 제품에 적용하는 것은 매우 어려운 일인데, 텐서플로우 Ecosystem을 활용하면 큰 도움을 받을 수 있다.

  1. 케라스 API를 활용하면 텐서플로우를 더욱 쉽게 활용할 수 있다.
  2. TensorFlow Hub를 활용하면 pre-trained 모델, 각 분야의 모델 Demo 등을 CoLab을 통해 쉽게 사용해 볼 수 있다.
    (개인적으로 매우 좋은 것 같습니다. pre-trained 모델을 쉽게 사용할 수 있는 것은 딥러닝의 매~~~~우 큰 장점이란 것을 아니까요)
  3. 구글 클라우드 플랫폼 파이프라인을 활용하면 end-to-end production을 쉽게 구축할 수 있습니다.
  4. 케라스를 통해 TPU를 쉽게 활용할 수 있습니다.

- TF Lite는 latency 수준을 매우 낮췄다!(밑 그림의 회색 bar를 확인하세요.)
- TF Lite의 사용을 더욱 쉽게 할 수 있고, Android Studio와도 연동이 더욱 수월하게 할 수 있도록 진행하는 것 같습니다.
- 뿐만 아니라, TensorFlow.js에서도 매우 훌륭한 성능을 제공하는 것을 보여줍니다.

- TensorFlow Runtime이 새롭게 개편되었습니다. 효율적이고 최적화를 진행했기 때문에 더욱 빠르게 연구할 수 있을 것으로 기대합니다.

- TensorFlow는 다양한 질문에 대비할 준비가 되어 있다.

- TensorFlow가 가지고 있는 커뮤니티의 힘은 매우 강력합니다.
(실제로 국내에서도 그렇죠. TensorFlow KR 또는 Keras KR with FaceBook을 찾아보세요!)

- TensorFlow를 학습하기 위한 다양한 course를 제공하고 있고, deeplearning.ai를 활용해도 좋습니다.
- TensorFlow 자격증이 생겼습니다!
(Tensorflow.org/certificate 를 참고하세요)

이 글은 다음 영상을 참조합니다.

https://www.youtube.com/watch?v=4iq-d2AmfRU&list=PLQY2H8rRoyvzIuB8rZXs7pfyjiSUs8Vza&index=2


Quantization이 무엇인가?

Quantization은 모델이 표현하는 바를 일부 제한시켜서 모델의 파라미터를 감소시키는 방법이다. 이 방법은 모델의 크기를 감소시켜서 프로그램을 빠르게하고, 파워 소비를 감소시킵니다. 또 마지막으로 메모리 요구를 감소시켜 하드웨어 측면에서도 이점을 취할 수 있습니다.

왜 어려운가?

장점이 많은만큼, 어렵다.
(1) 이 방법을 표현하기 어렵다. 표준이 없다는 이야기이다.
(2) 완전한 적용을 위한 메타 데이터가 충분하지 않다.
(3) 신경망의 고질적인 문제인 것처럼, 결과를 해석할 수 없다. 그래서 이 방법은 효과적이지만 정교하게 해석할 수 없기에 이를 목표로 잡고있다고 언급한다.

어떻게 사용하는가?

가장 쉬운 방법은 프로그램이 활용하는 수의 범위에서 min-max를 결정해서 clipping하는 것이다. 이 방법은 효과적일 수 있지만, 어떤 모델에서는 유용하고, 또 어떤 모델에서는 유용하지 않을 수 있기 때문에 주의해야 한다. 

이 방법은 주로 전처리 과정이나 또는 경우에 따라 사후 처리에서 사용해서 값의 범위를 제한시키고 모델에 입력시켰는데, 최근에는 모델의 학습 과정(예를 들면 backward)에서 사용한다.

학습 과정에서 사용하면 더욱 잘 adjust할 수 있지만, 재사용, 유지보수 불가, unstable, 다루기 어려움과 같은 주의점이 존재한다.

Neural connection pruning

우리가 흔히 하는 Model pruning과 동일한 이름인 것 같다. 이 방법은 신경망의 개별 connection을 끊어주는 것을 의미한다. 예를 들면, dense한 tensor에서 sparse한 tensor로 만들어주는 것이다.
sparse한 tensor가 되기 위해서는 기존의 값을 0으로 채워넣으면 되겠죠?

그래서 TensorFlow는 이를 위해 어떤 tool을 제공하는가?

텐서플로우는 2019년에 이를 위한 Model Optimization Toolkit을 배포했다.
(Quantization, Sparsity를 수행해서 이미지가 어떻게 변화하는지는 영상의 [15:14]에서 볼 수 있다.)

https://github.com/tensorflow/model-optimization

텐서플로우에서는 세 가지의 Quantization 방법을 제공한다.
- TensorFlow Lite용
- 학습 과정 중에 사용할 수 있는 방법
- 사후 처리 과정 중에 사용할 수 있는 방법

먼저 quantization 방법은 bit로 표현할 수 있는 범위를 제한시키는 것이 대표적인데,
텐서플로우는 float형태--> float + int 형태 --> only 8-bit int 형태의 quantization 방법을 제공한다.
(자세한 설명은 동영상을 참고하세요)

중요한 것은 keras API를 활용해서 쉽게 적용가능하다.

import tensorflow_model_optimization as tfmot

model = MyModel()

model_to_quantize = tfmot.quantization.keras.quantize(model)

model_for_pruning.fit()

왜 마지막 모델의 이름이 model_for_pruning 인지는 뒤에서 알 수 있다.

 

TF Lite에서도 매우 쉽게 활용할 수 있는 것을 보면, 감탄사가 나온다. 
convert하는 과정에서 converter의 옵션을 다음과 같이 설정하기만 하면 된다.

converter.optimizations = [tf.lite.Optimize.DEFAULT]

동영상에서는 CPU를 통해 얼마만큼의 속도 향상이 이루어졌는지 결과를 보여준다(무려 4배, RNN 계열은 3배).
보통 속도와 성능은 trade-off 관계인 것을 알고 있을 것이다. 동영상에서는 이에 대한 실험결과도 보여주는데 기본 baseline과 quantization을 수행한 결과 차이가 거의 업다!

케라스를 활용하여 사용하는 과정은 다음과 같다.

  1. 케라스 모델을 구성한다.
  2. 텐서플로우에서 제공하는 optimization API를 활용해서 pruning을 적용한다.
  3. 학습한다.
  4. 모델은 sparse한 tensor 보유를 통해 크기가 감소할 것이다.

적용 과정을 더욱 구체적으로 살펴보자면,

import tensorflow_model_optimization as tfmot

model = MyModel()

pruning_schedule = tfmot.sparsity.keras.PolynomialDecay(
				inital_sparsity = 0.0, final_sparsity = 0.5,
                begin_step = 2000, end_step = 4000)
                
model_for_pruning = tfmot.sparsity.keras.prune_low_magnitude(model,
								pruning_schedule=pruning_schedule)

여기서 final_sparsity 인자값을 강조하고 있다. 이 인자를 통해 각 layer에서 weight를 얼마나 감소시킬 것인지를 결정할 수 있는데, 여기서는 0.5이므로 50%를 선택한 것이다.
인자값의 선택은 0.5 ~ 0.8 즉, 50% ~ 80% 정도가 가장 효율적이라고 실험을 통해 설명한다.

 

 

Binary_crossentropyCategorical_crossentropy 함수에선 공통적으로 from_logits 인자를 설정할 수 있습니다.

기본값은 모두 False로 되어있는데요. True, False의 차이점을 보도록 하겠습니다.

먼저 가벼운 예제를 보고 넘어가죠. 다음 두 코드의 차이점은 단순히 from_logits의 차이입니다.

# 1번 코드
cce = tf.keras.losses.CategoricalCrossentropy(from_logits = True)

target = [[1., 0., 0.], [0., 1., 0.], [0., 0., 1.]] 
output = [[1., 0., 0.], [.05, .89, .06], [.05, .01, .94]]

loss = cce(target, output)

print(loss.numpy())
  • 0.5889537
# 2번 코드
cce = tf.keras.losses.CategoricalCrossentropy(from_logits = False)

target = [[1., 0., 0.], [0., 1., 0.], [0., 0., 1.]] 
output = [[1., 0., 0.], [.05, .89, .06], [.05, .01, .94]]

loss = cce(target, output)

print(loss.numpy())
  • 0.059469786

출력값의 차이가 크게 나는 것을 볼 수 있습니다. 동작하는게 다르다는 것을 보여주네요. 결과는 일단 제쳐두고, 다시 from_logits 인자에 대해 얘기해보죠.(밑에 길게 설명되어있지만, 사실 텐서플로우의 logit은 매우 간단합니다.)


from_logits의 의미는 모델이 출력하는 output이 logit인지 아닌지를 판단하는 것으로 생각할 수 있는데요. 이를 해석하려면 logit의 의미를 또 파헤쳐보아야 합니다.

일반적으로 통계학에서 쓰이는 logit과 딥러닝에서 쓰이는 logit은 의미가 다릅니다. 또, 몇몇 사람들의 글을 찾아보면 굳이 텐서플로우가 이를 함수 이름으로 채택하면서 사용자들에게 혼동을 주고 있다고 말하기도 하네요. 사실 이 글의 작성 동기도 함수 사용 과정에서 조금 헷갈려서...

통계학에서 쓰이는 logit은 Logistic Regression을 이야기할 때 자주 쓰이는 logit입니다.

  1. 이때 자주 이야기하는 오즈(odds)는 (성공 확률 / 실패 확률)을 통해 구해지게 됩니다. 하지만 실제 변수를 확률의 범위에서 해석하기엔 조금 애매합니다. 그래서 log를 사용하는데요. 이를 logit 변환이라고 합니다. log를 사용하게 되면, 값의 범위가 [-inf, inf]로 바뀌게 됩니다.
    (오즈값의 범위는 [0, inf] 입니다)
  2. logit 변환의 의미는 예측값(y)과 예측값을 만들어내는 특성값(x)의 관계를 선형관계로 만들어준다는 것입니다. 따라서 로지스틱 회귀식을 사용할 수 있는거죠! 원래 y는 [0, 1]의 확률의 범위였고, 특성값(x)은 [-inf, inf]이기 때문에 관계를 말할 수 없었습니다. 값의 범위가 동일하지 않으면 관계를 해석하기가 쉽지 않을겁니다.

    그래서 이를 해결하고자 logit 변환을 사용해서 값의 범위를 동일하게 맞춰주고, 값의 해석이 유의미하도록 도와주는거죠. 즉, 다시 말해서 logit 변환은 [0, 1] 범위를 가지는 확률을 [-inf, inf] 범위를 가지는 값으로 변환해주는 변환입니다.
  3. 로그를 씌운 오즈비(Odds Ratio)의 역함수를 구해보세요. 시그모이드 함수가 눈에 보일겁니다.

딥러닝에서 쓰이는 logit은 매우 간단합니다. 모델의 출력값이 문제에 맞게 normalize 되었느냐의 여부입니다. 예를 들어, 10개의 이미지를 분류하는 문제에서는 주로 softmax 함수를 사용하는데요.

이때, 모델이 출력값으로 해당 클래스의 범위에서의 확률을 출력한다면, 이를 logit=False라고 표현할 수 있습니다. logit이 아니라 확률값이니까요(이건 저만의 표현인 점을 참고해서 읽어주세요).

반대로 모델의 출력값이 sigmoid 또는 linear를 거쳐서 확률이 아닌 값이 나오게 된다면, logit=True라고 표현할 수 있습니다. 말 그대로 확률이 아니라 logit이니까요.


다시 코드로 돌아가보죠. 먼저 코드를 해석하려면 두 가지 가정이 필요합니다.

(1) Loss Function이 CategoricalCrossEntropy이기 때문에 클래스 분류인 것을 알 수 있다.
(2) output 배열은 모델의 출력값을 나타내며, softmax 함수를 거쳐서 나온 확률값이다.

이제 우리는 왜 2번 코드에서 from_logits=False를 사용했는지 알 수 있습니다. 문제에 알맞게 normalize된 상태이기 때문입니다(값을 전부 더해보면 1입니다, 확률을 예로 든거에요). 반대로 from_logits=True일 때는 output 배열의 값이 logit 상태가 아니기 때문에 우리가 생각한 값과 다른 값이 나오게 된 것입니다. 

tensorflow github source, categorical_crossentropy function

텐서플로우 깃허브의 코드를 보아도 from_logits 인자에 따라 계산 방식이 다른 것을 볼 수 있습니다. from_logits가 True인 경우에는 nn.softmax_cross_entropy_with_logits 함수로 바로 넘어가는데요. 이를 쉽게 설명하면, 출력값이 logit 형태이기 때문에 softmax 함수(logit을 확률로 변환하는 과정) -> crossentropy의 과정을 거치는 함수입니다.


결론: 클래스 분류 문제에서 softmax 함수를 거치면 from_logits = False(default값),

그렇지 않으면 from_logits = True.

+ 텐서플로우에서는 softmax 함수를 거치지 않고, from_logits = True를 사용하는게 numerical stable하다고 설명하고 있다.

 

Reference

https://ko.wikipedia.org/wiki/%EB%A1%9C%EC%A7%80%EC%8A%A4%ED%8B%B1_%ED%9A%8C%EA%B7%80
https://www.tensorflow.org/api_docs/python/tf/keras/losses/CategoricalCrossentropy