<commit_before>{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "b518b04cbfe0"
      },
      "source": [
        "##### Copyright 2020 The TensorFlow Authors."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "cellView": "form",
        "id": "906e07f6e562"
      },
      "outputs": [],
      "source": [
        "#@title Licensed under the Apache License, Version 2.0 (the \"License\");\n",
        "# you may not use this file except in compliance with the License.\n",
        "# You may obtain a copy of the License at\n",
        "#\n",
        "# https://www.apache.org/licenses/LICENSE-2.0\n",
        "#\n",
        "# Unless required by applicable law or agreed to in writing, software\n",
        "# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
        "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
        "# See the License for the specific language governing permissions and\n",
        "# limitations under the License."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "6e083398b477"
      },
      "source": [
        "# 전처리 레이어 처리"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "64010bd23c2e"
      },
      "source": [
        "<table class=\"tfo-notebook-buttons\" align=\"left\">\n",
        "  <td><a target=\"_blank\" href=\"https://www.tensorflow.org/guide/keras/preprocessing_layers\"><img src=\"https://www.tensorflow.org/images/tf_logo_32px.png\">TensorFlow.org에서 보기</a></td>\n",
        "  <td><a target=\"_blank\" href=\"https://colab.research.google.com/github/tensorflow/docs-l10n/blob/master/site/ko/guide/keras/preprocessing_layers.ipynb\"><img src=\"https://www.tensorflow.org/images/colab_logo_32px.png\">Google Colab에서 실행</a></td>\n",
        "  <td><a target=\"_blank\" href=\"https://github.com/tensorflow/docs-l10n/blob/master/site/ko/guide/keras/preprocessing_layers.ipynb\">     <img src=\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\">    GitHub에서 소스 보기</a></td>\n",
        "  <td><a href=\"https://storage.googleapis.com/tensorflow_docs/docs-l10n/site/ko/guide/keras/preprocessing_layers.ipynb\"><img src=\"https://www.tensorflow.org/images/download_logo_32px.png\">노트북 다운로드</a></td>\n",
        "</table>"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "b1d403f04693"
      },
      "source": [
        "## Keras preprocessing\n",
        "\n",
        "The Keras preprocessing layers API allows developers to build Keras-native input\n",
        "processing pipelines. These input processing pipelines can be used as independent\n",
        "preprocessing code in non-Keras workflows, combined directly with Keras models, and\n",
        "exported as part of a Keras SavedModel.\n",
        "\n",
        "With Keras preprocessing layers, you can build and export models that are truly\n",
        "end-to-end: models that accept raw images or raw structured data as input; models that\n",
        "handle feature normalization or feature value indexing on their own."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "313360fa9024"
      },
      "source": [
        "## Available preprocessing\n",
        "\n",
        "### Text preprocessing\n",
        "\n",
        "- `tf.keras.layers.TextVectorization`: turns raw strings into an encoded\n",
        "  representation that can be read by an `Embedding` layer or `Dense` layer.\n",
        "\n",
        "### Numerical features preprocessing\n",
        "\n",
        "- `tf.keras.layers.Normalization`: performs feature-wise normalize of\n",
        "  input features.\n",
        "- `tf.keras.layers.Discretization`: turns continuous numerical features\n",
        "  into integer categorical features.\n",
        "\n",
        "### Categorical features preprocessing\n",
        "\n",
        "- `tf.keras.layers.CategoryEncoding`: turns integer categorical features\n",
        "  into one-hot, multi-hot, or count dense representations.\n",
        "- `tf.keras.layers.Hashing`: performs categorical feature hashing, also known as\n",
        "  the \"hashing trick\".\n",
        "- `tf.keras.layers.StringLookup`: turns string categorical values an encoded\n",
        "  representation that can be read by an `Embedding` layer or `Dense` layer.\n",
        "- `tf.keras.layers.IntegerLookup`: turns integer categorical values into an\n",
        "  encoded representation that can be read by an `Embedding` layer or `Dense`\n",
        "  layer.\n",
        "\n",
        "\n",
        "### Image preprocessing\n",
        "\n",
        "These layers are for standardizing the inputs of an image model.\n",
        "\n",
        "- `tf.keras.layers.Resizing`: resizes a batch of images to a target size.\n",
        "- `tf.keras.layers.Rescaling`: rescales and offsets the values of a batch of\n",
        "  image (e.g. go from inputs in the `[0, 255]` range to inputs in the `[0, 1]`\n",
        "  range.\n",
        "- `tf.keras.layers.CenterCrop`: returns a center crop of a batch of images.\n",
        "\n",
        "### Image data augmentation\n",
        "\n",
        "These layers apply random augmentation transforms to a batch of images. They\n",
        "are only active during training.\n",
        "\n",
        "- `tf.keras.layers.RandomCrop`\n",
        "- `tf.keras.layers.RandomFlip`\n",
        "- `tf.keras.layers.RandomTranslation`\n",
        "- `tf.keras.layers.RandomRotation`\n",
        "- `tf.keras.layers.RandomZoom`\n",
        "- `tf.keras.layers.RandomHeight`\n",
        "- `tf.keras.layers.RandomWidth`\n",
        "- `tf.keras.layers.RandomContrast`"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "c923e41fb1b4"
      },
      "source": [
        "## The `adapt()` method\n",
        "\n",
        "Some preprocessing layers have an internal state that can be computed based on\n",
        "a sample of the training data. The list of stateful preprocessing layers is:\n",
        "\n",
        "- `TextVectorization`: holds a mapping between string tokens and integer indices\n",
        "- `StringLookup` and `IntegerLookup`: hold a mapping between input values and integer\n",
        "indices.\n",
        "- `Normalization`: holds the mean and standard deviation of the features.\n",
        "- `Discretization`: holds information about value bucket boundaries.\n",
        "\n",
        "Crucially, these layers are **non-trainable**. Their state is not set during training; it\n",
        "must be set **before training**, either by initializing them from a precomputed constant,\n",
        "or by \"adapting\" them on data.\n",
        "\n",
        "You set the state of a preprocessing layer by exposing it to training data, via the\n",
        "`adapt()` method:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "4cac6bd80812"
      },
      "outputs": [],
      "source": [
        "import numpy as np\n",
        "import tensorflow as tf\n",
        "from tensorflow.keras import layers\n",
        "\n",
        "data = np.array([[0.1, 0.2, 0.3], [0.8, 0.9, 1.0], [1.5, 1.6, 1.7],])\n",
        "layer = layers.Normalization()\n",
        "layer.adapt(data)\n",
        "normalized_data = layer(data)\n",
        "\n",
        "print(\"Features mean: %.2f\" % (normalized_data.numpy().mean()))\n",
        "print(\"Features std: %.2f\" % (normalized_data.numpy().std()))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "d43b8246b8a3"
      },
      "source": [
        "`adapt()` 메소드는 Numpy 배열 또는 `tf.data.Dataset` 객체를 사용합니다. `StringLookup` 및 `TextVectorization` 의 경우, 문자열의 목록을 전달할 수도 있습니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "48d95713348a"
      },
      "outputs": [],
      "source": [
        "data = [\n",
        "    \"ξεῖν᾽, ἦ τοι μὲν ὄνειροι ἀμήχανοι ἀκριτόμυθοι\",\n",
        "    \"γίγνοντ᾽, οὐδέ τι πάντα τελείεται ἀνθρώποισι.\",\n",
        "    \"δοιαὶ γάρ τε πύλαι ἀμενηνῶν εἰσὶν ὀνείρων:\",\n",
        "    \"αἱ μὲν γὰρ κεράεσσι τετεύχαται, αἱ δ᾽ ἐλέφαντι:\",\n",
        "    \"τῶν οἳ μέν κ᾽ ἔλθωσι διὰ πριστοῦ ἐλέφαντος,\",\n",
        "    \"οἵ ῥ᾽ ἐλεφαίρονται, ἔπε᾽ ἀκράαντα φέροντες:\",\n",
        "    \"οἱ δὲ διὰ ξεστῶν κεράων ἔλθωσι θύραζε,\",\n",
        "    \"οἵ ῥ᾽ ἔτυμα κραίνουσι, βροτῶν ὅτε κέν τις ἴδηται.\",\n",
        "]\n",
        "layer = layers.TextVectorization()\n",
        "layer.adapt(data)\n",
        "vectorized_text = layer(data)\n",
        "print(vectorized_text)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "7619914dfb40"
      },
      "source": [
        "또한, 적응 가능한 레이어는 항상 생성자 인수 또는 가중치 할당을 통해 상태를 직접 설정하는 옵션을 제공합니다. 의도한 상태 값이 레이어 생성 시 알려지거나 `adapt()` 호출 외부에서 계산되는 경우, 레이어의 내부 계산에 의존하지 않고 설정할 수 있습니다. 예를 들어, `TextVectorization` , `StringLookup` 또는 `IntegerLookup` 레이어에 대한 외부 어휘 파일이 이미 있는 경우, 레이어의 생성자 인수에 있는 어휘 파일에 대한 경로를 전달하여 조회 테이블에 직접 로드할 수 있습니다.\n",
        "\n",
        "다음은 사전 계산된 어휘로 `StringLookup` 레이어를 인스턴스화하는 예입니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "9df56efc7f3b"
      },
      "outputs": [],
      "source": [
        "vocab = [\"a\", \"b\", \"c\", \"d\"]\n",
        "data = tf.constant([[\"a\", \"c\", \"d\"], [\"d\", \"z\", \"b\"]])\n",
        "layer = layers.StringLookup(vocabulary=vocab)\n",
        "vectorized_data = layer(data)\n",
        "print(vectorized_data)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "49cbfe135b00"
      },
      "source": [
        "## Preprocessing data before the model or inside the model\n",
        "\n",
        "There are two ways you could be using preprocessing layers:\n",
        "\n",
        "**Option 1:** Make them part of the model, like this:\n",
        "\n",
        "```python\n",
        "inputs = keras.Input(shape=input_shape)\n",
        "x = preprocessing_layer(inputs)\n",
        "outputs = rest_of_the_model(x)\n",
        "model = keras.Model(inputs, outputs)\n",
        "```\n",
        "\n",
        "With this option, preprocessing will happen on device, synchronously with the rest of the\n",
        "model execution, meaning that it will benefit from GPU acceleration.\n",
        "If you're training on GPU, this is the best option for the `Normalization` layer, and for\n",
        "all image preprocessing and data augmentation layers.\n",
        "\n",
        "**Option 2:** apply it to your `tf.data.Dataset`, so as to obtain a dataset that yields\n",
        "batches of preprocessed data, like this:\n",
        "\n",
        "```python\n",
        "dataset = dataset.map(lambda x, y: (preprocessing_layer(x), y))\n",
        "```\n",
        "\n",
        "With this option, your preprocessing will happen on CPU, asynchronously, and will be\n",
        "buffered before going into the model.\n",
        "In addition, if you call `dataset.prefetch(tf.data.AUTOTUNE)` on your dataset,\n",
        "the preprocessing will happen efficiently in parallel with training:\n",
        "\n",
        "```python\n",
        "dataset = dataset.map(lambda x, y: (preprocessing_layer(x), y))\n",
        "dataset = dataset.prefetch(tf.data.AUTOTUNE)\n",
        "model.fit(dataset, ...)\n",
        "```\n",
        "\n",
        "This is the best option for `TextVectorization`, and all structured data preprocessing\n",
        "layers. It can also be a good option if you're training on CPU\n",
        "and you use image preprocessing layers.\n",
        "\n",
        "**When running on TPU, you should always place preprocessing layers in the `tf.data` pipeline**\n",
        "(with the exception of `Normalization` and `Rescaling`, which run fine on TPU and are commonly\n",
        "used as the first layer is an image model)."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "32f6d2a104b7"
      },
      "source": [
        "## 추론 시 모델 내에서 전처리를 수행할 때의 이점\n",
        "\n",
        "옵션 2를 사용하더라도 나중에 전처리 레이어를 포함하는 추론 전용 엔드 투 엔드 모델을 내보낼 수 있습니다. 이 작업의 주요 이점은 **모델을 이식 가능하게 만들고** **[훈련/적용 편향](https://developers.google.com/machine-learning/guides/rules-of-ml#training-serving_skew)**을 줄이는 데 도움이 된다는 것입니다.\n",
        "\n",
        "모든 데이터 전처리가 모델의 일부인 경우, 다른 사람들은 각 특성이 어떻게 인코딩되고 정규화될 것으로 예상되는지 알 필요 없이 모델을 로드하고 사용할 수 있습니다. 추론 모델은 원시 이미지 또는 원시 구조적 데이터를 처리할 수 있으며, 모델 사용자가 예를 들어, 텍스트에 사용되는 토큰화 체계, 범주형 특성에 사용되는 인덱싱 체계, 이미지 픽셀 값이 `[-1, +1]` 또는 `[0, 1]`로 정규화되었는지 여부와 같은 세부 정보를 알 필요가 없습니다. 이는 모델을 TensorFlow.js와 같은 다른 런타임으로 내보내는 경우 특히 강력합니다. JavaScript에서 전처리 파이프라인을 다시 구현할 필요가 없습니다.\n",
        "\n",
        "처음에 전처리 레이어를 `tf.data` 파이프라인에 배치한 경우, 전처리를 패키징하는 추론 모델을 내보낼 수 있습니다. 전처리 레이어와 훈련 모델을 연결하는 새 모델을 인스턴스화하기만 하면 됩니다.\n",
        "\n",
        "```python\n",
        "inputs = keras.Input(shape=input_shape)\n",
        "x = preprocessing_layer(inputs)\n",
        "outputs = training_model(x)\n",
        "inference_model = keras.Model(inputs, outputs)\n",
        "```"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "b41b381d48d4"
      },
      "source": [
        "## Quick recipes\n",
        "\n",
        "### Image data augmentation\n",
        "\n",
        "Note that image data augmentation layers are only active during training (similarly to\n",
        "the `Dropout` layer)."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "a3793692e983"
      },
      "outputs": [],
      "source": [
        "from tensorflow import keras\n",
        "from tensorflow.keras import layers\n",
        "\n",
        "# Create a data augmentation stage with horizontal flipping, rotations, zooms\n",
        "data_augmentation = keras.Sequential(\n",
        "    [\n",
        "        layers.RandomFlip(\"horizontal\"),\n",
        "        layers.RandomRotation(0.1),\n",
        "        layers.RandomZoom(0.1),\n",
        "    ]\n",
        ")\n",
        "\n",
        "# Load some data\n",
        "(x_train, y_train), _ = keras.datasets.cifar10.load_data()\n",
        "input_shape = x_train.shape[1:]\n",
        "classes = 10\n",
        "\n",
        "# Create a tf.data pipeline of augmented images (and their labels)\n",
        "train_dataset = tf.data.Dataset.from_tensor_slices((x_train, y_train))\n",
        "train_dataset = train_dataset.batch(16).map(lambda x, y: (data_augmentation(x), y))\n",
        "\n",
        "\n",
        "# Create a model and train it on the augmented image data\n",
        "inputs = keras.Input(shape=input_shape)\n",
        "x = layers.Rescaling(1.0 / 255)(inputs)  # Rescale inputs\n",
        "outputs = keras.applications.ResNet50(  # Add the rest of the model\n",
        "    weights=None, input_shape=input_shape, classes=classes\n",
        ")(x)\n",
        "model = keras.Model(inputs, outputs)\n",
        "model.compile(optimizer=\"rmsprop\", loss=\"sparse_categorical_crossentropy\")\n",
        "model.fit(train_dataset, steps_per_epoch=5)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "51d369f0310f"
      },
      "source": [
        "[처음부터 이미지 분류하기](https://keras.io/examples/vision/image_classification_from_scratch/) 예제에서 유사한 설정이 동작하는 것을 볼 수 있습니다."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "a79a1c48b2b7"
      },
      "source": [
        "### 수치 특성 정규화하기"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "9cc2607a45c8"
      },
      "outputs": [],
      "source": [
        "# Load some data\n",
        "(x_train, y_train), _ = keras.datasets.cifar10.load_data()\n",
        "x_train = x_train.reshape((len(x_train), -1))\n",
        "input_shape = x_train.shape[1:]\n",
        "classes = 10\n",
        "\n",
        "# Create a Normalization layer and set its internal state using the training data\n",
        "normalizer = layers.Normalization()\n",
        "normalizer.adapt(x_train)\n",
        "\n",
        "# Create a model that include the normalization layer\n",
        "inputs = keras.Input(shape=input_shape)\n",
        "x = normalizer(inputs)\n",
        "outputs = layers.Dense(classes, activation=\"softmax\")(x)\n",
        "model = keras.Model(inputs, outputs)\n",
        "\n",
        "# Train the model\n",
        "model.compile(optimizer=\"adam\", loss=\"sparse_categorical_crossentropy\")\n",
        "model.fit(x_train, y_train)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "62685d477010"
      },
      "source": [
        "### 원-핫 인코딩을 통해 문자열 범주형 특성 인코딩하기"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "ae0d2b0405f1"
      },
      "outputs": [],
      "source": [
        "# Define some toy data\n",
        "data = tf.constant([[\"a\"], [\"b\"], [\"c\"], [\"b\"], [\"c\"], [\"a\"]])\n",
        "\n",
        "# Use StringLookup to build an index of the feature values and encode output.\n",
        "lookup = layers.StringLookup(output_mode=\"one_hot\")\n",
        "lookup.adapt(data)\n",
        "\n",
        "# Convert new test data (which includes unknown feature values)\n",
        "test_data = tf.constant([[\"a\"], [\"b\"], [\"c\"], [\"d\"], [\"e\"], [\"\"]])\n",
        "encoded_data = lookup(test_data)\n",
        "print(encoded_data)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "686aeda532f5"
      },
      "source": [
        "Note that, here, index 0 is reserved for out-of-vocabulary values\n",
        "(values that were not seen during `adapt()`).\n",
        "\n",
        "You can see the `StringLookup` in action in the\n",
        "[Structured data classification from scratch](https://keras.io/examples/structured_data/structured_data_classification_from_scratch/)\n",
        "example."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "dc8af3e290df"
      },
      "source": [
        "### 원-핫 인코딩을 통해 정수 범주형 특성 인코딩하기"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "75f3d6af4522"
      },
      "outputs": [],
      "source": [
        "# Define some toy data\n",
        "data = tf.constant([[10], [20], [20], [10], [30], [0]])\n",
        "\n",
        "# Use IntegerLookup to build an index of the feature values and encode output.\n",
        "lookup = layers.IntegerLookup(output_mode=\"one_hot\")\n",
        "lookup.adapt(data)\n",
        "\n",
        "# Convert new test data (which includes unknown feature values)\n",
        "test_data = tf.constant([[10], [10], [20], [50], [60], [0]])\n",
        "encoded_data = lookup(test_data)\n",
        "print(encoded_data)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "da5a6be487be"
      },
      "source": [
        "Note that index 0 is reserved for missing values (which you should specify as the value\n",
        "0), and index 1 is reserved for out-of-vocabulary values (values that were not seen\n",
        "during `adapt()`). You can configure this by using the `mask_token` and `oov_token`\n",
        "constructor arguments  of `IntegerLookup`.\n",
        "\n",
        "You can see the `IntegerLookup` in action in the example\n",
        "[structured data classification from scratch](https://keras.io/examples/structured_data/structured_data_classification_from_scratch/)."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "8fbfaa6ab3e2"
      },
      "source": [
        "### 정수 범주형 특성에 해싱 트릭 적용하기\n",
        "\n",
        "여러 다른 값(10e3 이상)을 사용할 수 있는 범주형 특성의 각 값이 데이터에서 몇 번만 나타나는 경우, 특성 값을 인덱싱하고 원-핫 인코딩하는 것은 비실용적이고 비효율적입니다. 대신, \"해싱 트릭\"을 적용하는 것이 좋습니다. 값을 고정된 크기의 벡터로 해싱합니다. 이는 특성 공간의 크기를 관리 가능한 상태로 유지하고 명시적 인덱싱의 필요성을 제거합니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "8f6c1f84c43c"
      },
      "outputs": [],
      "source": [
        "# Sample data: 10,000 random integers with values between 0 and 100,000\n",
        "data = np.random.randint(0, 100000, size=(10000, 1))\n",
        "\n",
        "# Use the Hashing layer to hash the values to the range [0, 64]\n",
        "hasher = layers.Hashing(num_bins=64, salt=1337)\n",
        "\n",
        "# Use the CategoryEncoding layer to multi-hot encode the hashed values\n",
        "encoder = layers.CategoryEncoding(num_tokens=64, output_mode=\"multi_hot\")\n",
        "encoded_data = encoder(hasher(data))\n",
        "print(encoded_data.shape)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "df69b434d327"
      },
      "source": [
        "### 일련의 토큰 인덱스로 텍스트 인코딩하기\n",
        "\n",
        "`Embedding` 레이어에 전달될 텍스트를 전처리하는 방법입니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "361b561bc88b"
      },
      "outputs": [],
      "source": [
        "# Define some text data to adapt the layer\n",
        "adapt_data = tf.constant(\n",
        "    [\n",
        "        \"The Brain is wider than the Sky\",\n",
        "        \"For put them side by side\",\n",
        "        \"The one the other will contain\",\n",
        "        \"With ease and You beside\",\n",
        "    ]\n",
        ")\n",
        "\n",
        "# Create a TextVectorization layer\n",
        "text_vectorizer = layers.TextVectorization(output_mode=\"int\")\n",
        "# Index the vocabulary via `adapt()`\n",
        "text_vectorizer.adapt(adapt_data)\n",
        "\n",
        "# Try out the layer\n",
        "print(\n",
        "    \"Encoded text:\\n\", text_vectorizer([\"The Brain is deeper than the sea\"]).numpy(),\n",
        ")\n",
        "\n",
        "# Create a simple model\n",
        "inputs = keras.Input(shape=(None,), dtype=\"int64\")\n",
        "x = layers.Embedding(input_dim=text_vectorizer.vocabulary_size(), output_dim=16)(inputs)\n",
        "x = layers.GRU(8)(x)\n",
        "outputs = layers.Dense(1)(x)\n",
        "model = keras.Model(inputs, outputs)\n",
        "\n",
        "# Create a labeled dataset (which includes unknown tokens)\n",
        "train_dataset = tf.data.Dataset.from_tensor_slices(\n",
        "    ([\"The Brain is deeper than the sea\", \"for if they are held Blue to Blue\"], [1, 0])\n",
        ")\n",
        "\n",
        "# Preprocess the string inputs, turning them into int sequences\n",
        "train_dataset = train_dataset.batch(2).map(lambda x, y: (text_vectorizer(x), y))\n",
        "# Train the model on the int sequences\n",
        "print(\"\\nTraining model...\")\n",
        "model.compile(optimizer=\"rmsprop\", loss=\"mse\")\n",
        "model.fit(train_dataset)\n",
        "\n",
        "# For inference, you can export a model that accepts strings as input\n",
        "inputs = keras.Input(shape=(1,), dtype=\"string\")\n",
        "x = text_vectorizer(inputs)\n",
        "outputs = model(x)\n",
        "end_to_end_model = keras.Model(inputs, outputs)\n",
        "\n",
        "# Call the end-to-end model on test data (which includes unknown tokens)\n",
        "print(\"\\nCalling end-to-end model on test string...\")\n",
        "test_data = tf.constant([\"The one the other will absorb\"])\n",
        "test_output = end_to_end_model(test_data)\n",
        "print(\"Model output:\", test_output)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "e725dbcae3e4"
      },
      "source": [
        "<a>처음부터 텍스트 분류</a> 예에서 `Embedding` 모드와 결합된 <code>TextVectorization</code> 레이어가 동작하는 것을 볼 수 있습니다.\n",
        "\n",
        "Note that when training such a model, for best performance, you should always use the `TextVectorization` layer as part of the input pipeline."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "28c2f2ff61fb"
      },
      "source": [
        "### 멀티-핫 인코딩을 사용하여 텍스트를 ngram의 밀집 행렬로 인코딩하기\n",
        "\n",
        "다음은 `Dense` 레이어로 전달될 텍스트를 전처리하는 방법입니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "7bae1c223cd8"
      },
      "outputs": [],
      "source": [
        "# Define some text data to adapt the layer\n",
        "adapt_data = tf.constant(\n",
        "    [\n",
        "        \"The Brain is wider than the Sky\",\n",
        "        \"For put them side by side\",\n",
        "        \"The one the other will contain\",\n",
        "        \"With ease and You beside\",\n",
        "    ]\n",
        ")\n",
        "# Instantiate TextVectorization with \"multi_hot\" output_mode\n",
        "# and ngrams=2 (index all bigrams)\n",
        "text_vectorizer = layers.TextVectorization(output_mode=\"multi_hot\", ngrams=2)\n",
        "# Index the bigrams via `adapt()`\n",
        "text_vectorizer.adapt(adapt_data)\n",
        "\n",
        "# Try out the layer\n",
        "print(\n",
        "    \"Encoded text:\\n\", text_vectorizer([\"The Brain is deeper than the sea\"]).numpy(),\n",
        ")\n",
        "\n",
        "# Create a simple model\n",
        "inputs = keras.Input(shape=(text_vectorizer.vocabulary_size(),))\n",
        "outputs = layers.Dense(1)(inputs)\n",
        "model = keras.Model(inputs, outputs)\n",
        "\n",
        "# Create a labeled dataset (which includes unknown tokens)\n",
        "train_dataset = tf.data.Dataset.from_tensor_slices(\n",
        "    ([\"The Brain is deeper than the sea\", \"for if they are held Blue to Blue\"], [1, 0])\n",
        ")\n",
        "\n",
        "# Preprocess the string inputs, turning them into int sequences\n",
        "train_dataset = train_dataset.batch(2).map(lambda x, y: (text_vectorizer(x), y))\n",
        "# Train the model on the int sequences\n",
        "print(\"\\nTraining model...\")\n",
        "model.compile(optimizer=\"rmsprop\", loss=\"mse\")\n",
        "model.fit(train_dataset)\n",
        "\n",
        "# For inference, you can export a model that accepts strings as input\n",
        "inputs = keras.Input(shape=(1,), dtype=\"string\")\n",
        "x = text_vectorizer(inputs)\n",
        "outputs = model(x)\n",
        "end_to_end_model = keras.Model(inputs, outputs)\n",
        "\n",
        "# Call the end-to-end model on test data (which includes unknown tokens)\n",
        "print(\"\\nCalling end-to-end model on test string...\")\n",
        "test_data = tf.constant([\"The one the other will absorb\"])\n",
        "test_output = end_to_end_model(test_data)\n",
        "print(\"Model output:\", test_output)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "336a4d3426ed"
      },
      "source": [
        "### TF-IDF 가중치를 사용하여 텍스트를 ngram의 밀집 행렬로 인코딩하기\n",
        "\n",
        "다음은 `Dense` 레이어로 전달하기 전에 텍스트를 전처리하는 또 다른 방법입니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "5b6c0fec928e"
      },
      "outputs": [],
      "source": [
        "# Define some text data to adapt the layer\n",
        "adapt_data = tf.constant(\n",
        "    [\n",
        "        \"The Brain is wider than the Sky\",\n",
        "        \"For put them side by side\",\n",
        "        \"The one the other will contain\",\n",
        "        \"With ease and You beside\",\n",
        "    ]\n",
        ")\n",
        "# Instantiate TextVectorization with \"tf-idf\" output_mode\n",
        "# (multi-hot with TF-IDF weighting) and ngrams=2 (index all bigrams)\n",
        "text_vectorizer = layers.TextVectorization(output_mode=\"tf-idf\", ngrams=2)\n",
        "# Index the bigrams and learn the TF-IDF weights via `adapt()`\n",
        "\n",
        "with tf.device(\"CPU\"):\n",
        "    # A bug that prevents this from running on GPU for now.\n",
        "    text_vectorizer.adapt(adapt_data)\n",
        "\n",
        "# Try out the layer\n",
        "print(\n",
        "    \"Encoded text:\\n\", text_vectorizer([\"The Brain is deeper than the sea\"]).numpy(),\n",
        ")\n",
        "\n",
        "# Create a simple model\n",
        "inputs = keras.Input(shape=(text_vectorizer.vocabulary_size(),))\n",
        "outputs = layers.Dense(1)(inputs)\n",
        "model = keras.Model(inputs, outputs)\n",
        "\n",
        "# Create a labeled dataset (which includes unknown tokens)\n",
        "train_dataset = tf.data.Dataset.from_tensor_slices(\n",
        "    ([\"The Brain is deeper than the sea\", \"for if they are held Blue to Blue\"], [1, 0])\n",
        ")\n",
        "\n",
        "# Preprocess the string inputs, turning them into int sequences\n",
        "train_dataset = train_dataset.batch(2).map(lambda x, y: (text_vectorizer(x), y))\n",
        "# Train the model on the int sequences\n",
        "print(\"\\nTraining model...\")\n",
        "model.compile(optimizer=\"rmsprop\", loss=\"mse\")\n",
        "model.fit(train_dataset)\n",
        "\n",
        "# For inference, you can export a model that accepts strings as input\n",
        "inputs = keras.Input(shape=(1,), dtype=\"string\")\n",
        "x = text_vectorizer(inputs)\n",
        "outputs = model(x)\n",
        "end_to_end_model = keras.Model(inputs, outputs)\n",
        "\n",
        "# Call the end-to-end model on test data (which includes unknown tokens)\n",
        "print(\"\\nCalling end-to-end model on test string...\")\n",
        "test_data = tf.constant([\"The one the other will absorb\"])\n",
        "test_output = end_to_end_model(test_data)\n",
        "print(\"Model output:\", test_output)\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "143ce01c5558"
      },
      "source": [
        "## Important gotchas\n",
        "\n",
        "### Working with lookup layers with very large vocabularies\n",
        "\n",
        "You may find yourself working with a very large vocabulary in a `TextVectorization`, a `StringLookup` layer,\n",
        "or an `IntegerLookup` layer. Typically, a vocabulary larger than 500MB would be considered \"very large\".\n",
        "\n",
        "In such case, for best performance, you should avoid using `adapt()`.\n",
        "Instead, pre-compute your vocabulary in advance\n",
        "(you could use Apache Beam or TF Transform for this)\n",
        "and store it in a file. Then load the vocabulary into the layer at construction\n",
        "time by passing the filepath as the `vocabulary` argument.\n",
        "\n",
        "\n",
        "### Using lookup layers on a TPU pod or with `ParameterServerStrategy`.\n",
        "\n",
        "There is an outstanding issue that causes performance to degrade when using\n",
        "a `TextVectorization`, `StringLookup`, or `IntegerLookup` layer while\n",
        "training on a TPU pod or on multiple machines via `ParameterServerStrategy`.\n",
        "This is slated to be fixed in TensorFlow 2.7."
      ]
    }
  ],
  "metadata": {
    "colab": {
      "collapsed_sections": [],
      "name": "preprocessing_layers.ipynb",
      "toc_visible": true<commit_msg>Translate preprocessing_layers.ipynb via GitLocalize<commit_after>{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "b518b04cbfe0"
      },
      "source": [
        "##### Copyright 2020 The TensorFlow Authors."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "cellView": "form",
        "id": "906e07f6e562"
      },
      "outputs": [],
      "source": [
        "#@title Licensed under the Apache License, Version 2.0 (the \"License\");\n",
        "# you may not use this file except in compliance with the License.\n",
        "# You may obtain a copy of the License at\n",
        "#\n",
        "# https://www.apache.org/licenses/LICENSE-2.0\n",
        "#\n",
        "# Unless required by applicable law or agreed to in writing, software\n",
        "# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
        "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
        "# See the License for the specific language governing permissions and\n",
        "# limitations under the License."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "6e083398b477"
      },
      "source": [
        "# 전처리 레이어 처리"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "64010bd23c2e"
      },
      "source": [
        "<table class=\"tfo-notebook-buttons\" align=\"left\">\n",
        "  <td><a target=\"_blank\" href=\"https://www.tensorflow.org/guide/keras/preprocessing_layers\"><img src=\"https://www.tensorflow.org/images/tf_logo_32px.png\">TensorFlow.org에서 보기</a></td>\n",
        "  <td><a target=\"_blank\" href=\"https://colab.research.google.com/github/tensorflow/docs-l10n/blob/master/site/ko/guide/keras/preprocessing_layers.ipynb\"><img src=\"https://www.tensorflow.org/images/colab_logo_32px.png\">Google Colab에서 실행</a></td>\n",
        "  <td><a target=\"_blank\" href=\"https://github.com/tensorflow/docs-l10n/blob/master/site/ko/guide/keras/preprocessing_layers.ipynb\">     <img src=\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\">    GitHub에서 소스 보기</a></td>\n",
        "  <td><a href=\"https://storage.googleapis.com/tensorflow_docs/docs-l10n/site/ko/guide/keras/preprocessing_layers.ipynb\"><img src=\"https://www.tensorflow.org/images/download_logo_32px.png\">노트북 다운로드</a></td>\n",
        "</table>"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "b1d403f04693"
      },
      "source": [
        "## Keras 전처리\n",
        "\n",
        "개발자는 Keras 전처리 레이어 API를 사용하여 Keras 네이티브 입력 처리 파이프라인을 구축할 수 있습니다. 이러한 입력 처리 파이프라인은 Keras가 아닌 워크플로에서 독립적인 전처리 코드로 사용할 수 있고, Keras 모델과 직접 결합하고, Keras SavedModel의 일부로 내보낼 수 있습니다.\n",
        "\n",
        "Keras 전처리 레이어를 사용하여 진정한 엔드 투 엔드 모델을 구축하고 내보낼 수 있으며, 이러한 모델에는 원시 이미지 또는 구조화된 원시 데이터를 입력으로 받아들이는 모델, 기능 정규화 또는 기능 값 인덱싱을 자체적으로 처리하는 모델이 있습니다."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "313360fa9024"
      },
      "source": [
        "## 사용할 수 있는 전처리\n",
        "\n",
        "### 텍스트 전처리\n",
        "\n",
        "- `tf.keras.layers.TextVectorization`: 원시 문자열을 `Embedding` 레이어 또는 `Dense` 레이어에서 읽을 수 있는 인코딩 표현으로 바꿉니다.\n",
        "\n",
        "### 숫자 기능 전처리\n",
        "\n",
        "- `tf.keras.layers.Normalization`: 입력 기능의 기능별 정규화를 수행합니다.\n",
        "- `tf.keras.layers.Discretization`: 연속 숫자 기능을 정수 범주형 기능으로 바꿉니다.\n",
        "\n",
        "### 범주형 기능 전처리\n",
        "\n",
        "- `tf.keras.layers.CategoryEncoding`: 정수 범주형 기능을 원-핫(one-hot), 멀티-핫(multi-hot) 또는 카운트 밀집 표현(count dense representations)으로 바꿉니다.\n",
        "- `tf.keras.layers.Hashing`: \"해싱 트릭(hashing trick)\"이라 불리우는 범주형 기능 해싱을 수행합니다.\n",
        "- `tf.keras.layers.StringLookup`: 문자열 범주를 `Embedding` 레이어 또는 `Dense` 레이어에서 읽을 수 있는 인코딩 표현으로 바꿉니다.\n",
        "- `tf.keras.layers.IntegerLookup`: 정수 범주를 `Embedding` 레이어 또는 `Dense` 레이어에서 읽을 수 있는 인코딩 표현으로 바꿉니다.\n",
        "\n",
        "### 이미지 전처리\n",
        "\n",
        "이미지 모델 입력을 표준화하기 위한 레이어입니다.\n",
        "\n",
        "- `tf.keras.layers.Resizing`: 이미지 배치의 크기를 대상 크기로 조정합니다.\n",
        "- `tf.keras.layers.Rescaling`: 이미지 배치의 값을 재조정하고 오프셋합니다(예: `[0, 255]` 범위의 입력에서 `[0, 1]` 범위의 입력으로 이동).\n",
        "- `tf.keras.layers.CenterCrop`: 이미지 배치의 가운데 크롭을 반환합니다.\n",
        "\n",
        "### 이미지 데이터 증강\n",
        "\n",
        "이 레이어는 이미지 배치에 무작위 증강 변환을 적용합니다. 이러한 작업은 훈련 중에만 활성화됩니다.\n",
        "\n",
        "- `tf.keras.layers.RandomCrop`\n",
        "- `tf.keras.layers.RandomFlip`\n",
        "- `tf.keras.layers.RandomTranslation`\n",
        "- `tf.keras.layers.RandomRotation`\n",
        "- `tf.keras.layers.RandomZoom`\n",
        "- `tf.keras.layers.RandomHeight`\n",
        "- `tf.keras.layers.RandomWidth`\n",
        "- `tf.keras.layers.RandomContrast`"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "c923e41fb1b4"
      },
      "source": [
        "## `adapt()` 메서드\n",
        "\n",
        "일부 전처리 레이어에는 훈련 데이터의 샘플을 기반으로 계산할 수 있는 내부 상태가 있습니다. 상태 저장 전처리 레이어 목록은 다음과 같습니다.\n",
        "\n",
        "- `TextVectorization`: 문자열 토큰과 정수 인덱스 간의 매핑을 유지합니다.\n",
        "- `StringLookup` 및 `IntegerLookup`: 입력 값과 정수 인덱스 간의 매핑을 유지합니다.\n",
        "- `Normalization`: 기능의 평균과 표준편차를 유지합니다.\n",
        "- `Discretization`: 값 버킷 경계에 대한 정보를 유지합니다.\n",
        "\n",
        "결정적으로 이러한 레이어는 **훈련할 수 없습니다**. 이들의 상태는 훈련 중에 설정되지 않습니다. 미리 계산된 상수로부터 초기화하거나 데이터에 \"적용\"하는 방식을 사용하여 **훈련하기 전**에 설정해야 합니다.\n",
        "\n",
        "다음과 같이 `adapt()` 메서드를 통해 학습 데이터에 노출하여 전처리 레이어의 상태를 설정할 수 있습니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "4cac6bd80812"
      },
      "outputs": [],
      "source": [
        "import numpy as np\n",
        "import tensorflow as tf\n",
        "from tensorflow.keras import layers\n",
        "\n",
        "data = np.array([[0.1, 0.2, 0.3], [0.8, 0.9, 1.0], [1.5, 1.6, 1.7],])\n",
        "layer = layers.Normalization()\n",
        "layer.adapt(data)\n",
        "normalized_data = layer(data)\n",
        "\n",
        "print(\"Features mean: %.2f\" % (normalized_data.numpy().mean()))\n",
        "print(\"Features std: %.2f\" % (normalized_data.numpy().std()))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "d43b8246b8a3"
      },
      "source": [
        "`adapt()` 메소드는 Numpy 배열 또는 `tf.data.Dataset` 객체를 사용합니다. `StringLookup` 및 `TextVectorization` 의 경우, 문자열의 목록을 전달할 수도 있습니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "48d95713348a"
      },
      "outputs": [],
      "source": [
        "data = [\n",
        "    \"ξεῖν᾽, ἦ τοι μὲν ὄνειροι ἀμήχανοι ἀκριτόμυθοι\",\n",
        "    \"γίγνοντ᾽, οὐδέ τι πάντα τελείεται ἀνθρώποισι.\",\n",
        "    \"δοιαὶ γάρ τε πύλαι ἀμενηνῶν εἰσὶν ὀνείρων:\",\n",
        "    \"αἱ μὲν γὰρ κεράεσσι τετεύχαται, αἱ δ᾽ ἐλέφαντι:\",\n",
        "    \"τῶν οἳ μέν κ᾽ ἔλθωσι διὰ πριστοῦ ἐλέφαντος,\",\n",
        "    \"οἵ ῥ᾽ ἐλεφαίρονται, ἔπε᾽ ἀκράαντα φέροντες:\",\n",
        "    \"οἱ δὲ διὰ ξεστῶν κεράων ἔλθωσι θύραζε,\",\n",
        "    \"οἵ ῥ᾽ ἔτυμα κραίνουσι, βροτῶν ὅτε κέν τις ἴδηται.\",\n",
        "]\n",
        "layer = layers.TextVectorization()\n",
        "layer.adapt(data)\n",
        "vectorized_text = layer(data)\n",
        "print(vectorized_text)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "7619914dfb40"
      },
      "source": [
        "또한, 적응 가능한 레이어는 항상 생성자 인수 또는 가중치 할당을 통해 상태를 직접 설정하는 옵션을 제공합니다. 의도한 상태 값이 레이어 생성 시 알려지거나 `adapt()` 호출 외부에서 계산되는 경우, 레이어의 내부 계산에 의존하지 않고 설정할 수 있습니다. 예를 들어, `TextVectorization` , `StringLookup` 또는 `IntegerLookup` 레이어에 대한 외부 어휘 파일이 이미 있는 경우, 레이어의 생성자 인수에 있는 어휘 파일에 대한 경로를 전달하여 조회 테이블에 직접 로드할 수 있습니다.\n",
        "\n",
        "다음은 사전 계산된 어휘로 `StringLookup` 레이어를 인스턴스화하는 예입니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "9df56efc7f3b"
      },
      "outputs": [],
      "source": [
        "vocab = [\"a\", \"b\", \"c\", \"d\"]\n",
        "data = tf.constant([[\"a\", \"c\", \"d\"], [\"d\", \"z\", \"b\"]])\n",
        "layer = layers.StringLookup(vocabulary=vocab)\n",
        "vectorized_data = layer(data)\n",
        "print(vectorized_data)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "49cbfe135b00"
      },
      "source": [
        "## 모델 이전 또는 모델 내부에서의 데이터 전처리\n",
        "\n",
        "전처리 레이어를 사용할 수 있는 두 가지 방법이 있습니다.\n",
        "\n",
        "**옵션 1:** 다음과 같이 모델의 일부로 만듭니다.\n",
        "\n",
        "```python\n",
        "inputs = keras.Input(shape=input_shape)\n",
        "x = preprocessing_layer(inputs)\n",
        "outputs = rest_of_the_model(x)\n",
        "model = keras.Model(inputs, outputs)\n",
        "```\n",
        "\n",
        "이 옵션을 사용하면 나머지 모델 실행과 동시에 장치에서 전처리가 발생하므로 GPU 가속의 이점을 얻을 수 있습니다. GPU로 훈련을 진행하는 경우 `Normalization` 레이어와 모든 이미지 전처리 및 데이터 강화 레이어에 이 옵션이 가장 적합합니다.\n",
        "\n",
        "**옵션 2:** `tf.data.Dataset`에 이 옵션을 적용하면 다음과 같이 전처리된 데이터 배치를 생성하는 데이터세트를 얻습니다.\n",
        "\n",
        "```python\n",
        "dataset = dataset.map(lambda x, y: (preprocessing_layer(x), y))\n",
        "```\n",
        "\n",
        "이 옵션을 사용하면 전처리가 CPU에서 비동기적으로 발생하고 모델에 들어가기 전에 전처리가 버퍼링됩니다. 추가적으로 데이터세트에서 `dataset.prefetch(tf.data.AUTOTUNE)`를 호출하면 사전처리가 훈련과 병행하게 효율적으로 이루어집니다.\n",
        "\n",
        "```python\n",
        "dataset = dataset.map(lambda x, y: (preprocessing_layer(x), y))\n",
        "dataset = dataset.prefetch(tf.data.AUTOTUNE)\n",
        "model.fit(dataset, ...)\n",
        "```\n",
        "\n",
        "이는 `TextVectorization` 및 모든 구조화된 데이터 전처리 레이어에 가장 적합한 옵션입니다. CPU로 훈련하고 이미지 전처리 레이어를 사용하는 경우에도 좋은 옵션이 될 수 있습니다.\n",
        "\n",
        "**TPU에서 실행할 경우 항상 `tf.data` 파이프라인**에 전처리 레이어를 배치해야 합니다(TPU에서 잘 실행되고 일반적으로 첫 번째 레이어를 이미지 모델로 사용하는 `Normalization` 및 `Rescaling`은 제외)."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "32f6d2a104b7"
      },
      "source": [
        "## 추론 시 모델 내에서 전처리를 수행할 때의 이점\n",
        "\n",
        "옵션 2를 사용하더라도 나중에 전처리 레이어를 포함하는 추론 전용 엔드 투 엔드 모델을 내보낼 수 있습니다. 이 작업의 주요 이점은 **모델을 이식 가능하게 만들고** **[훈련/적용 편향](https://developers.google.com/machine-learning/guides/rules-of-ml#training-serving_skew)**을 줄이는 데 도움이 된다는 것입니다.\n",
        "\n",
        "모든 데이터 전처리가 모델의 일부인 경우, 다른 사람들은 각 특성이 어떻게 인코딩되고 정규화될 것으로 예상되는지 알 필요 없이 모델을 로드하고 사용할 수 있습니다. 추론 모델은 원시 이미지 또는 원시 구조적 데이터를 처리할 수 있으며, 모델 사용자가 예를 들어, 텍스트에 사용되는 토큰화 체계, 범주형 특성에 사용되는 인덱싱 체계, 이미지 픽셀 값이 `[-1, +1]` 또는 `[0, 1]`로 정규화되었는지 여부와 같은 세부 정보를 알 필요가 없습니다. 이는 모델을 TensorFlow.js와 같은 다른 런타임으로 내보내는 경우 특히 강력합니다. JavaScript에서 전처리 파이프라인을 다시 구현할 필요가 없습니다.\n",
        "\n",
        "처음에 전처리 레이어를 `tf.data` 파이프라인에 배치한 경우, 전처리를 패키징하는 추론 모델을 내보낼 수 있습니다. 전처리 레이어와 훈련 모델을 연결하는 새 모델을 인스턴스화하기만 하면 됩니다.\n",
        "\n",
        "```python\n",
        "inputs = keras.Input(shape=input_shape)\n",
        "x = preprocessing_layer(inputs)\n",
        "outputs = training_model(x)\n",
        "inference_model = keras.Model(inputs, outputs)\n",
        "```"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "b41b381d48d4"
      },
      "source": [
        "## 퀵 레시피\n",
        "\n",
        "### 이미지 데이터 증강\n",
        "\n",
        "이미지 데이터 증강 레이어는 훈련 중에만 활성화됩니다(`Dropout` 레이어와 유사)."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "a3793692e983"
      },
      "outputs": [],
      "source": [
        "from tensorflow import keras\n",
        "from tensorflow.keras import layers\n",
        "\n",
        "# Create a data augmentation stage with horizontal flipping, rotations, zooms\n",
        "data_augmentation = keras.Sequential(\n",
        "    [\n",
        "        layers.RandomFlip(\"horizontal\"),\n",
        "        layers.RandomRotation(0.1),\n",
        "        layers.RandomZoom(0.1),\n",
        "    ]\n",
        ")\n",
        "\n",
        "# Load some data\n",
        "(x_train, y_train), _ = keras.datasets.cifar10.load_data()\n",
        "input_shape = x_train.shape[1:]\n",
        "classes = 10\n",
        "\n",
        "# Create a tf.data pipeline of augmented images (and their labels)\n",
        "train_dataset = tf.data.Dataset.from_tensor_slices((x_train, y_train))\n",
        "train_dataset = train_dataset.batch(16).map(lambda x, y: (data_augmentation(x), y))\n",
        "\n",
        "\n",
        "# Create a model and train it on the augmented image data\n",
        "inputs = keras.Input(shape=input_shape)\n",
        "x = layers.Rescaling(1.0 / 255)(inputs)  # Rescale inputs\n",
        "outputs = keras.applications.ResNet50(  # Add the rest of the model\n",
        "    weights=None, input_shape=input_shape, classes=classes\n",
        ")(x)\n",
        "model = keras.Model(inputs, outputs)\n",
        "model.compile(optimizer=\"rmsprop\", loss=\"sparse_categorical_crossentropy\")\n",
        "model.fit(train_dataset, steps_per_epoch=5)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "51d369f0310f"
      },
      "source": [
        "[처음부터 이미지 분류하기](https://keras.io/examples/vision/image_classification_from_scratch/) 예제에서 유사한 설정이 동작하는 것을 볼 수 있습니다."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "a79a1c48b2b7"
      },
      "source": [
        "### 수치 특성 정규화하기"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "9cc2607a45c8"
      },
      "outputs": [],
      "source": [
        "# Load some data\n",
        "(x_train, y_train), _ = keras.datasets.cifar10.load_data()\n",
        "x_train = x_train.reshape((len(x_train), -1))\n",
        "input_shape = x_train.shape[1:]\n",
        "classes = 10\n",
        "\n",
        "# Create a Normalization layer and set its internal state using the training data\n",
        "normalizer = layers.Normalization()\n",
        "normalizer.adapt(x_train)\n",
        "\n",
        "# Create a model that include the normalization layer\n",
        "inputs = keras.Input(shape=input_shape)\n",
        "x = normalizer(inputs)\n",
        "outputs = layers.Dense(classes, activation=\"softmax\")(x)\n",
        "model = keras.Model(inputs, outputs)\n",
        "\n",
        "# Train the model\n",
        "model.compile(optimizer=\"adam\", loss=\"sparse_categorical_crossentropy\")\n",
        "model.fit(x_train, y_train)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "62685d477010"
      },
      "source": [
        "### 원-핫 인코딩을 통해 문자열 범주형 특성 인코딩하기"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "ae0d2b0405f1"
      },
      "outputs": [],
      "source": [
        "# Define some toy data\n",
        "data = tf.constant([[\"a\"], [\"b\"], [\"c\"], [\"b\"], [\"c\"], [\"a\"]])\n",
        "\n",
        "# Use StringLookup to build an index of the feature values and encode output.\n",
        "lookup = layers.StringLookup(output_mode=\"one_hot\")\n",
        "lookup.adapt(data)\n",
        "\n",
        "# Convert new test data (which includes unknown feature values)\n",
        "test_data = tf.constant([[\"a\"], [\"b\"], [\"c\"], [\"d\"], [\"e\"], [\"\"]])\n",
        "encoded_data = lookup(test_data)\n",
        "print(encoded_data)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "686aeda532f5"
      },
      "source": [
        "여기에서 인덱스 0은 어휘 이외의 값(`adapt()` 동안 표시되지 않은 값)을 위해 예약되어 있습니다\n",
        "\n",
        "<a>구조화된 데이터 분류 처음부터 하기</a> 예시에서 작동 중인 `StringLookup`을 볼 수 있습니다."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "dc8af3e290df"
      },
      "source": [
        "### 원-핫 인코딩을 통해 정수 범주형 특성 인코딩하기"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "75f3d6af4522"
      },
      "outputs": [],
      "source": [
        "# Define some toy data\n",
        "data = tf.constant([[10], [20], [20], [10], [30], [0]])\n",
        "\n",
        "# Use IntegerLookup to build an index of the feature values and encode output.\n",
        "lookup = layers.IntegerLookup(output_mode=\"one_hot\")\n",
        "lookup.adapt(data)\n",
        "\n",
        "# Convert new test data (which includes unknown feature values)\n",
        "test_data = tf.constant([[10], [10], [20], [50], [60], [0]])\n",
        "encoded_data = lookup(test_data)\n",
        "print(encoded_data)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "da5a6be487be"
      },
      "source": [
        "인덱스 0은 누락된 값(값을 0으로 지정해야 함)용으로 예약되어 있고 인덱스 1은 어휘 외 의값(`adapt()` 동안 표시되지 않은 값)용으로 예약되어 있습니다. `IntegerLookup`의 `mask_token` 및 `oov_token` 생성자 인수를 사용하여 이를 구성할 수 있습니다.\n",
        "\n",
        "[구조화된 데이터 분류 처음부터 하기](https://keras.io/examples/structured_data/structured_data_classification_from_scratch/) 예시에서 작동 중인 `IntegerLookup`을 볼 수 있습니다."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "8fbfaa6ab3e2"
      },
      "source": [
        "### 정수 범주형 특성에 해싱 트릭 적용하기\n",
        "\n",
        "여러 다른 값(10e3 이상)을 사용할 수 있는 범주형 특성의 각 값이 데이터에서 몇 번만 나타나는 경우, 특성 값을 인덱싱하고 원-핫 인코딩하는 것은 비실용적이고 비효율적입니다. 대신, \"해싱 트릭\"을 적용하는 것이 좋습니다. 값을 고정된 크기의 벡터로 해싱합니다. 이는 특성 공간의 크기를 관리 가능한 상태로 유지하고 명시적 인덱싱의 필요성을 제거합니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "8f6c1f84c43c"
      },
      "outputs": [],
      "source": [
        "# Sample data: 10,000 random integers with values between 0 and 100,000\n",
        "data = np.random.randint(0, 100000, size=(10000, 1))\n",
        "\n",
        "# Use the Hashing layer to hash the values to the range [0, 64]\n",
        "hasher = layers.Hashing(num_bins=64, salt=1337)\n",
        "\n",
        "# Use the CategoryEncoding layer to multi-hot encode the hashed values\n",
        "encoder = layers.CategoryEncoding(num_tokens=64, output_mode=\"multi_hot\")\n",
        "encoded_data = encoder(hasher(data))\n",
        "print(encoded_data.shape)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "df69b434d327"
      },
      "source": [
        "### 일련의 토큰 인덱스로 텍스트 인코딩하기\n",
        "\n",
        "`Embedding` 레이어에 전달될 텍스트를 전처리하는 방법입니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "361b561bc88b"
      },
      "outputs": [],
      "source": [
        "# Define some text data to adapt the layer\n",
        "adapt_data = tf.constant(\n",
        "    [\n",
        "        \"The Brain is wider than the Sky\",\n",
        "        \"For put them side by side\",\n",
        "        \"The one the other will contain\",\n",
        "        \"With ease and You beside\",\n",
        "    ]\n",
        ")\n",
        "\n",
        "# Create a TextVectorization layer\n",
        "text_vectorizer = layers.TextVectorization(output_mode=\"int\")\n",
        "# Index the vocabulary via `adapt()`\n",
        "text_vectorizer.adapt(adapt_data)\n",
        "\n",
        "# Try out the layer\n",
        "print(\n",
        "    \"Encoded text:\\n\", text_vectorizer([\"The Brain is deeper than the sea\"]).numpy(),\n",
        ")\n",
        "\n",
        "# Create a simple model\n",
        "inputs = keras.Input(shape=(None,), dtype=\"int64\")\n",
        "x = layers.Embedding(input_dim=text_vectorizer.vocabulary_size(), output_dim=16)(inputs)\n",
        "x = layers.GRU(8)(x)\n",
        "outputs = layers.Dense(1)(x)\n",
        "model = keras.Model(inputs, outputs)\n",
        "\n",
        "# Create a labeled dataset (which includes unknown tokens)\n",
        "train_dataset = tf.data.Dataset.from_tensor_slices(\n",
        "    ([\"The Brain is deeper than the sea\", \"for if they are held Blue to Blue\"], [1, 0])\n",
        ")\n",
        "\n",
        "# Preprocess the string inputs, turning them into int sequences\n",
        "train_dataset = train_dataset.batch(2).map(lambda x, y: (text_vectorizer(x), y))\n",
        "# Train the model on the int sequences\n",
        "print(\"\\nTraining model...\")\n",
        "model.compile(optimizer=\"rmsprop\", loss=\"mse\")\n",
        "model.fit(train_dataset)\n",
        "\n",
        "# For inference, you can export a model that accepts strings as input\n",
        "inputs = keras.Input(shape=(1,), dtype=\"string\")\n",
        "x = text_vectorizer(inputs)\n",
        "outputs = model(x)\n",
        "end_to_end_model = keras.Model(inputs, outputs)\n",
        "\n",
        "# Call the end-to-end model on test data (which includes unknown tokens)\n",
        "print(\"\\nCalling end-to-end model on test string...\")\n",
        "test_data = tf.constant([\"The one the other will absorb\"])\n",
        "test_output = end_to_end_model(test_data)\n",
        "print(\"Model output:\", test_output)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "e725dbcae3e4"
      },
      "source": [
        "<a>처음부터 텍스트 분류</a> 예에서 `Embedding` 모드와 결합된 <code>TextVectorization</code> 레이어가 동작하는 것을 볼 수 있습니다.\n",
        "\n",
        "이러한 모델을 훈련할 때에는 최상의 성능을 위해 `TextVectorization` 레이어를 입력 파이프라인의 일부로 사용해야 합니다."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "28c2f2ff61fb"
      },
      "source": [
        "### 멀티-핫 인코딩을 사용하여 텍스트를 ngram의 밀집 행렬로 인코딩하기\n",
        "\n",
        "다음은 `Dense` 레이어로 전달될 텍스트를 전처리하는 방법입니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "7bae1c223cd8"
      },
      "outputs": [],
      "source": [
        "# Define some text data to adapt the layer\n",
        "adapt_data = tf.constant(\n",
        "    [\n",
        "        \"The Brain is wider than the Sky\",\n",
        "        \"For put them side by side\",\n",
        "        \"The one the other will contain\",\n",
        "        \"With ease and You beside\",\n",
        "    ]\n",
        ")\n",
        "# Instantiate TextVectorization with \"multi_hot\" output_mode\n",
        "# and ngrams=2 (index all bigrams)\n",
        "text_vectorizer = layers.TextVectorization(output_mode=\"multi_hot\", ngrams=2)\n",
        "# Index the bigrams via `adapt()`\n",
        "text_vectorizer.adapt(adapt_data)\n",
        "\n",
        "# Try out the layer\n",
        "print(\n",
        "    \"Encoded text:\\n\", text_vectorizer([\"The Brain is deeper than the sea\"]).numpy(),\n",
        ")\n",
        "\n",
        "# Create a simple model\n",
        "inputs = keras.Input(shape=(text_vectorizer.vocabulary_size(),))\n",
        "outputs = layers.Dense(1)(inputs)\n",
        "model = keras.Model(inputs, outputs)\n",
        "\n",
        "# Create a labeled dataset (which includes unknown tokens)\n",
        "train_dataset = tf.data.Dataset.from_tensor_slices(\n",
        "    ([\"The Brain is deeper than the sea\", \"for if they are held Blue to Blue\"], [1, 0])\n",
        ")\n",
        "\n",
        "# Preprocess the string inputs, turning them into int sequences\n",
        "train_dataset = train_dataset.batch(2).map(lambda x, y: (text_vectorizer(x), y))\n",
        "# Train the model on the int sequences\n",
        "print(\"\\nTraining model...\")\n",
        "model.compile(optimizer=\"rmsprop\", loss=\"mse\")\n",
        "model.fit(train_dataset)\n",
        "\n",
        "# For inference, you can export a model that accepts strings as input\n",
        "inputs = keras.Input(shape=(1,), dtype=\"string\")\n",
        "x = text_vectorizer(inputs)\n",
        "outputs = model(x)\n",
        "end_to_end_model = keras.Model(inputs, outputs)\n",
        "\n",
        "# Call the end-to-end model on test data (which includes unknown tokens)\n",
        "print(\"\\nCalling end-to-end model on test string...\")\n",
        "test_data = tf.constant([\"The one the other will absorb\"])\n",
        "test_output = end_to_end_model(test_data)\n",
        "print(\"Model output:\", test_output)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "336a4d3426ed"
      },
      "source": [
        "### TF-IDF 가중치를 사용하여 텍스트를 ngram의 밀집 행렬로 인코딩하기\n",
        "\n",
        "다음은 `Dense` 레이어로 전달하기 전에 텍스트를 전처리하는 또 다른 방법입니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "5b6c0fec928e"
      },
      "outputs": [],
      "source": [
        "# Define some text data to adapt the layer\n",
        "adapt_data = tf.constant(\n",
        "    [\n",
        "        \"The Brain is wider than the Sky\",\n",
        "        \"For put them side by side\",\n",
        "        \"The one the other will contain\",\n",
        "        \"With ease and You beside\",\n",
        "    ]\n",
        ")\n",
        "# Instantiate TextVectorization with \"tf-idf\" output_mode\n",
        "# (multi-hot with TF-IDF weighting) and ngrams=2 (index all bigrams)\n",
        "text_vectorizer = layers.TextVectorization(output_mode=\"tf-idf\", ngrams=2)\n",
        "# Index the bigrams and learn the TF-IDF weights via `adapt()`\n",
        "\n",
        "with tf.device(\"CPU\"):\n",
        "    # A bug that prevents this from running on GPU for now.\n",
        "    text_vectorizer.adapt(adapt_data)\n",
        "\n",
        "# Try out the layer\n",
        "print(\n",
        "    \"Encoded text:\\n\", text_vectorizer([\"The Brain is deeper than the sea\"]).numpy(),\n",
        ")\n",
        "\n",
        "# Create a simple model\n",
        "inputs = keras.Input(shape=(text_vectorizer.vocabulary_size(),))\n",
        "outputs = layers.Dense(1)(inputs)\n",
        "model = keras.Model(inputs, outputs)\n",
        "\n",
        "# Create a labeled dataset (which includes unknown tokens)\n",
        "train_dataset = tf.data.Dataset.from_tensor_slices(\n",
        "    ([\"The Brain is deeper than the sea\", \"for if they are held Blue to Blue\"], [1, 0])\n",
        ")\n",
        "\n",
        "# Preprocess the string inputs, turning them into int sequences\n",
        "train_dataset = train_dataset.batch(2).map(lambda x, y: (text_vectorizer(x), y))\n",
        "# Train the model on the int sequences\n",
        "print(\"\\nTraining model...\")\n",
        "model.compile(optimizer=\"rmsprop\", loss=\"mse\")\n",
        "model.fit(train_dataset)\n",
        "\n",
        "# For inference, you can export a model that accepts strings as input\n",
        "inputs = keras.Input(shape=(1,), dtype=\"string\")\n",
        "x = text_vectorizer(inputs)\n",
        "outputs = model(x)\n",
        "end_to_end_model = keras.Model(inputs, outputs)\n",
        "\n",
        "# Call the end-to-end model on test data (which includes unknown tokens)\n",
        "print(\"\\nCalling end-to-end model on test string...\")\n",
        "test_data = tf.constant([\"The one the other will absorb\"])\n",
        "test_output = end_to_end_model(test_data)\n",
        "print(\"Model output:\", test_output)\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "143ce01c5558"
      },
      "source": [
        "## 중요 정보\n",
        "\n",
        "### 매우 방대한 어휘를 보유한 룩업(lookup) 레이어로 작업 수행하기\n",
        "\n",
        "`TextVectorization`, `StringLookup` 레이어 또는 `IntegerLookup` 레이어에서 매우 방대한 어휘로 작업해야 하는 경우가 있을 수 있습니다. 일반적으로 500MB보다 큰 어휘는 \"매우 큰\" 것으로 간주합니다.\n",
        "\n",
        "이러한 경우 최상의 성능을 위해 `adapt()` 사용을 피해야 합니다. 대신 사전에 어휘를 미리 계산하고(이를 위해 Apache Beam 또는 TF Transform을 사용할 수 있음) 이를 파일에 저장합니다. 그런 다음 파일 경로를 `vocabulary` 인수로 전달하여 구성하는 시점에 어휘를 레이어에 로드합니다.\n",
        "\n",
        "### TPU 포드에서 또는 `ParameterServerStrategy`로 룩업(lookup) 레이어 사용하기\n",
        "\n",
        "TPU 포드 또는 여러 기기에서 `ParameterServerStrategy`를 사용하여 훈련을 진행하는 동안 `TextVectorization`, `StringLookup` 또는 `IntegerLookup`을 사용할 경우 성능을 저하시키는 미해결 문제가 있습니다. 이는 TensorFlow 2.7에서 수정될 예정입니다."
      ]
    }
  ],
  "metadata": {
    "colab": {
      "collapsed_sections": [],
      "name": "preprocessing_layers.ipynb",
      "toc_visible": true
{'fraction_non_alphanumeric': 0.17034532158384835, 'fraction_numerical': 0.01462577668204761, 'mean_word_length': 2.0448366808683303, 'pattern_counts': {'":': 342, '<': 57, '<?xml version=': 0, '>': 57, 'https://': 25, 'lorem ipsum': 0, 'www.': 12, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6029971', 'n_tokens_mistral': 24859, 'n_tokens_neox': 25938, 'n_words': 5247}