반응형

source: tensorflow.org

오늘은 딥러닝 모델을 생성할 때 많이 사용하는 Python 라이브러리 중 Tensorflow의 기본 동작 원리와 코드, Tensorflow 2.0에서는 어떻게 변화하였는지에 대해 살펴보도록 하겠습니다!

 

텐서플로 1 코드를 텐서플로 2로 바꿔보자

여전히 텐서플로 1.X 버전의 코드를 수정하지 않고 텐서플로 2.0에서 실행할 수 있습니다(contrib 모듈은 제외):

import tensorflow.compat.v1 as tf
tf.disable_v2_behavior()

→ contrib 모듈의 경우 각 함수에 맞게 대체 가능한 모듈 찾아서 변경하기 (이 부분이 조금 귀찮음)

 

텐서플로 1 vs 2 동작원리 살펴보기

1. 텐서플로 1.x 코드

import tensorflow as tf  # v1

# 1. tf.placeholder를 사용하여 그래프에 입력할 값의 형태를 미리 지정한다.
in_a = tf.placeholder(dtype=tf.float32, shape=(2))
in_b = tf.placeholder(dtype=tf.float32, shape=(2))

# 2. 그래프 생성
def forward(x):
    # tf.get_variable을 사용하여 변수를 생성하고 tf.variable scope로 그 범위를 지정한다.
    with tf.variable_scope("matmul", reuse=tf.AUTO_REUSE):
        W = tf.get_variable("W", initializer=tf.ones(shape=(2,2)),
                            regularizer=tf.contrib.layers.l2_regularizer(0.04))
        b = tf.get_variable("b", initializer=tf.zeros(shape=(2)))
        return W * x + b

out_a = forward(in_a)
out_b = forward(in_b)

reg_loss = tf.losses.get_regularization_loss(scope="matmul")

# 3. tf.Session.run을 이용하여 그래프를 실행한다.
# The value returned by run() has the same shape as the fetches argument.
# feed_dict을 이용하여 placeholder의 실제 입력값을 넣는다.
with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())
    outs = sess.run([out_a, out_b, reg_loss],  # fetches
                    feed_dict={in_a: [1, 0], in_b: [0, 1]})

 

 

2. 텐서플로 2.x 코드로 변경

import tensorflow as tf  # v2

# tf.variable_scope 사용하지 않음
W = tf.Variable(tf.ones(shape=(2,2)), name="W")
b = tf.Variable(tf.zeros(shape=(2)), name="b")

@tf.function
def forward(x):	
    return W * x + b

# tf.placeholder & tf.Session.run를 사용하지 않음
# Session.run 대신 실제 입력값의 단순 함수 호출로 변경(즉시 실행가능)
out_a = forward([1,0])
out_b = forward([0,1])

regularizer = tf.keras.regularizers.l2(0.04)
reg_loss = regularizer(W)

: TensorFlow 2 패키지에는 pip 버전 >19.0이 필요합니다. (https://www.tensorflow.org/install)

 

결론

source: https://www.tensorflow.org/guide/migrate#결론

전체적인 과정은 다음과 같습니다:

  1. 업그레이드 스크립트를 실행하세요.
  2. contrib 모듈을 삭제하세요.
  3. 모델을 객체 지향 스타일(케라스)로 바꾸세요.
  4. 가능한 **[tf.keras](<https://www.tensorflow.org/api_docs/python/tf/keras>)**나 **[tf.estimator](<https://www.tensorflow.org/api_docs/python/tf/estimator>)**의 훈련과 평가 루프를 사용하세요.
  5. 그렇지 않으면 사용자 정의 루프를 사용하세요. 세션과 컬렉션은 사용하지 말아야 합니다.

텐서플로 2.0 스타일로 코드를 바꾸려면 약간의 작업이 필요하지만 다음과 같은 장점을 얻을 수 있습니다:

  • 코드 라인이 줄어 듭니다.
  • 명료하고 단순해집니다.
  • 디버깅이 쉬워집니다.

 


텐서플로 2 | Basic Model 만들어보자

→ tensorflow2라고 쓰고 tf.keras 라고 읽는다.

참고: https://www.tensorflow.org/guide/keras

  • 아래의 tensorflow2 모델은 이미지 분류를 예시로 작성하였습니다.

 

1. dataset 생성하기

  • 여러 방법이 있지만 2가지만 소개합니다.
import tensorflow as tf

# Case 1
## ex, 
## x_train = [train_data_dir/file1.jpg, train_data_dir/file2.jpg, train_data_dir/file3.jpg, ...]
## y_train = [class1, class1, class2, ...]

train_dataset = tf.data.Dataset.from_tensor_slices((x_train, y_train))

# 필요에 따라 map_func 사용할 수 있음 (ex, 이미지 read, resize, ...)
train_dataset = train_dataset.map(lambda item1, item2: tf.numpy_function(
                                      map_func, [item1, item2], [tf.float32, tf.float32]))

# Shuffle and batch
train_dataset = train_dataset.shuffle(BUFFER_SIZE, seed=2021).batch(BATCH_SIZE)
train_dataset = train_dataset.prefetch(BUFFER_SIZE//2)
# Case 2
## ex, train_data_dir/class1/file1.jpg, train_data_dir/class1/file2.jpg, train_data_dir/class2/file3.jpg, ...
train_dataset = tf.keras.preprocessing.image_dataset_from_directory(train_data_dir,
                                                                    seed=2021,
                                                                    image_size=(img_height, img_width),
                                                                    batch_size=batch_size)

# class name 찾을 수 있음
class_names = train_dataset.class_names

train_dataset = train_dataset.cache().prefetch(BUFFER_SIZE//2)

 

2. model 생성하기

2.1 함수형

import tensorflow as tf
class_num = 5

def create_image_classification_model(input_shape):
    # 아래의 구조의 경우, tf.keras.Model 대신 tf.keras.models.Sequential 사용 가능
    # 다만, 다중 입력 및 출력, residual connection, attention 등 Model function이 활용성이 더 높음
    model_input = tf.keras.Input(shape=input_shape)

    embedding = tf.keras.layers.Conv2D(2, 3, padding='same', activation='relu')(model_input)

    embedding = tf.keras.layers.GlobalAveragePooling2D()(embedding)

    embedding = tf.keras.layers.Dense(128, activation='relu')(embedding)
    embedding = tf.keras.layers.Dropout(0.2)(embedding)
    
    embedding = tf.keras.layers.Dense(class_num, activation='softmax', name='output')(embedding)
    
    return tf.keras.Model(model_input, embedding, name='image_classification_model')

input_shape = (256, 256, 3)
classification_model = create_image_classification_model(input_shape)

## sample test
temp_input = tf.random.uniform(input_shape, dtype=tf.float32, minval=0, maxval=256)
output = classification_model.predict(tf.expand_dims(temp_input, 0))
output.shape  # (1, 5)

# model architecture(shape) & params 확인
classification_model.summary()

## (구조가 더 복잡한 경우,) model architecture(shape) ploting하여 확인 가능
# tf.keras.utils.plot_model(classification_model, "image_classification_model.png", show_shapes=True)

 

2.2 클래스 상속

import tensorflow as tf
class_num = 5

class ClassificationModel(tf.keras.Model):
    def __init__(self, class_num, dim=128, rate=0.1):
        super(ClassificationModel, self).__init__()
        self.conv2d = tf.keras.layers.Conv2D(2, 3, padding='same', activation='relu')
        
        self.dense1 = tf.keras.layers.Dense(dim, activation='relu')
        self.dense2 = tf.keras.layers.Dense(class_num, activation='softmax', name='output')
        
        self.gapooling = tf.keras.layers.GlobalAveragePooling2D()
        self.dropout = tf.keras.layers.Dropout(rate)
        

    def call(self, inputs):

        embedding = self.conv2d(inputs)

        embedding = self.gapooling(embedding)

        embedding = self.dense1(embedding)
        embedding = self.dropout(embedding)

        embedding = self.dense2(embedding)

        return embedding


input_shape = (256, 256, 3)
classification_model = ClassificationModel(class_num=class_num, rate=0.2)

## sample test
temp_input = tf.random.uniform(input_shape, dtype=tf.float32, minval=0, maxval=256)
output = classification_model(tf.expand_dims(temp_input, 0))

output.shape # TensorShape([1, 5])

# model architecture & params 확인
classification_model.build((None, 256, 256, 3))
classification_model.summary()

 

3. model 훈련하기

classification_model.compile(optimizer='adam',
                             loss=tf.keras.losses.CategoricalCrossentropy(from_logits=False),
                             metrics=['categorical_accuracy'])

history = classification_model.fit(train_dataset,
                                   epochs=EPOCHS,
                                   validation_data=val_dataset,
                                   callbacks=[modelcheck],)  # callbacks: modelcheckpoint, etc

 

  • 시각화하기
    import matplotlib.pyplot as plt
    
    acc = history.history['accuracy']
    val_acc = history.history['val_accuracy']
    
    loss=history.history['loss']
    val_loss=history.history['val_loss']
    
    epochs_range = range(EPOCHS)
    
    plt.figure(figsize=(8, 8))
    plt.subplot(1, 2, 1)
    plt.plot(epochs_range, acc, label='Training Accuracy')
    plt.plot(epochs_range, val_acc, label='Validation Accuracy')
    plt.legend(loc='lower right')
    plt.title('Training and Validation Accuracy')
    
    plt.subplot(1, 2, 2)
    plt.plot(epochs_range, loss, label='Training Loss')
    plt.plot(epochs_range, val_loss, label='Validation Loss')
    plt.legend(loc='upper right')
    plt.title('Training and Validation Loss')
    plt.show()​
    source: https://www.tensorflow.org/tutorials/images/classification

 

4. model save & load, predict 하기

model_save_path = 'image_classification_model'

# model architecture & weight 저장
classification_model.save(model_save_path)

# model architecture & weight 불러오기
new_model = tf.keras.models.load_model(model_save_path)
# new_model.trainable = False  # transfer learning시 pre-trained model을 freezing할 때 사용, layer별로 설정 가능

# 불러온 모델 predict
y_pred = new_model.predict(test_dataset,
                           steps=STEP_SIZE_TEST
                           )

 

 

반응형
반응형

Tensorflow serving 하기

TensorFlow Serving을 사용하면 동일한 서버 아키텍처와 API를 유지하면서 새로운 알고리즘과 실험을 쉽게 배포 할 수 있습니다. TensorFlow Serving은 TensorFlow 모델과의 즉각적인 통합을 제공하지만 다른 유형의 모델 및 데이터를 제공하도록 쉽게 확장 할 수 있습니다.(출처: tensorflow.org)

 

참조

Train and serve a TensorFlow model with TensorFlow Serving | TFX

TensorFlow Serving with Docker | TFX

모델 저장

# >> 추후 모델 업데이트를 위해 version별로 모델 저장함
## tensorflow version 2.3.1
model_path='models'
model_name='my_model'
model.save(f'{model_path}/{model_name}/1')

저장된 모델 확인

## model format check
for root, dirs, files in os.walk(os.path.join(model_path, model_name)):
    indent = '    ' * root.count(os.sep)
    print('{}{}/'.format(indent, os.path.basename(root)))
    for filename in files:
        print('{}{}'.format(indent + '    ', filename))

## output format ex)
# 1/
#             .DS_Store
#             saved_model.pb
#             variables/
#                 variables.data-00000-of-00001
#                 variables.index
#             assets/
## ex
$ export model_path='models'
$ export model_name='my_model'

## model input, output shape check
$ saved_model_cli show --dir ${model_path}/${model_name}/1 --tag_set serve \
                       --signature_def serving_default

Serving with Docker

TensorFlow Serving을 시작하는 가장 쉬운 방법 중 하나는 Docker를 사용하는 것 입니다. (출처: tensorflow.org)

Docker 설치하기

(on MacOS)

큐베플로우 설치 on MacOS

(others)

https://www.tensorflow.org/tfx/serving/docker#install_docker

## !!도커 설치가 선행되어야함!! ##

## tensorflow에서 제공하는 이미지 가장 최신버전으로 다운로드(현재 기준 2.3.0)
$ docker pull tensorflow/serving
# image 생성 체크
$ docker image ls
# image 삭제
$ docker image rm <IMAGE ID>
# ex
$ export container_name='serving_base'

## tensorflow/serving container 띄우기
$ docker run -d --name ${container_name} -it --rm \
        -p 8500:8500 \
        -p 8501:8501 \
        -v "$(pwd)/${model_path}/${model_name}:/${model_path}/${model_name}" \
        -e MODEL_NAME=${model_name} \
                tensorflow/serving &

# container 생성 체크
$ docker container ls

# container 삭제
# issue: Bind for x.x.x.x:port# failed: port is already allocated.
$ docker rm -f <NAMES>

docker container ls

모델 실행 (REST API)

rest api port: 8501

import json
import numpy
import requests
data = json.dumps({"signature_name": "serving_default",
                   "instances": x.numpy().tolist()})  # json dumps를 위해 list로 변경 (shape 확인 필수)
headers = {"content-type": "application/json"}
json_response = requests.post(f'http://localhost:8501/v1/{model_path}/{model_name}:predict',
                              data=data, headers=headers)
predictions = numpy.array(json.loads(json_response.text)["predictions"])
print(predictions)
반응형

+ Recent posts