Making TFReocrd File
Making TFReocrd File
-
TFRecord File Format은 Tensorflow의 자체적인 Binary File Format입니다.
-
대규모 Dataset으로 작업을 할 경우 Binary File로 작업을 한다면 Data Input Pipeline의 효율을 높일 수 있으며, 결과적으로 전체적인 Model의 Training 시간도 향상될 수 있습니다.
-
Binary Data는 Storage에 공간도 덜 차지할 뿐 아니라, Read / Write시에도 더 효율적입니다. 더욱이, Storage가 Motor를 사용하는 장치라면 더욱 그렇습니다.
-
단순히 TFReocrd File Format이 Binary여서 성능 향상을 이룬다는 것이 아니라, TFRecord가 Tensorflow에 최적화 되어 있기 때문에 Tensorflow가 제공하는 다양한 Library와 같이 사용될 경우에 그 성능은 최고가 됩니다.
-
TFRecord File Format에 대한 공식 문서는 아래 Link를 참고해 주시기 바랍니다.
-
제가 TFReocrd를 사용하기 위해서 여러 자료를 확인해 본 결과, 이 File Format을 생성하거나 사용하기가 쉽지 않다는 것입니다.
-
그래서 이번 Post에서는 TFRecord File Format을 직접 만들어보도록 해 보겠습니다.
0. Dataset 선택
-
이번 Post의 목적은 기존에 존재하던 Dataset을 TFRecord Format으로 변환하는 것입니다.
-
이를 위해 다음의 ‘Dog & Cat’ Image File Dataset을 준비했습니다. 아래 Link에서 Download할 수 있습니다.
-
이 Dataset을 선택한 이유는 적절한 Data의 수(Dog & Cat 각각 12500장씩)가 있고, CNN을 이용하여 Classification하기에 적절하다고 생각했기 때문입니다.
-
Download 받아서 압축을 풀면, ‘PetImages’라는 Folder가 보이고 그 안에는 Dog / Cat이라는 개별 Folder가 있고 그 안에 각각 12500장씩 Image File이 있습니다.
1. Dataset 살펴보기
- 먼저 Dataset을 살짝 살펴보기로 하죠
- 필요한 Module을 Load합니다.
import tensorflow as tf
from tqdm import tqdm
from matplotlib.pyplot import imshow
from PIL import Image
-
Dog와 Cat의 File List들을 Full Path로 저장해 놓습니다.
-
이후 작업에 필요한 작업이니 미리 해 두도록 합시다.
CatFileList = tf.io.gfile.glob("./PetImages/Cat/*.jpg")
DogFileList = tf.io.gfile.glob("./PetImages/Dog/*.jpg")
CatFileList = [c.replace("\\","/") for c in CatFileList]
DogFileList = [c.replace("\\","/") for c in DogFileList]
- 어떤 그림들이 있는지 볼까요?
pil_im = Image.open(CatFileList[0])
imshow(pil_im)
pil_im = Image.open(DogFileList[0])
imshow(pil_im)
- 좋습니다. 이런 Image File들이 12500장씩 있는 것 같네요.
2. Preprocess
-
변환에 앞서 미리 해야할 일이 있습니다.
-
TFRecord File Format으로 변환하기 위해서 TF module로 Image File을 Load하는 Step이 있는데, 이 과정에서 몇몇 File들을 TF Module이 읽지 못하는 문제가 발생합니다.
-
정확한 원인은 모르나, 무언가 Image File의 문제로 TF Module이 읽지 못하는 것으로 보이며, 미리 이런 File들을 걸러내도록 하겠습니다.
-
방법은 전체 Image File들을 하나씩 읽어보며 실제 Error가 발생하는지 확인하는 단순하지만 확실한 방법을 사용하도록 하겠습니다.
-
아래 Function은 Imgae File을 읽어서 Model에 입력할 수 있는 Format으로 변환하는 함수입니다.
-
이 Function을 이용해 Image File을 Test해 보도록 하겠습니다.
def load_image(image_path):
img = tf.io.read_file(image_path)
img = tf.image.decode_jpeg(img, channels=3)
img = tf.image.resize(img, (224, 224))
img = tf.keras.applications.efficientnet.preprocess_input(img)
return img , image_path
2.1 Filtering Image File
- 아래 Function으로 걸러내도록 하겠습니다.
def FilterOpenErrorFile():
ErrorFileList = []
for fname in tqdm(CatFileList):
try:
load_image(fname)
except:
ErrorFileList.append(fname)
dst = fname.replace("Cat","Error")
tf.io.gfile.rename(fname , dst)
for e in ErrorFileList:
CatFileList.remove(e)
ErrorFileList = []
for fname in tqdm(DogFileList):
try:
load_image(fname)
except:
ErrorFileList.append(fname)
dst = fname.replace("Dog","Error")
tf.io.gfile.rename(fname , dst)
for e in ErrorFileList:
DogFileList.remove(e)
ErrorFile = tf.io.gfile.glob("./PetImages/Error/*.jpg")
if len(ErrorFile) == 0:
FilterOpenErrorFile()
100%|███████████████████████████████████████████████████████████████████████████| 12427/12427 [01:34<00:00, 131.10it/s]
100%|███████████████████████████████████████████████████████████████████████████| 12397/12397 [01:00<00:00, 205.36it/s]
print("Num of Cat File : ",len(CatFileList))
print("Num of Dog File : ",len(DogFileList))
Num of Cat File : 12427
Num of Dog File : 12397
- Error가 있는 Image File 들을 제거하고 나니, Image File 수가 약간 줄어들었네요.
3. Writing TFRecord Format File
-
이제 본격적으로 시작해 보도록 하겠습니다.
-
우선 앞에서 만들어 놓은 각각의 Full Path List를 이용해서 Dataset을 각각 만듭니다.
cat_dataset = tf.data.Dataset.from_tensor_slices( CatFileList )
dog_dataset = tf.data.Dataset.from_tensor_slices( DogFileList )
-
우리가 이번에 TFRecord File로 저장하려고 하는 내용은 EfficientNet으로 추출된 Image들의 Feature들입니다.
-
그래서 앞의 load_image() Function에서 efficientnet의 preprocess() Function을 사용했습니다.
-
알아두시면 좋은 것은 TFRecord나 Tensorflow Pipeline을 구성할 때 가능하면 모든 Function은 Tensorflow에서 제공하는 Function을 사용하면 성능면에서 훨씬 뛰어난 결과를 얻을 수 있습니다.
( 살펴보시면 load_image() Function에서도 모든 Code를 TF Module로만 작성하였습니다. )
- Cat Dataset / Dog Dataset 2개를 만듭니다.
cat_dataset = cat_dataset.map(
load_image,
num_parallel_calls=tf.data.experimental.AUTOTUNE)
dog_dataset = dog_dataset.map(
load_image,
num_parallel_calls=tf.data.experimental.AUTOTUNE)
print(cat_dataset , dog_dataset)
<ParallelMapDataset shapes: ((224, 224, 3), ()), types: (tf.float32, tf.string)> <ParallelMapDataset shapes: ((224, 224, 3), ()), types: (tf.float32, tf.string)>
-
Dataset에 Map Function(load_image)를 적용하면, Return Data의 Shape과 Type을 주목해 주시기 바랍니다.
-
(224,224,3)은 EfficientNet의 Input Shape이며, Data Type은 Float입니다.
-
Label 생성에 사용할 목적으로 Full Path도 같이 Return하고 있으며, String형태입니다.
-
Feature를 Extract할 CNN Model을 만듭니다.
-
EfficientNetB0를 이용해서 Feature Extraction을 하도록 하겠습니다.
image_model = tf.keras.applications.EfficientNetB0(include_top=False,
weights='imagenet')
new_input = image_model.input
hidden_layer = image_model.layers[-1].output
image_features_extract_model = tf.keras.Model(new_input, hidden_layer)
print(cat_dataset.cardinality().numpy())
print(dog_dataset.cardinality().numpy())
12427
12397
- 각 Dataset의 전체 Image File의 개수는 위와 같습니다.
-
아래 Function이 TFRecord File로 저장하는 부분을 구현한 내용이며 이번 Post의 핵심입니다.
- 제가 TFRecord File로 저장하려는 내용은 2가지입니다.
- EfficientNet으로 추출한 Image의 Feature. Shape은 (7,7,1280) 입니다.
- 해당 Image에 대한 Label 입니다. Cat은 0, Dog는 1로 하도록 하겠습니다.
- feature = image_features_extract_model(img)
- EfficientNet으로부터 Feature를 추출합니다.
- Shape은 (7,7,1280) 입니다.
- feature = feature.numpy().flatten()
- 제가 TFRecord에 관련해서 Test해 본 결과, TFRecord는 2차원 이상의 Array는 저장하지 못하더라구요.
- 위와 같이 (7,7,1280)을 Flatten할 필요가 있습니다.
- writer = tf.io.TFRecordWriter( tf_path )
- TFRecordWriter를 하나 정의합니다.
- Parameter는 TFRecord File의 Full Path입니다.
-
tf.train.Example을 이용해서 TFRecord File에 저장될 내용의 Format을 정의합니다.
-
tf.train.Features()로 Contents를 정의합니다.
- feature라는 Dict.으로 정의하는데, Dict.의 Key는 Item의 Name, Value는 해당 Item의 Data Type과 값을 가지고 있는 변수를 지정합니다.
- 이번 예제에서는 2가지 값만 정의했지만, 필요에 따라서는 더 많이 추가하시면 됩니다.
- ‘Feature’: tf.train.Feature(float_list=tf.train.FloatList(value = feature))
- Key는 ‘Feature’이고, Image의 Feature를 저장할 부분입니다.
- value는 앞에서 구한 feature를 넣습니다.
- ‘Label’: tf.train.Feature(int64_list=tf.train.Int64List(value = label))
- Key는 ‘Label’이고, Image가 Cat인지 Dog인지를 저장합니다.
- Value의 Data Type에 따라서 어떤 tf.train.XXX를 사용할지는 아래를 참고해 주시기 바랍니다.
- tf.train.BytesList
- string
- byte
- tf.train.FloatList
- float (float32)
- double (float64)
- tf.train.Int64List
- bool
- enum
- int32
- uint32
- int64
- uint64
- tf.train.BytesList
- writer.write(example.SerializeToString())
- 준비가 다 되었으면, 실제 TFRecord File로 기록합니다.
for img, path in tqdm(cat_dataset):
feature = image_features_extract_model(img)
tf_path = path.numpy().decode("utf-8")
tf_path = tf_path.replace("Cat" , "Cat/TFRecord")
tf_path = tf_path.replace("jpg" , "tfrecord")
feature = feature.numpy().flatten() #TensorShape([1, 7, 7, 1280])
writer = tf.io.TFRecordWriter( tf_path )
label = [0] # Cat
example = tf.train.Example(features=tf.train.Features(
feature={'Feature': tf.train.Feature(float_list=tf.train.FloatList(value = feature)),
'Label': tf.train.Feature(int64_list=tf.train.Int64List(value = label))
}
)
)
writer.write(example.SerializeToString())
- Dog도 Cat과 동일하게 작성합니다.
for img, path in tqdm(dog_dataset):
feature = image_features_extract_model(img)
tf_path = path.numpy().decode("utf-8")
tf_path = tf_path.replace("Dog" , "Dog/TFRecord")
tf_path = tf_path.replace("jpg" , "tfrecord")
feature = feature.numpy().flatten() #TensorShape([1, 7, 7, 1280])
writer = tf.io.TFRecordWriter( tf_path )
label = [1] # Dog
example = tf.train.Example(features=tf.train.Features(
feature={'Feature': tf.train.Feature(float_list=tf.train.FloatList(value = feature)),
'Label': tf.train.Feature(int64_list=tf.train.Int64List(value = label))
}
)
)
writer.write(example.SerializeToString())
-
한참 시간이 지나면 Cat / Dog Folder 내에 TFReocrd라는 Folder가 생기고, 그 안에 Image File Name과 동일하지만 확장자만 .tfrecord인 TFReocrd File들이 생겼을 것입니다.
-
다음 Post에서는 이렇게 만든 TFReocrd File들을 이용하여 Image Classification을 해보도록 하겠습니다.