Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
tensorflow
GitHub Repository: tensorflow/docs-l10n
Path: blob/master/site/ko/guide/keras/preprocessing_layers.ipynb
25118 views
Kernel: Python 3
#@title Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License.

전처리 레이어 처리

Keras 전처리

개발자는 Keras 전처리 레이어 API를 사용하여 Keras 네이티브 입력 처리 파이프라인을 구축할 수 있습니다. 이러한 입력 처리 파이프라인은 Keras가 아닌 워크플로에서 독립적인 전처리 코드로 사용할 수 있고, Keras 모델과 직접 결합하고, Keras SavedModel의 일부로 내보낼 수 있습니다.

Keras 전처리 레이어를 사용하여 진정한 엔드 투 엔드 모델을 구축하고 내보낼 수 있으며, 이러한 모델에는 원시 이미지 또는 구조화된 원시 데이터를 입력으로 받아들이는 모델, 기능 정규화 또는 기능 값 인덱싱을 자체적으로 처리하는 모델이 있습니다.

사용할 수 있는 전처리

텍스트 전처리

  • tf.keras.layers.TextVectorization: 원시 문자열을 Embedding 레이어 또는 Dense 레이어에서 읽을 수 있는 인코딩 표현으로 바꿉니다.

숫자 기능 전처리

  • tf.keras.layers.Normalization: 입력 기능의 기능별 정규화를 수행합니다.

  • tf.keras.layers.Discretization: 연속 숫자 기능을 정수 범주형 기능으로 바꿉니다.

범주형 기능 전처리

  • tf.keras.layers.CategoryEncoding: 정수 범주형 기능을 원-핫(one-hot), 멀티-핫(multi-hot) 또는 카운트 밀집 표현(count dense representations)으로 바꿉니다.

  • tf.keras.layers.Hashing: "해싱 트릭(hashing trick)"이라 불리우는 범주형 기능 해싱을 수행합니다.

  • tf.keras.layers.StringLookup: 문자열 범주를 Embedding 레이어 또는 Dense 레이어에서 읽을 수 있는 인코딩 표현으로 바꿉니다.

  • tf.keras.layers.IntegerLookup: 정수 범주를 Embedding 레이어 또는 Dense 레이어에서 읽을 수 있는 인코딩 표현으로 바꿉니다.

이미지 전처리

이미지 모델 입력을 표준화하기 위한 레이어입니다.

  • tf.keras.layers.Resizing: 이미지 배치의 크기를 대상 크기로 조정합니다.

  • tf.keras.layers.Rescaling: 이미지 배치의 값을 재조정하고 오프셋합니다(예: [0, 255] 범위의 입력에서 [0, 1] 범위의 입력으로 이동).

  • tf.keras.layers.CenterCrop: 이미지 배치의 가운데 크롭을 반환합니다.

이미지 데이터 증강

이 레이어는 이미지 배치에 무작위 증강 변환을 적용합니다. 이러한 작업은 훈련 중에만 활성화됩니다.

  • tf.keras.layers.RandomCrop

  • tf.keras.layers.RandomFlip

  • tf.keras.layers.RandomTranslation

  • tf.keras.layers.RandomRotation

  • tf.keras.layers.RandomZoom

  • tf.keras.layers.RandomHeight

  • tf.keras.layers.RandomWidth

  • tf.keras.layers.RandomContrast

adapt() 메서드

일부 전처리 레이어에는 훈련 데이터의 샘플을 기반으로 계산할 수 있는 내부 상태가 있습니다. 상태 저장 전처리 레이어 목록은 다음과 같습니다.

  • TextVectorization: 문자열 토큰과 정수 인덱스 간의 매핑을 유지합니다.

  • StringLookupIntegerLookup: 입력 값과 정수 인덱스 간의 매핑을 유지합니다.

  • Normalization: 기능의 평균과 표준편차를 유지합니다.

  • Discretization: 값 버킷 경계에 대한 정보를 유지합니다.

결정적으로 이러한 레이어는 훈련할 수 없습니다. 이들의 상태는 훈련 중에 설정되지 않습니다. 미리 계산된 상수로부터 초기화하거나 데이터에 "적용"하는 방식을 사용하여 훈련하기 전에 설정해야 합니다.

다음과 같이 adapt() 메서드를 통해 학습 데이터에 노출하여 전처리 레이어의 상태를 설정할 수 있습니다.

import numpy as np import tensorflow as tf from tensorflow.keras import layers data = np.array([[0.1, 0.2, 0.3], [0.8, 0.9, 1.0], [1.5, 1.6, 1.7],]) layer = layers.Normalization() layer.adapt(data) normalized_data = layer(data) print("Features mean: %.2f" % (normalized_data.numpy().mean())) print("Features std: %.2f" % (normalized_data.numpy().std()))

adapt() 메소드는 Numpy 배열 또는 tf.data.Dataset 객체를 사용합니다. StringLookupTextVectorization 의 경우, 문자열의 목록을 전달할 수도 있습니다.

data = [ "ξεῖν᾽, ἦ τοι μὲν ὄνειροι ἀμήχανοι ἀκριτόμυθοι", "γίγνοντ᾽, οὐδέ τι πάντα τελείεται ἀνθρώποισι.", "δοιαὶ γάρ τε πύλαι ἀμενηνῶν εἰσὶν ὀνείρων:", "αἱ μὲν γὰρ κεράεσσι τετεύχαται, αἱ δ᾽ ἐλέφαντι:", "τῶν οἳ μέν κ᾽ ἔλθωσι διὰ πριστοῦ ἐλέφαντος,", "οἵ ῥ᾽ ἐλεφαίρονται, ἔπε᾽ ἀκράαντα φέροντες:", "οἱ δὲ διὰ ξεστῶν κεράων ἔλθωσι θύραζε,", "οἵ ῥ᾽ ἔτυμα κραίνουσι, βροτῶν ὅτε κέν τις ἴδηται.", ] layer = layers.TextVectorization() layer.adapt(data) vectorized_text = layer(data) print(vectorized_text)

또한, 적응 가능한 레이어는 항상 생성자 인수 또는 가중치 할당을 통해 상태를 직접 설정하는 옵션을 제공합니다. 의도한 상태 값이 레이어 생성 시 알려지거나 adapt() 호출 외부에서 계산되는 경우, 레이어의 내부 계산에 의존하지 않고 설정할 수 있습니다. 예를 들어, TextVectorization , StringLookup 또는 IntegerLookup 레이어에 대한 외부 어휘 파일이 이미 있는 경우, 레이어의 생성자 인수에 있는 어휘 파일에 대한 경로를 전달하여 조회 테이블에 직접 로드할 수 있습니다.

다음은 사전 계산된 어휘로 StringLookup 레이어를 인스턴스화하는 예입니다.

vocab = ["a", "b", "c", "d"] data = tf.constant([["a", "c", "d"], ["d", "z", "b"]]) layer = layers.StringLookup(vocabulary=vocab) vectorized_data = layer(data) print(vectorized_data)

모델 이전 또는 모델 내부에서의 데이터 전처리

전처리 레이어를 사용할 수 있는 두 가지 방법이 있습니다.

옵션 1: 다음과 같이 모델의 일부로 만듭니다.

inputs = keras.Input(shape=input_shape) x = preprocessing_layer(inputs) outputs = rest_of_the_model(x) model = keras.Model(inputs, outputs)

이 옵션을 사용하면 나머지 모델 실행과 동시에 장치에서 전처리가 발생하므로 GPU 가속의 이점을 얻을 수 있습니다. GPU로 훈련을 진행하는 경우 Normalization 레이어와 모든 이미지 전처리 및 데이터 강화 레이어에 이 옵션이 가장 적합합니다.

옵션 2: tf.data.Dataset에 이 옵션을 적용하면 다음과 같이 전처리된 데이터 배치를 생성하는 데이터세트를 얻습니다.

dataset = dataset.map(lambda x, y: (preprocessing_layer(x), y))

이 옵션을 사용하면 전처리가 CPU에서 비동기적으로 발생하고 모델에 들어가기 전에 전처리가 버퍼링됩니다. 추가적으로 데이터세트에서 dataset.prefetch(tf.data.AUTOTUNE)를 호출하면 사전처리가 훈련과 병행하게 효율적으로 이루어집니다.

dataset = dataset.map(lambda x, y: (preprocessing_layer(x), y)) dataset = dataset.prefetch(tf.data.AUTOTUNE) model.fit(dataset, ...)

이는 TextVectorization 및 모든 구조화된 데이터 전처리 레이어에 가장 적합한 옵션입니다. CPU로 훈련하고 이미지 전처리 레이어를 사용하는 경우에도 좋은 옵션이 될 수 있습니다.

TPU에서 실행할 경우 항상 tf.data 파이프라인에 전처리 레이어를 배치해야 합니다(TPU에서 잘 실행되고 일반적으로 첫 번째 레이어를 이미지 모델로 사용하는 NormalizationRescaling은 제외).

추론 시 모델 내에서 전처리를 수행할 때의 이점

옵션 2를 사용하더라도 나중에 전처리 레이어를 포함하는 추론 전용 엔드 투 엔드 모델을 내보낼 수 있습니다. 이 작업의 주요 이점은 모델을 이식 가능하게 만들고 **훈련/적용 편향**을 줄이는 데 도움이 된다는 것입니다.

모든 데이터 전처리가 모델의 일부인 경우, 다른 사람들은 각 특성이 어떻게 인코딩되고 정규화될 것으로 예상되는지 알 필요 없이 모델을 로드하고 사용할 수 있습니다. 추론 모델은 원시 이미지 또는 원시 구조적 데이터를 처리할 수 있으며, 모델 사용자가 예를 들어, 텍스트에 사용되는 토큰화 체계, 범주형 특성에 사용되는 인덱싱 체계, 이미지 픽셀 값이 [-1, +1] 또는 [0, 1]로 정규화되었는지 여부와 같은 세부 정보를 알 필요가 없습니다. 이는 모델을 TensorFlow.js와 같은 다른 런타임으로 내보내는 경우 특히 강력합니다. JavaScript에서 전처리 파이프라인을 다시 구현할 필요가 없습니다.

처음에 전처리 레이어를 tf.data 파이프라인에 배치한 경우, 전처리를 패키징하는 추론 모델을 내보낼 수 있습니다. 전처리 레이어와 훈련 모델을 연결하는 새 모델을 인스턴스화하기만 하면 됩니다.

inputs = keras.Input(shape=input_shape) x = preprocessing_layer(inputs) outputs = training_model(x) inference_model = keras.Model(inputs, outputs)

퀵 레시피

이미지 데이터 증강

이미지 데이터 증강 레이어는 훈련 중에만 활성화됩니다(Dropout 레이어와 유사).

from tensorflow import keras from tensorflow.keras import layers # Create a data augmentation stage with horizontal flipping, rotations, zooms data_augmentation = keras.Sequential( [ layers.RandomFlip("horizontal"), layers.RandomRotation(0.1), layers.RandomZoom(0.1), ] ) # Load some data (x_train, y_train), _ = keras.datasets.cifar10.load_data() input_shape = x_train.shape[1:] classes = 10 # Create a tf.data pipeline of augmented images (and their labels) train_dataset = tf.data.Dataset.from_tensor_slices((x_train, y_train)) train_dataset = train_dataset.batch(16).map(lambda x, y: (data_augmentation(x), y)) # Create a model and train it on the augmented image data inputs = keras.Input(shape=input_shape) x = layers.Rescaling(1.0 / 255)(inputs) # Rescale inputs outputs = keras.applications.ResNet50( # Add the rest of the model weights=None, input_shape=input_shape, classes=classes )(x) model = keras.Model(inputs, outputs) model.compile(optimizer="rmsprop", loss="sparse_categorical_crossentropy") model.fit(train_dataset, steps_per_epoch=5)

처음부터 이미지 분류하기 예제에서 유사한 설정이 동작하는 것을 볼 수 있습니다.

수치 특성 정규화하기

# Load some data (x_train, y_train), _ = keras.datasets.cifar10.load_data() x_train = x_train.reshape((len(x_train), -1)) input_shape = x_train.shape[1:] classes = 10 # Create a Normalization layer and set its internal state using the training data normalizer = layers.Normalization() normalizer.adapt(x_train) # Create a model that include the normalization layer inputs = keras.Input(shape=input_shape) x = normalizer(inputs) outputs = layers.Dense(classes, activation="softmax")(x) model = keras.Model(inputs, outputs) # Train the model model.compile(optimizer="adam", loss="sparse_categorical_crossentropy") model.fit(x_train, y_train)

원-핫 인코딩을 통해 문자열 범주형 특성 인코딩하기

# Define some toy data data = tf.constant([["a"], ["b"], ["c"], ["b"], ["c"], ["a"]]) # Use StringLookup to build an index of the feature values and encode output. lookup = layers.StringLookup(output_mode="one_hot") lookup.adapt(data) # Convert new test data (which includes unknown feature values) test_data = tf.constant([["a"], ["b"], ["c"], ["d"], ["e"], [""]]) encoded_data = lookup(test_data) print(encoded_data)

여기에서 인덱스 0은 어휘 이외의 값(adapt() 동안 표시되지 않은 값)을 위해 예약되어 있습니다

구조화된 데이터 분류 처음부터 하기 예시에서 작동 중인 StringLookup을 볼 수 있습니다.

원-핫 인코딩을 통해 정수 범주형 특성 인코딩하기

# Define some toy data data = tf.constant([[10], [20], [20], [10], [30], [0]]) # Use IntegerLookup to build an index of the feature values and encode output. lookup = layers.IntegerLookup(output_mode="one_hot") lookup.adapt(data) # Convert new test data (which includes unknown feature values) test_data = tf.constant([[10], [10], [20], [50], [60], [0]]) encoded_data = lookup(test_data) print(encoded_data)

인덱스 0은 누락된 값(값을 0으로 지정해야 함)용으로 예약되어 있고 인덱스 1은 어휘 외 의값(adapt() 동안 표시되지 않은 값)용으로 예약되어 있습니다. IntegerLookupmask_tokenoov_token 생성자 인수를 사용하여 이를 구성할 수 있습니다.

구조화된 데이터 분류 처음부터 하기 예시에서 작동 중인 IntegerLookup을 볼 수 있습니다.

정수 범주형 특성에 해싱 트릭 적용하기

여러 다른 값(10e3 이상)을 사용할 수 있는 범주형 특성의 각 값이 데이터에서 몇 번만 나타나는 경우, 특성 값을 인덱싱하고 원-핫 인코딩하는 것은 비실용적이고 비효율적입니다. 대신, "해싱 트릭"을 적용하는 것이 좋습니다. 값을 고정된 크기의 벡터로 해싱합니다. 이는 특성 공간의 크기를 관리 가능한 상태로 유지하고 명시적 인덱싱의 필요성을 제거합니다.

# Sample data: 10,000 random integers with values between 0 and 100,000 data = np.random.randint(0, 100000, size=(10000, 1)) # Use the Hashing layer to hash the values to the range [0, 64] hasher = layers.Hashing(num_bins=64, salt=1337) # Use the CategoryEncoding layer to multi-hot encode the hashed values encoder = layers.CategoryEncoding(num_tokens=64, output_mode="multi_hot") encoded_data = encoder(hasher(data)) print(encoded_data.shape)

일련의 토큰 인덱스로 텍스트 인코딩하기

Embedding 레이어에 전달될 텍스트를 전처리하는 방법입니다.

# Define some text data to adapt the layer adapt_data = tf.constant( [ "The Brain is wider than the Sky", "For put them side by side", "The one the other will contain", "With ease and You beside", ] ) # Create a TextVectorization layer text_vectorizer = layers.TextVectorization(output_mode="int") # Index the vocabulary via `adapt()` text_vectorizer.adapt(adapt_data) # Try out the layer print( "Encoded text:\n", text_vectorizer(["The Brain is deeper than the sea"]).numpy(), ) # Create a simple model inputs = keras.Input(shape=(None,), dtype="int64") x = layers.Embedding(input_dim=text_vectorizer.vocabulary_size(), output_dim=16)(inputs) x = layers.GRU(8)(x) outputs = layers.Dense(1)(x) model = keras.Model(inputs, outputs) # Create a labeled dataset (which includes unknown tokens) train_dataset = tf.data.Dataset.from_tensor_slices( (["The Brain is deeper than the sea", "for if they are held Blue to Blue"], [1, 0]) ) # Preprocess the string inputs, turning them into int sequences train_dataset = train_dataset.batch(2).map(lambda x, y: (text_vectorizer(x), y)) # Train the model on the int sequences print("\nTraining model...") model.compile(optimizer="rmsprop", loss="mse") model.fit(train_dataset) # For inference, you can export a model that accepts strings as input inputs = keras.Input(shape=(1,), dtype="string") x = text_vectorizer(inputs) outputs = model(x) end_to_end_model = keras.Model(inputs, outputs) # Call the end-to-end model on test data (which includes unknown tokens) print("\nCalling end-to-end model on test string...") test_data = tf.constant(["The one the other will absorb"]) test_output = end_to_end_model(test_data) print("Model output:", test_output)

처음부터 텍스트 분류 예에서 Embedding 모드와 결합된 TextVectorization 레이어가 동작하는 것을 볼 수 있습니다.

이러한 모델을 훈련할 때에는 최상의 성능을 위해 TextVectorization 레이어를 입력 파이프라인의 일부로 사용해야 합니다.

멀티-핫 인코딩을 사용하여 텍스트를 ngram의 밀집 행렬로 인코딩하기

다음은 Dense 레이어로 전달될 텍스트를 전처리하는 방법입니다.

# Define some text data to adapt the layer adapt_data = tf.constant( [ "The Brain is wider than the Sky", "For put them side by side", "The one the other will contain", "With ease and You beside", ] ) # Instantiate TextVectorization with "multi_hot" output_mode # and ngrams=2 (index all bigrams) text_vectorizer = layers.TextVectorization(output_mode="multi_hot", ngrams=2) # Index the bigrams via `adapt()` text_vectorizer.adapt(adapt_data) # Try out the layer print( "Encoded text:\n", text_vectorizer(["The Brain is deeper than the sea"]).numpy(), ) # Create a simple model inputs = keras.Input(shape=(text_vectorizer.vocabulary_size(),)) outputs = layers.Dense(1)(inputs) model = keras.Model(inputs, outputs) # Create a labeled dataset (which includes unknown tokens) train_dataset = tf.data.Dataset.from_tensor_slices( (["The Brain is deeper than the sea", "for if they are held Blue to Blue"], [1, 0]) ) # Preprocess the string inputs, turning them into int sequences train_dataset = train_dataset.batch(2).map(lambda x, y: (text_vectorizer(x), y)) # Train the model on the int sequences print("\nTraining model...") model.compile(optimizer="rmsprop", loss="mse") model.fit(train_dataset) # For inference, you can export a model that accepts strings as input inputs = keras.Input(shape=(1,), dtype="string") x = text_vectorizer(inputs) outputs = model(x) end_to_end_model = keras.Model(inputs, outputs) # Call the end-to-end model on test data (which includes unknown tokens) print("\nCalling end-to-end model on test string...") test_data = tf.constant(["The one the other will absorb"]) test_output = end_to_end_model(test_data) print("Model output:", test_output)

TF-IDF 가중치를 사용하여 텍스트를 ngram의 밀집 행렬로 인코딩하기

다음은 Dense 레이어로 전달하기 전에 텍스트를 전처리하는 또 다른 방법입니다.

# Define some text data to adapt the layer adapt_data = tf.constant( [ "The Brain is wider than the Sky", "For put them side by side", "The one the other will contain", "With ease and You beside", ] ) # Instantiate TextVectorization with "tf-idf" output_mode # (multi-hot with TF-IDF weighting) and ngrams=2 (index all bigrams) text_vectorizer = layers.TextVectorization(output_mode="tf-idf", ngrams=2) # Index the bigrams and learn the TF-IDF weights via `adapt()` with tf.device("CPU"): # A bug that prevents this from running on GPU for now. text_vectorizer.adapt(adapt_data) # Try out the layer print( "Encoded text:\n", text_vectorizer(["The Brain is deeper than the sea"]).numpy(), ) # Create a simple model inputs = keras.Input(shape=(text_vectorizer.vocabulary_size(),)) outputs = layers.Dense(1)(inputs) model = keras.Model(inputs, outputs) # Create a labeled dataset (which includes unknown tokens) train_dataset = tf.data.Dataset.from_tensor_slices( (["The Brain is deeper than the sea", "for if they are held Blue to Blue"], [1, 0]) ) # Preprocess the string inputs, turning them into int sequences train_dataset = train_dataset.batch(2).map(lambda x, y: (text_vectorizer(x), y)) # Train the model on the int sequences print("\nTraining model...") model.compile(optimizer="rmsprop", loss="mse") model.fit(train_dataset) # For inference, you can export a model that accepts strings as input inputs = keras.Input(shape=(1,), dtype="string") x = text_vectorizer(inputs) outputs = model(x) end_to_end_model = keras.Model(inputs, outputs) # Call the end-to-end model on test data (which includes unknown tokens) print("\nCalling end-to-end model on test string...") test_data = tf.constant(["The one the other will absorb"]) test_output = end_to_end_model(test_data) print("Model output:", test_output)

중요 정보

매우 방대한 어휘를 보유한 룩업(lookup) 레이어로 작업 수행하기

TextVectorization, StringLookup 레이어 또는 IntegerLookup 레이어에서 매우 방대한 어휘로 작업해야 하는 경우가 있을 수 있습니다. 일반적으로 500MB보다 큰 어휘는 "매우 큰" 것으로 간주합니다.

이러한 경우 최상의 성능을 위해 adapt() 사용을 피해야 합니다. 대신 사전에 어휘를 미리 계산하고(이를 위해 Apache Beam 또는 TF Transform을 사용할 수 있음) 이를 파일에 저장합니다. 그런 다음 파일 경로를 vocabulary 인수로 전달하여 구성하는 시점에 어휘를 레이어에 로드합니다.

TPU 포드에서 또는 ParameterServerStrategy로 룩업(lookup) 레이어 사용하기

TPU 포드 또는 여러 기기에서 ParameterServerStrategy를 사용하여 훈련을 진행하는 동안 TextVectorization, StringLookup 또는 IntegerLookup을 사용할 경우 성능을 저하시키는 미해결 문제가 있습니다. 이는 TensorFlow 2.7에서 수정될 예정입니다.