예제로 손글씨 숫자 인식 모델 개발
i) MINST 데이터셋 불러오기
import tensorflow as tf
mnist = tf.keras.datasets.mnist
(x_train, y_train) , (x_test, y_test) = mnist.load_data()

mnist.npz 데이터가 생성됨C:\Users\사용자명.keras\datasets
ii) 입력 데이터 정규화(nomalization)
x_train, x_test = x_train / 255.0, x_test / 255.0
여기서 만들 모델의 입력 데이터는 28 x 28 이미지이고, 각 픽셀 값의 범위는 0~255
정규화는 이 입력 데이터를 0~1 범위로 변환
(대상 값 - 입력 값의 최솟값) / (입력 값의 최댓값 - 입력 값의 최솟값)
입력 값의 범위가 0~255이므로 입력 값의 최솟값은 0, 최댓값은 255
따라서, 위의 식에 대입하면 정규화 공식이 다음과 같이 도출
대상 값 / 255
입력 데이터의 정규화는 실제 모델의 성능에 영향을 미침
| 정규화 여부 | 예측 정확도 |
|---|---|
| 정규화를 한 경우 | 97.68% |
| 정규화를 하지 않은 경우 | 92.99% |
👉 입력층은 28 x 28 = 784이고, 각 픽셀 값이 입력 값이 되므로 x1 ~ x784의 총 784개의 노드로 나타낼 수 있음
👉 또한, 은닉층은 a1~a128의 128개의 노드로 구성되고 출력층은 y1~y9의 9개의 노드로 구성
i) 다중 퍼셉트론 모델 생성
mlp_model = tf.keras.models.Sequential([
tf.keras.layers.Flatten(input_shape=(28, 28)),
tf.keras.layers.Dense(128, activation='relu'),
tf.keras.layers.Dense(10, activation='softmax')
])
tf.keras.models.Sequential 클래스를 이용했는데, Sequential 클래스는 모델 및 모델 내 모든 레이어에 입력 텐서와 출력 텐서가 하나씩만 있을 때 사용하며, 각 레이어를 순차적으로 쌓아서 모델을 만듦Flatten 레이어는 28 x 28 크기의 2차원 입력을 784개의 1차원 배열로 바꿔줌Dense 레이어는 128개의 뉴런으로 이루어진 은닉층Dense 레이어는 10개의 클래스에 대응되도록 10개의 뉴런으로 이루어진 출력층compile() 함수를 호출adam, 손실 함수는 sparse_categorical_crossentropy, 평가 지표는 accuracy를 사용ii) 다중 퍼셉트론 모델 컴파일
mlp_model.compile(optimizer='adam',
loss='sparse_categorical_crossentropy',
metrics=['accuracy'])
iii) 모델의 구조 확인
mlp_model.summary()

summary() 함수를 호출하면 레이어의 구성과 레이어별 출력 형태, 파라미터 수를 보여줌iv) mlp_model 학습
fit() 함수를 호출하여 학습 시작mlp_model.fit(x_train, y_train, epochs=5)

epochs를 5로 설정했으므로 다섯 번 반복해 학습하면서 점점 loss가 줄어들고 accuracy가 증가하는 것을 볼 수 있음i) mlp_model 학습 결과 평가
evaluate() 함수를 호출하여 테스트 데이터를 가지고 모델의 정확도를 확인verbose는 2로 설정mlp_model.evaluate(x_test, y_test, verbose=2)

tf.keras.models.Sequential 클래스를 이용하는 방법 말고도, Functional API를 이용하거나 tf.keras.Model 클래스를 상속하여 모델을 개발할 수 있음Functional API의 경우 Sequential 클래스보다 더 자유롭게 모델을 만들 수 있음Functional API를 이용한 케라스 모델 생성
inputs = tf.keras.Input(shape=(28,28))
x = tf.keras.layers.Flatten()(inputs)
x = tf.keras.layers.Dense(128, activation='relu')(x)
outputs = tf.keras.layers.Dense(10, activation='softmax')(x)
mlp_model = tf.keras.Model(inputs=inputs, outputs=outputs)
Model 클래스 상속을 통한 케라스 모델 생성
class MLP_Model(tf.keras.Model):
def __init__(self):
super(MLP_Model, self).__init__()
self.flatten = tf.keras.layers.Flatten()
self.dense = tf.keras.layers.Dense(128, activation='relu')
self.softmax = tf.keras.layers.Dense(10, activation='softmax')
def call(self, inputs):
x = self.flatten(inputs)
x = self.dense(x)
return self.softmax(x)
mlp_model = MLP_Model()
x_train_4d = x_train.reshape(-1, 28, 28, 1)
x_test_4d = x_test.reshape(-1, 28, 28, 1)
cnn_model = tf.keras.models.Sequential([
tf.keras.layers.Conv2D(32, (3, 3), activation='relu', input_shape=(28, 28, 1)),
tf.keras.layers.MaxPooling2D((2,2)),
tf.keras.layers.Conv2D(64, (3, 3), activation='relu'),
tf.keras.layers.MaxPooling2D((2,2)),
tf.keras.layers.Conv2D(64, (3, 3), activation='relu'),
tf.keras.layers.Flatten(),
tf.keras.layers.Dense(64, activation='relu'),
tf.keras.layers.Dense(10, activation='softmax')
])
cnn_model.compile(optimizer='adam',
loss='sparse_categorical_crossentropy',
metrics=['accuracy'])
cnn_model.summary()
cnn_model.fit(x_train_4d, y_train, epochs=5)
cnn_model.evaluate(x_test_4d, y_test, verbose=2)

ResNet을 이용한 손글씨 분류 모델 구현
x_train_4d = x_train.reshape(-1, 28, 28, 1)
x_test_4d = x_test.reshape(-1, 28, 28, 1)
resized_x_train = tf.image.resize(x_train_4d, [32, 32])
resized_x_test = tf.image.resize(x_test_4d, [32, 32])
resnet_model = tf.keras.applications.ResNet50V2(
input_shape=(32, 32, 1),
classes=10,
weights=None
)
# resnet_model.summary()
resnet_model.compile(optimizer='adam',
loss='sparse_categorical_crossentropy',
metrics=['accuracy'])
resnet_model.fit(resized_x_train, y_train, epochs=5)
resnet_model.evaluate(resized_x_test, y_test, verbose=2)

tf.image.resize() 함수를 이용하여 이미지 크기를 28 x 28에서 32 x 32로 확대tf.keras.applications.ResNet50V2 클래스에 입력 데이터의 형태(input_shape), 분류할 클래스 수(classes), 초기 가중치(weights)를 파라미터로 전달tflite 파일 또는 텐서플로 모델로 제공됨tflite 파일은 모델 개발의 최종 산출물이므로 바로 안드로이드 스튜디오에 배포하여 앱을 개발하면 됨tflite 파일로 변환하는 과정을 거쳐야 함ImageNet 데이터로 학습된 MobileNet V2를 사용할 것임tf.keras.applications.MobileNetV2 클래스를 호출하여 MobileNet V2 모델 생성 가능테스트 데이터 로드
pip install image
from PIL import Image
import os
import numpy as np
data_dir = "./images/"
files = os.listdir(data_dir)
images = []
for file in files:
path = os.path.join(data_dir, file)
images.append(np.array(Image.open(path)))
PIL(Python Imaging Library)의 Image.open() 함수와 numpy의 np.array() 함수를 이용하여 이미지를 array 형태로 불러옴import tensorflow as tf
resized_images = np.array(np.zeros((len(images), 224, 224, 3)))
for i in range(len(images)):
resized_images[i] = tf.image.resize(images[i], [224, 224])
preprocessed_images = tf.keras.applications.mobilenet_v2.preprocess_input(resized_images)
np.array() 함수와 np.zeros() 함수를 이용하여 0으로 초기화된 array를 만들고, tf.image.resize() 함수로 이미지 크기를 변환하여 array에 담기preprocess_input() 함수를 적용하여 입력 값을 전처리모델 생성 및 추론
mobilenet_imagenet_model = tf.keras.applications.MobileNetV2(weights="imagenet")
y_pred = mobilenet_imagenet_model.predict(preprocessed_images)
topK = 1
y_pred_top = tf.keras.applications.mobilenet_v2.decode_predictions(y_pred, top=topK)
tf.keras.applications.MobileNetV2 클래스를 통해 모델을 불러올 수 있음weights를 "imagenet"으로 지정하면 ImageNet 데이터로 학습된 모델을 얻을 수 있음predict() 함수로 테스트 데이터를 모델에 입력하고 예측 결과를 y_pred에 담았음y_pred는 5개의 이미지가 1,000개의 클래스에 각각 속할 확률이 얼마인지를 담고 있으므로 형태가 (5, 1000)임decode_predictions() 함수를 적용함topK인 1을 주었기 때문에 확률이 가장 높은 클래스 1개만 반환추론 결과 확인
pip install matplotlib
from matplotlib import pyplot as plt
import numpy as np
for i in range(len(images)):
plt.imshow(images[i])
plt.show()
for k in range(topK):
print(f'{y_pred_top[i][k][1]} ({round(y_pred_top[i][k][2] * 100 , 1)}%)')
pip install tensorflow-datasets
import tensorflow_datasets as tfds
tfds.disable_progress_bar()
raw_train, raw_test = tfds.load(
'cats_vs_dogs',
split=['train[:80%]', 'train[20%:]'],
as_supervised=True
)
tfds.disable_progress_bar() 함수를 호출하여 로그를 출력하지 않도록 설정tfds.load() 함수를 이용하여 데이터를 받아 raw_train, raw_test 변수에 데이터를 저장load() 함수의 첫 번째 인자로 데이터명을 전달하고, 두 번째 인자로 split 속성을 이용하여 데이터의 80%를 raw_train에, 20%를 raw_test에 할당as_supervised 값은 데이터의 형태를 결정True로 설정하면 (input, label) 형태의 튜플 자료형을 반환하고,False로 설정하면 데이터별 고유한 dictionary 형태로 반환import numpy as np
import tensorflow as tf
from tensorflow.image import ResizeMethod
def preprocess(image, label):
out_image = tf.image.resize(image, [224, 224], method=ResizeMethod.BICUBIC)
out_image = tf.keras.applications.mobilenet_v2.preprocess_input(out_image)
return out_image, label
batch_size = 32
train_batch = raw_train.map(preprocess).batch(batch_size)
test_batch = raw_test.map(preprocess).batch(batch_size)
preprocess() 함수를 작성하고, raw_train과 raw_test 데이터에 각각 preprocess() 함수를 적용하여 결과 값으로 배칠르 만듦preprocess() 함수 안에서 바이큐빅 보간법을 이용하여 입력 이미지의 크기를 224 x 224로 변환mobilenet_v2 모듈에서 제공하는 preprocess_input() 함수를 이용하여 크기가 변환된 이미지 데이터를 한 번 더 전처리mobilenet_base = tf.keras.applications.MobileNetV2(
input_shape=(224, 224, 3),
weights="imagenet",
include_top=False)
tf.keras.applications.MobileNetV2 클래스를 통해 모델을 생성하고 파라미터로 입력 데이터의 크기와 weights, include_top 지정False로 설정하면 모델의 마지막 풀링 레이어와 Dense 레이어를 제외한 모델을 얻을 수 있음mobilenet_base.trainable=False
mobilenet_model = tf.keras.Sequential([
mobilenet_base,
tf.keras.layers.GlobalAveragePooling2D(),
tf.keras.layers.Dense(1)
])
mobilenet_model.compile(optimizer='adam',
loss='binary_crossentropy',
metrics=['accuracy'])
mobilenet_model.fit(train_batch, epochs=5)
mobilenet_model.evaluate(test_batch, verbose=2)
Problem domains에서 Image를 선택Architecture에서 MobileNet V2를 선택
MobileNet은 깊이 분할 합성곱 연산에 사용하는 depth multiplier 값을 지정할 수 있으며, 이 값을 통해 각 레이어의 채널 수 변경 가능depth multiplier 값이 작을수록 모델의 속도가 빠르지만 정확도는 떨어짐depth multiplier가 1.00이고 입력 이미지의 크기가 224 x 224인 모델을 선택mobilenet_v2_100_224 입력
feature_vector는 전이 학습이 가능하도록 마지막 레이어를 제거한 모델classification은 바로 분류가 가능한 전체 모델feature_vector 선택The input images are expected to have color values in the range [0,1], following the common image input conventions. For this model, the size of the input images is fixed to height x width = 224 x 224 pixels.
Copy URLi) 개와 고양이 이미지 데이터 불러오기
import tensorflow_datasets as tfds
tfds.disable_progress_bar()
raw_train, raw_test = tfds.load(
'cats_vs_dogs',
split=['train[:80%]', 'train[20%:]'],
as_supervised=True
)
ii) 개와 고양이 이미지 데이터 전처리
import numpy as np
import tensorflow as tf
def preprocess(image, label):
out_image = tf.image.resize(image/255, [224, 224])
return out_image, label
batch_size = 32
train_batch = raw_train.map(preprocess).batch(batch_size)
test_batch = raw_test.map(preprocess).batch(batch_size)
tf.image.resize() 함수를 이용하여 이미지 크기를 244 x 244로 변환pip install tensorflow-hub
i) 텐서플로 허브 모델 생성
import tensorflow_hub as hub
url = "https://tfhub.dev/google/imagenet/mobilenet_v2_100_224/feature_vector/5"
hub_model_transfer = tf.keras.Sequential([
hub.KerasLayer(url, input_shape=(224, 224, 3), trainable=False),
tf.keras.layers.Dense(1)
])
tf.keras.Sequential 클래스를 이용하여 모델을 만들고, hub.KerasLayer 클래스에 URL과 입력 값의 형태를 전달trainable 값을 False로 설정iii) 텐서플로 허브 모델 컴파일 및 학습
hub_model_transfer.compile(optimizer='adam',
loss='binary_crossentropy',
metrics=['accuracy'])
hub_model_transfer.fit(train_batch, epochs=5)
iv) 텐서플로 허브 모델 추론 결과 평가
hub_model_transfer.evaluate(test_batch, verbose=2)
💡 출처
텐서플로 라이트를 활용한 안드로이드 딥러닝, 임태규, 한빛미디어
