이와 같은 전처리 레이어는 다양하게 keras에서 제공하고 있으며, tf.data.Datasets와 활용하면 매우 효과적입니다. 다음 그림에서 전처리 레이어가 얼마나 다양하게 존재하는지 알 수 있습니다.
모델 준비하기
이제 모델을 구성하면 됩니다.(seq2seq 모델) 케라스와 TensorFlow AddOnes를 활용하면 쉽게 구성할 수 있습니다. seq2seq 모델은 상당히 복잡한데, Encoder는 keras layer를 통해 쉽게 구성할 수 있고, Decoder는 AddOnes를 활용하여 한번에 구성할 수 있는 것을 영상에서 보여줍니다.
TF 2.x에서는 학습 과정을 클린하고 모듈화된 과정을 제공하기 위해 노력한다고 합니다. train_step을 직접 만들고, Gradient_Tape()를 활용하여 모델을 업데이트시키죠.
학습 단계를 정의했다면, 기존의 케라스와 동일하게 compile() 함수를 활용해서 과정을 설정합니다. 그래서 위의 train_step을 직접 정의해야 되냐고요? model.fit() 함수를 사용하면 위의 과정이 전부 포함되어 있습니다.
model = MyModel()
model.compile(...)
model.fit(...)
케라스를 사용해서 우리는 만족할 수준의 모델 성능을 쉽게 얻을 수 있었습니다. 하지만 아직 하이퍼파라미터 튜닝을 진행해보지 않았군요.
하이퍼파라미터 튜닝하기
KerasTuner를 활용한다면 매우 쉽습니다! 코드에서 hp(hyperparameter 객체) 인자를 사용해 튜닝을 준비하고 있습니다.
import kerastuner as kt
def build_model(hp):
model = EncoderDecoder(
rnn_units = hp.Int(
'units', min_value = 256, max_value = 1100, step = 256))
model.compile(...)
model.vectorize_layer.adapt(lines.batch(256))
return model
Dataset.map(f)는 입력 데이터셋의 각 원소에 주어진 함수 f를 적용하여 새로운 데이터셋을 생성해줍니다. 함수형 프로그래밍 언어에서 리스트 또는 기타 구조에 적용되는 map() 함수를 기반으로 합니다. 함수 f는 입력에서 단일 요소인 tf.Tensor 오브젝트를 받으며, 새로운 데이터셋에 포함될 tf.Tensor 오브젝트를 반환합니다. 이에 대한 구현은 TensorFlow 연산을 사용하여 한 요소를 다른 요소로 변환합니다.
이번 절에서는 Dataset.map()의 사용 방법을 다룹니다.
Decoding image data and resizing it
실제 환경의 이미지 데이터를 학습시킬 때, 보통 서로 다른 크기의 이미지를 공통 크기로 변환하여 고정 크기의 배치를 사용합니다. flower 데이터셋을 사용해봅시다.
# Reads an image from a file, decodes it into a dense tensor, and resizes it
# to a fixed shape.
def parse_image(filename):
parts = tf.strings.split(filename, '/')
label = parts[-2]
image = tf.io.read_file(filename)
image = tf.image.decode_jpeg(image)
image = tf.image.convert_image_dtype(image, tf.float32)
image = tf.image.resize(image, [128, 128])
return image, label
images_ds = list_ds.map(parse_image)
for image, label in images_ds.take(2):
show(image, label)
Applying arbitrary Python logic
데이터 전처리 작업에 TensorFlow 연산을 사용하면 성능적으로 이득을 볼 수 있습니다. 하지만 가끔은 입력 데이터를 처리하기 위해 파이썬 라이브러리 함수가 유용할 때가 있습니다. 이를 위해 Dataset.map()에서 tf.py_function()을 사용하세요.
예를 들어, random rotation 처리를 적용하고 싶지만 TensorFlow 연산은 tf.image의 tf.image.rot90 함수만 제공하기 때문에 유용하지 않을 수 있습니다. tf.py_function()을 경험해보기 위해, scipy.ndimage.rotate 함수를 사용해보죠.
또는, dense한 prediction을 원할 경우, feature와 label을 한 단계씩 이동(shift)할 수 있습니다.
def dense_1_step(batch):
# Shift features and labels one step relative to each other.
return batch[:-1], batch[1:]
predict_dense_1_step = batches.map(dense_1_step)
for features, label in predict_dense_1_step.take(3):
print(features.numpy(), " => ", label.numpy())
batches = range_ds.batch(15, drop_remainder=True)
def label_next_5_steps(batch):
return (batch[:-5], # Take the first 5 steps
batch[-5:]) # take the remainder
predict_5_steps = batches.map(label_next_5_steps)
for features, label in predict_5_steps.take(3):
print(features.numpy(), " => ", label.numpy())
데이터셋은 15 배치 크기를 가집니다. label_next_5_steps에서 batch[:-5]는 학습 데이터로 0~9까지 10개, batch[-5:]는 레이블로 10~14까지 5개를 반환합니다.
windows는 [0, 1, 2, 3, 4] --> [1, 2, 3, 4, 5] --> [2, 3, 4, 5, 6] --> ... 과 같이 데이터를 반환합니다.
거의 모든 경우에서, dataset의 첫 단계로 .batch를 사용할 것입니다.
def sub_to_batch(sub):
return sub.batch(window_size, drop_remainder=True)
for example in windows.flat_map(sub_to_batch).take(5):
print(example.numpy())
creditcard_ds = tf.data.experimental.make_csv_dataset(
csv_path, batch_size=1024, label_name="Class",
# Set the column types: 30 floats and an int.
column_defaults=[float()]*30+[int()])
먼저, rejection resampling은 리샘플링에서도 자주 사용되는 방법입니다. 이에 대해 관심이 있다면, 직접 검색하여 공부하는 것도 나쁘지 않습니다.
experimental.sample_from_datasets의 문제점은 클래스마다 별도의 tf.data.Dataset가 필요하다는 것입니다. Dataset.filter를 사용하면 해결할 수 있지만, 데이터를 두배로 로드하는 결과를 초래합니다.
data.experimental.rejection_resample 함수는 dataset 한 번만 로드하여 균형잡힌 결과를 얻을 수 있게 도와줍니다. 밸런스를 위해 이에 위반하는 요소는 제거됩니다. data.experimental.rejection_resample에서 class_func 인자를 사용합니다. class_func 인자는 각 dataset의 요소에 적용되며, 밸런싱을 위해 어떤 클래스에 속하는지를 결정합니다.
creditcard_ds의 요소는 (features, label) 쌍으로 이루어져 있습니다. class_func는 해당 레이블을 반환합니다.
def class_func(features, label):
return label
resampler는 target distribution을 필요로 하며, 선택적으로 initial distribution 추정을 필요로 합니다.