Keras : ImageDataGenerator 대신에 tf.data로 빠르게 학습하기 1
2편 -
[AI/Self-Study] - Keras : ImageDataGenerator 대신에 tf.data로 빠르게 학습하기 2
이미지 분류 프로젝트 진행하면서 Keras의 ImageDataGenerator를 쓰는데 너무 느리다는 문제점이 발생했다.
구글링을 통해 Tensorflow의 tf.data API를 사용하면 이미지를 로딩하는 시간을 약 5배정도 감소시킬 수 있다고 해 직접 ImageDataGenerator를 사용해 EfficientNet 모델을 훈련시키는 코드를 tf.data로 바꿔보았다.
결과는 epoch=2 기준으로 ImageDataGenerator는 한 epoch당 2시간 (ETA) 예상 소요시간이 나왔고, tf.data API는 epoch 2번을 다 도는데 30분 정도 밖에 걸리지 않았다.
즉 ==> 실제로 훈련시간이 매우 단축되었다!
유용한 참고 링크들:
- Dump Keras-ImageDataGenerator. Start Using TensorFlow-tf.data (Part 1)
https://medium.com/swlh/dump-keras-imagedatagenerator-start-using-tensorflow-tf-data-part-1-a30330bdbca9 - Dump Keras-ImageDataGenerator. Start Using TensorFlow-tf.data (Part 2)
https://towardsdatascience.com/dump-keras-imagedatagenerator-start-using-tensorflow-tf-data-part-2-fba7cda81203 - 핸즈온 머신러닝 2판 p.504 - 515
- tf.data API로 성능 향상하기 www.tensorflow.org/guide/data_performance?hl=ko
- A guide to build fast input pipeline using tf.data api vs ImageDataGenerator medium.com/@razerspeed.rohit/a-guide-to-build-fast-input-pipeline-using-tf-data-api-vs-imagedatagenerator-9e537aa33b1c
- github.com/razerspeed/dataflow/blob/master/data_pipeline.ipynb
tf.data API사용하기
0. 하이퍼파라미터 세팅
BATCH_SIZE = 32
IMG_HEIGHT = 224
IMG_WIDTH = 224
AUTOTUNE = tf.data.experimental.AUTOTUNE
tf.data.experimental.AUTOTUNE = 작동하는 Network가 스스로 설정해 dataset을 잘 불러올 수 있게 결정
- 나중에 성능 최적화을 위해 프리페치(prefetch)와 멀티스레드 적재와 전처리를 사용할 때 텐서플로우가 환경을 기반으로 동적으로 적절한 스레드 개수를 선택 및 buffer size를 선택해준다.
- 멀티스레드로 데이터를 적재하고 전처리 하면 CPU의 멀티 코어를 활용해 GPU에서 훈련 스텝을 수행하는 것보다 짧은 시간안에 한 배치 데이터를 준비할 수 있다.
1. 데이터 불러오기
list_files() 함수는 파일 경로를 섞은 데이터셋을 반환한다. Shuffle를 원하지 않는다면 shuffle=False로 지정하면 된다.
path로 파일이 위치해 있는 경로를 넣으면 된다.
나중에 이 path에서 label를 가져오는 함수가 있음으로 label (target)이 될 부분과 파일명 부분을 /*/* 로 표시
# Create a source dataset from input data
train_list_ds = tf.data.Dataset.list_files(str(train_path+'/*/*'),shuffle=True) # 파일 경로를 섞은 데이터셋 반환
valid_list_ds = tf.data.Dataset.list_files(str(valid_path+'/*/*'),shuffle=False)
아래 코드로 데이터 셋을 확인할 수 있다. (이미지 파일의 경로가 String으로 출력)
take() 메서드는 데이터 셋에 있는 몇 개의 아이템만 볼 수 있도록 해준다.
for f in train_list_ds.take(5): # check
print(f.numpy())
2. 데이터 준비
tf.data를 사용해 이미지를 지정된 배치 단위로 로딩한다.
아래는
1) tf.data.Dataset.list_files로 가지고온 dataset list 에서 (string으로 저장됨) slicing을 이용해서 label (target) 이름을 가지고 온다. 폴더 구조 상 data file path에서 (train_path+'/*/*') 뒤에서 두번째가 label 이 될 디렉토리
2) 경로에서 이미지를 읽어서 이미지를 tensor로 변환하고 resize
3) image 와 label 를 tensor로 반환
# 파일의 트리 구조를 사용하여 class_names 목록을 컴파일
CLASS_NAMES = np.array(sorted(os.listdir(train_path)))
def get_label(file_path):
# convert the path to a list of path components
parts = tf.strings.split(file_path, os.path.sep)
# The second to last is the class-directory
# path 의 뒤에서 2번째가 output class 디렉토리
return parts[-2] == CLASS_NAMES
def decode_img(img):
# convert the compressed string to a 3D uint8 tensor
img = tf.image.decode_jpeg(img, channels=3)
# resize the image to the desired size
return tf.image.resize(img, [IMG_WIDTH, IMG_HEIGHT])
def process_path(file_path):
label = get_label(file_path)
# load the raw data from the file as a string
img = tf.io.read_file(file_path)
img = decode_img(img)
return img, label
3. 성능 최적화
데이터 셋을 효율적으로 셔플링, 반복, 배치를 적용한 데이터 셋을 만들어서 반환.
마지막에 prefetch()를 호출하면 데이터 셋은 항상 한 배치가 미리 준비되어 있도록 한다. 즉, 훈련 알고리즘이 한 배치로 작업을 하는 동안 데이터 셋은 동시에 다음 배치를 준비 (예를 들면, 디스크에서 데이터를 읽고 전처리) 한다. => 성능을 크게 향상 시킴
뒤에 map() 메서드를 호출할 때 num_parallel_calls 매개변수를 지정해 멀티스레드로 데이터를 적재하고 전처리 하면 CPU의 멀티 코어를 활용해서 GPU에서 훈련 스텝을 수행하는 것보다 짧은 시간에 한 배치 데이터를 준비할 수 있다 => 훈련 속도가 더 빨라짐
* 데이터 셋이 메모리에 모두 들어갈 수 있을 정도로 작을 때 RAM에 모두 캐싱 할 수 있는 cache() 메서드를 사용하면 훈력 속도를 크게 높일 수 있다. 일반적으로 데이터를 적재하고 전처리 한 후에, 셔플링, 반복, 배치, 프리페치 하기 전에 캐시를 수행한다 (각 샘플을 매 에포크가 아니라 한번만 읽고 전처리 하지만 에포크 마다 다르게 셔플링 되게 하고 다음 배치도 미리 준비할 수 있다).
def prepare_for_training(ds, cache=True, shuffle_buffer_size=1000):
if cache:
if isinstance(cache, str):
ds = ds.cache(cache)
else:
ds = ds.cache()
ds = ds.shuffle(buffer_size=shuffle_buffer_size)
ds = ds.repeat()
ds = ds.batch(BATCH_SIZE)
ds = ds.prefetch(buffer_size=AUTOTUNE)
return ds
+ repeat() batch() 순서에 따라 다른점
4. 데이터 변환
map() 메서드를 호출해 아이템을 변환 (데이터 전처리 작업에 활용) 한다. 이때 num_parallel_calls 매개변수를 지정하면 복잡한 계산을 포함할 때 여러 스레드로 나눠서 속도를 높일 수 있다.
* map() 과 apply()의 차이점 : map() 메서드는 각 아이템에 변환을 적용하고, apply() 메서드는 데이터셋 전체에 변환을 적용한다.
train_labeled_ds = train_list_ds.map(process_path, num_parallel_calls=AUTOTUNE) # 전처리
test_labeled_ds = test_list_ds.map(process_path, num_parallel_calls=AUTOTUNE)
train_ds = prepare_for_training(train_labeled_ds) # 셔플링, 반복, 배치, 프리페치 적용
test_ds = prepare_for_training(test_labeled_ds)
5. 학습
# 1. model
model = Sequential()
model.add(EfficientNetB0(include_top=False, pooling = 'avg', weights = "imagenet"))
model.add(Dense(n, activation='softmax'))
# model.summary()
# checkpoint 저장 path 설정
checkpoint_path = '저장할 경로 + 이름.ckpt'
# callbacks
modelcheckpoint = ModelCheckpoint(checkpoint_path, monitor='val_loss', mode='min', save_best_only=True, save_weights_only=True, verbose=0)
reduce_lr = ReduceLROnPlateau(monitor='val_loss', factor=0.1, patience=10, verbose=1)
time_callback = TimeHistory() # 훈련 시간
# tensorboard 설정
to_hist = TensorBoard(log_dir='로그 저장할 폴더', histogram_freq=0, write_graph=True, write_images=True)
# 2. compile, fit
model.compile(optimizer = 'adam', loss = tf.keras.losses.categorical_crossentropy, metrics = ['acc'])
hist = model.fit(train_ds,
steps_per_epoch=int(len(train_labeled_ds) / BATCH_SIZE),
epochs=100,
validation_data=test_ds,
validation_steps=int(len(test_labeled_ds) / BATCH_SIZE),
callbacks=[modelcheckpoint, reduce_lr, time_callback, to_hist])
print(time_callback.times) # 리스트 반환 (fit한 시간)
model.load_weights(checkpoint_path)
#모델 저장
model_path = '모델 저장 경로 + 이름.h5'
model.save(model_path)
'AI > Self-Study' 카테고리의 다른 글
음성인식에 필요한 기초개념 1 (1) | 2021.04.29 |
---|---|
Keras : ImageDataGenerator 대신에 tf.data로 빠르게 학습하기 2 (0) | 2021.04.12 |
Keras에서 predict와 predict_generator 가 다른 값을 내는 경우 (Image Data Generator) (0) | 2021.04.09 |
ArcFace - ResNetFace / SE-LResNet50E-IR (2) | 2021.04.09 |
K-Fold Cross Validation 딥러닝 (Keras, Image Data Generator) (0) | 2021.04.08 |
댓글
이 글 공유하기
다른 글
-
음성인식에 필요한 기초개념 1
음성인식에 필요한 기초개념 1
2021.04.29 -
Keras : ImageDataGenerator 대신에 tf.data로 빠르게 학습하기 2
Keras : ImageDataGenerator 대신에 tf.data로 빠르게 학습하기 2
2021.04.12 -
Keras에서 predict와 predict_generator 가 다른 값을 내는 경우 (Image Data Generator)
Keras에서 predict와 predict_generator 가 다른 값을 내는 경우 (Image Data Generator)
2021.04.09 -
ArcFace - ResNetFace / SE-LResNet50E-IR
ArcFace - ResNetFace / SE-LResNet50E-IR
2021.04.09