머신러닝

TensorFlow Lite mnist.tflite 준비를 위한 Keras 예제

coding art 2020. 8. 6. 16:12
728x90

TensorFlow Lite 안드로이드 스튜디오 코드의 asset 폴더에 업로딩 할 수 있도록 구글 Colaboratory에서 MNIST Keras 예제를 실행하여 학습 결과를 binary 형태로 출력 저장하자. 우선 이 코드를 실행해 보기 전에 구글 Colab에서 Default 로 지원되는 TensorFlow 의 버전이 2.0 이상인지 확인해 보도록 하자.

다음 코드는 구글 Colabotory에서 실행 가능한 가장 간략한 형태의 Keras MNIST 뉴럴 네트워크 사례이다. Keras 데이터세트로부터 MNIST를 불러 와서 학습용(train) 과 테스트용(test)으로 튜플 데이터 구조로 나눈 후 x_train x_test255 로 나누어 0.0~1.0 사이의 값을 가지도록 한다. 한편 y_train y_test는 라벨 값을 저장하고 있다. 코드를 한 번 실행 후에 각각 하나씩 출력해 보면 다음과 같다. y_train 의 라벨 값에 주목해 보면 one-hot-code 가 아님을 알 수 있다. Keras 코드 작성과정에서 라벨 값이 one-hot-code 일 때에는 loss 함수 설정에서 categorical_crossentroy를 사용하며 그렇치 않으면 sparse_categorical_crossentropy 사용됨에 주의하자.

Sequential()에서 뉴럴 네트워크 코드를 작성하자. 읽어 들일 때 28X28이었던 데이터를 1X784 형태의 데이터로 환할 수 있도록 Flatten()을 사용한다.

Dense()에서 1X784 인 입력 데이터를 784X128 웨이트 매트릭스를 사용하여 매트릭스 곱을 계산한다. 이 매트릭스들은 Numpy 어레이 요소가 아니며 TensorFlow의 텐서성분들이다. 계산된 1X128 매트릭스에 대해 활성화 함수 ReLU를 적용하므로서 첫 번째 뉴럴 레이어가 준비가 된다. 두 번째 레이어로 넘어가기 전에 Overfitting을 방지할 수 있도록 Dropout(0.2) 즉 무작위로 네트워크의 20% 끓어 버리도록 한다. 두 번째 레이어에서 1X128 매트릭스에 128X10 웨이트 매트릭스를 사용하여 매트릭스 곱을 계산하여 1X10 매트릭스를 얻어 마지막 단계의 활성화 함수인 softmax에 입력하여 확률을 계산한다.

compile()은 코드를 실행할 수 있도록 준비하는 과정이다. optimizerAdam을 설정하자. Adam 옵티마이저는 Low Pass Filtering 스타일로 상당히 변동성이 큰 Cost 함수 추적 과정에서 사용하기 좋은 특성이 있다. 이울러 라벨 값 데이터들이 one hot code 가 아니므로 sparse_categorical_crossentropy를 사용하도록 한다. 만약 라벨 값 데이터들이 one hot code 라면 categorical_crossentropy가 사용된다.

 

모델 학습을 위한 fit()에서 epoch 5로 두기로 한다. 클수록 좋으나 이러한 뉴럴 네트워크 모델의 인식률 한계가 97% 수준이므로 그 정도 수준의 결과가 얻어질 수 있다면 충분하다. 학습이 완료되면 evaluate() 과정에서 인식률을 체크한다.

마지막 convert() 부분은 Keras에서 학습했던 내용을 biary 형태로 mnist.tflite 라는 파일명으로 그대로 저장하였다가 스마트폰의 안드로이드 OS에서 import 하여 모바일에서 카메라를 사용한 MNIST 코딩을 위해 사용할 수 있다.

안드로이드 OS 에 올리는 과정에 관해서는 아래 URL 주소 내용을 참조하자.

5.7 TensorFlow.Lite Mobile IOT MNIST APP 코드 예제

http://blog.daum.net/ejleep1/985

 

지금은 Keras에서 작성할 수 있는 가장 간단한 뉴럴 네트워크로부터 얻어지는 학습내용을 안드로이드 OS import 하였으나 보다 인식률이 높을 수 있는 즉 99% 인식률 수준의 Keras CNN 코드에서 얻어진 결과를 사용할 수도 있다.

 

다음의 코드는 구글 GPU 서비스 지원을 받아야 수분 낸에 계산이 끝나므로 일정 한 양 만큼 잘라 붙여서 셀별로 실행하도록 하자.

 

#keras_mnist.py

import tensorflow as tf
mnist = tf.keras.datasets.mnist

(x_train, y_train),(x_test, y_test) = mnist.load_data()
x_train, x_test = x_train / 255.0, x_test / 255.0

model = tf.keras.models.Sequential([
tf.keras.layers.Flatten(input_shape=(28, 28)),
tf.keras.layers.Dense(128, activation='relu'),
tf.keras.layers.Dropout(0.2),
tf.keras.layers.Dense(10, activation='softmax')
])

model.compile(optimizer='adam',
loss='sparse_categorical_crossentropy',
metrics=['accuracy'])

model.fit(x_train, y_train, epochs=5)
model.evaluate(x_test, y_test)

converter = tf.lite.TFLiteConverter.from_keras_model(model)
tflite_model = converter.convert()
open('mnist.tflite', 'wb').write(tflite_model)

 

 

다음 CNN 코드는 앞서의 keras mnist 코드와 거의 비슷한 구조를 가지지만 CNN 부분이 차별이 있을 수 있다. 헤더 영역의 라이브러리 import 과정에서 layers 까지 import 하게 되면 실제 코드에서 tensorflow.keras.layers 만큼의 클라스 명을 생략하고 간단하게 코드가 이루어지게 된다. 255fh sksndj 0.0~1.0 사이로 만드는 과정까지 큰 차이는 없어 보인다. 하지만 라벨 값은 keras.utility.to_categorical()을 사용하여 one_hot_code 로 변환하도록 한다.

Sequential()에서 (28,28,1) 입력 데이터에 대하여 첫 번째 CNN 레이어에서 32개의 디지털 필터를 택하여 Conv2D 작업을 실행 후 ReLU 활성화 함수를 적용하고 MaxPooling 작업을 한다. 두 번째 CNN 레이어에서는 64개의 디지털 필터를 택하여 Conv2D 작업을 실행하고 ReLU 활성화 함수를 적용 후 MaxPooling 작업을 한 후 Flatten() 명령에 의해 일차원화 한다. Dropout 0.5를 택한 후 마지막 레이어에서 최종 10개의 클라스 즉 10 비트로 활성화 함수 softmax를 사용하여 처리한다.

batch size 128 epochs 15 로 설정하자. compile 단계에서는 라벨 값이 one_hot_code 로 준비되었으므로 당연히 categorical_crossentropy 사용하도록 한다.테스트 후 인식률이 99%임을 알 수 있다.

mnist_cnn.tflite 파일을 만들기 위해서는 앞서의 코드의 마지막 부분을 이용하면 될 것이다. 다음의 코드는 구글 GPU 서비스 지원을 받아야 수분 내에 계산이 끝나므로 일정 한 양 만큼 잘라 붙여서 셀별로 실행하도록 하자.

 

#keras_mnist_cnn.py

import numpy as np
from tensorflow import keras
from tensorflow.keras import layers

num_classes = 10
input_shape = (28, 28, 1)

(x_train, y_train), (x_test, y_test) = keras.datasets.mnist.load_data()

x_train = x_train.astype("float32") / 255
x_test = x_test.astype("float32") / 255

x_train = np.expand_dims(x_train, -1)
x_test = np.expand_dims(x_test, -1)
print("x_train shape:", x_train.shape)
print(x_train.shape[0], "train samples")
print(x_test.shape[0], "test samples")

y_train = keras.utils.to_categorical(y_train, num_classes)
y_test = keras.utils.to_categorical(y_test, num_classes)


model = keras.Sequential(
[
keras.Input(shape=input_shape),
layers.Conv2D(32, kernel_size=(3, 3), activation="relu"),
layers.MaxPooling2D(pool_size=(2, 2)),
layers.Conv2D(64, kernel_size=(3, 3), activation="relu"),
layers.MaxPooling2D(pool_size=(2, 2)),
layers.Flatten(),
layers.Dropout(0.5),
layers.Dense(num_classes, activation="softmax"),
]
)

model.summary()

batch_size = 128
epochs = 15

model.compile(loss="categorical_crossentropy", optimizer="adam", metrics=["accuracy"])

model.fit(x_train, y_train, batch_size=batch_size, epochs=epochs, validation_split=0.1)

score = model.evaluate(x_test, y_test, verbose=0)
print("Test loss:", score[0])
print("Test accuracy:", score[1])

"
Test loss: 0.02595830149948597
Test accuracy: 0.9907000064849854
"