{ "cells": [ { "cell_type": "markdown", "metadata": { "id": "Tce3stUlHN0L" }, "source": [ "##### Copyright 2019 The TensorFlow Authors." ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "cellView": "form", "execution": { "iopub.execute_input": "2022-12-14T22:09:44.071277Z", "iopub.status.busy": "2022-12-14T22:09:44.070902Z", "iopub.status.idle": "2022-12-14T22:09:44.074742Z", "shell.execute_reply": "2022-12-14T22:09:44.074211Z" }, "id": "tuOe1ymfHZPu" }, "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": "qFdPvlXBOdUN" }, "source": [ "# tf.data API로 성능 향상하기" ] }, { "cell_type": "markdown", "metadata": { "id": "MfBg1C5NB3X0" }, "source": [ "\n", " \n", " \n", " \n", " \n", "
TensorFlow.org에서 보기 Google Colab에서 실행GitHub에서 소스 보기노트북 다운로드
" ] }, { "cell_type": "markdown", "metadata": { "id": "xHxb-dlhMIzW" }, "source": [ "## 개요\n", "\n", "GPU와 TPU는 하나의 학습 단계를 실행하는데 필요한 시간을 급격하게 줄일 수 있습니다. 최대 성능을 위해서는 현재 단계가 종료되기 전에 다음 스텝의 데이터를 운반하는 효율적인 입력 파이프라인이 필요합니다.`tf.data` API는 유연하고 효율적인 입력 파이프라인을 만드는데 도움이 됩니다. 이 문서는 다양한 모델과 가속기에서 고성능의 텐서플로 입력 파이프라인을 만드는 방법과 `tf.data` API의 특정을 설명합니다.\n", "\n", "진행하기 전에, `tf.data` API 사용법을 익히려면 \"[텐서플로 입력 파이프라인 빌드하기](./data.ipynb)\" 가이드를 읽으십시오." ] }, { "cell_type": "markdown", "metadata": { "id": "UhNtHfuxCGVy" }, "source": [ "## 참고 자료\n", "\n", "- [텐서플로 입력 파이프라인 빌드하기](./data.ipynb)\n", "- `tf.data.Dataset` API\n", "- [TF Profiler로 `tf.data` 성능 분석](./data_performance_analysis.md)" ] }, { "cell_type": "markdown", "metadata": { "id": "MUXex9ctTuDB" }, "source": [ "## 설정" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "execution": { "iopub.execute_input": "2022-12-14T22:09:44.078115Z", "iopub.status.busy": "2022-12-14T22:09:44.077668Z", "iopub.status.idle": "2022-12-14T22:09:46.001635Z", "shell.execute_reply": "2022-12-14T22:09:46.000937Z" }, "id": "IqR2PQG4ZaZ0" }, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "2022-12-14 22:09:45.018277: W tensorflow/compiler/xla/stream_executor/platform/default/dso_loader.cc:64] Could not load dynamic library 'libnvinfer.so.7'; dlerror: libnvinfer.so.7: cannot open shared object file: No such file or directory\n", "2022-12-14 22:09:45.018364: W tensorflow/compiler/xla/stream_executor/platform/default/dso_loader.cc:64] Could not load dynamic library 'libnvinfer_plugin.so.7'; dlerror: libnvinfer_plugin.so.7: cannot open shared object file: No such file or directory\n", "2022-12-14 22:09:45.018374: W tensorflow/compiler/tf2tensorrt/utils/py_utils.cc:38] TF-TRT Warning: Cannot dlopen some TensorRT libraries. If you would like to use Nvidia GPU with TensorRT, please make sure the missing libraries mentioned above are installed properly.\n" ] } ], "source": [ "import tensorflow as tf\n", "\n", "import time" ] }, { "cell_type": "markdown", "metadata": { "id": "QthTHCKF-jKD" }, "source": [ "전반적인 가이드에서는 데이터셋을 반복하고 성능을 측정합니다. 재현 가능한 성능 벤치마크를 만드는 것은 그것에 영향을 미치는 다른 요인들로 인해 어려울 수 있습니다. 그 요인들로는:\n", "\n", "- 현재 CPU 로드,\n", "- 네트워크 트래픽,\n", "- 캐시와 같은 복잡한 메커니즘 등이 있습니다.\n", "\n", "따라서 재현 가능한 벤치마크를 제공하기 위해 인공 예제를 빌드합니다." ] }, { "cell_type": "markdown", "metadata": { "id": "3bU5gsSI-jKF" }, "source": [ "### 데이터셋\n", "\n", "`tf.data.Dataset`에서 상속하여 `ArtificialDataset`이라 불리는 클래스를 정의합니다. 이 데이터셋은:\n", "\n", "- `num_samples`(기본값은 3)개의 샘플을 생성하기\n", "- 첫 번째 항목이 파일 열기를 시뮬레이션하기 전에 일정 시간 동안 휴면\n", "- 파일에서 데이터 읽기를 시뮬레이션하기 위해 각 항목을 생성하기 전에 일정 시간 동안 휴면" ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "execution": { "iopub.execute_input": "2022-12-14T22:09:46.006300Z", "iopub.status.busy": "2022-12-14T22:09:46.005493Z", "iopub.status.idle": "2022-12-14T22:09:46.010649Z", "shell.execute_reply": "2022-12-14T22:09:46.010048Z" }, "id": "zUQv4kCd-jKH" }, "outputs": [], "source": [ "class ArtificialDataset(tf.data.Dataset):\n", " def _generator(num_samples):\n", " # Opening the file\n", " time.sleep(0.03)\n", " \n", " for sample_idx in range(num_samples):\n", " # Reading data (line, record) from the file\n", " time.sleep(0.015)\n", " \n", " yield (sample_idx,)\n", " \n", " def __new__(cls, num_samples=3):\n", " return tf.data.Dataset.from_generator(\n", " cls._generator,\n", " output_signature = tf.TensorSpec(shape = (1,), dtype = tf.int64),\n", " args=(num_samples,)\n", " )" ] }, { "cell_type": "markdown", "metadata": { "id": "O9y1WjNv-jKL" }, "source": [ "이 데이터셋은 `tf.data.Dataset.range`와 유사하며 각 샘플의 시작과 사이에 일정한 지연시간을 추가합니다." ] }, { "cell_type": "markdown", "metadata": { "id": "FGK1Y4jn-jKM" }, "source": [ "### 훈련 루프\n", "\n", "데이터셋을 반복하는 데 걸리는 시간을 측정하는 더미 훈련 루프를 작성합니다. 훈련 시간이 시뮬레이션됩니다." ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "execution": { "iopub.execute_input": "2022-12-14T22:09:46.014328Z", "iopub.status.busy": "2022-12-14T22:09:46.013852Z", "iopub.status.idle": "2022-12-14T22:09:46.017470Z", "shell.execute_reply": "2022-12-14T22:09:46.016947Z" }, "id": "MIaM3u00-jKP" }, "outputs": [], "source": [ "def benchmark(dataset, num_epochs=2):\n", " start_time = time.perf_counter()\n", " for epoch_num in range(num_epochs):\n", " for sample in dataset:\n", " # Performing a training step\n", " time.sleep(0.01)\n", " print(\"Execution time:\", time.perf_counter() - start_time)" ] }, { "cell_type": "markdown", "metadata": { "id": "KK58SuXS-jKT" }, "source": [ "## 성능 최적화\n", "\n", "성능을 최적화하는 방법을 보여주기 위해 `ArtificialDataset`의 성능을 향상시킵니다." ] }, { "cell_type": "markdown", "metadata": { "id": "Xi8t26y7-jKV" }, "source": [ "### 추상적 접근\n", "\n", "트릭 없이 추상적 파이프라인으로 시작하여 데이터셋을 그대로 반복합니다." ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "execution": { "iopub.execute_input": "2022-12-14T22:09:46.020921Z", "iopub.status.busy": "2022-12-14T22:09:46.020434Z", "iopub.status.idle": "2022-12-14T22:09:49.674911Z", "shell.execute_reply": "2022-12-14T22:09:49.674106Z" }, "id": "_gP7J1y4-jKY" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Execution time: 0.23512932500034367\n" ] } ], "source": [ "benchmark(ArtificialDataset())" ] }, { "cell_type": "markdown", "metadata": { "id": "Lxeat5dH-jKf" }, "source": [ "실제로는 다음과 같이 실행 시간이 소비되었습니다:\n", "\n", "![Naive](https://www.tensorflow.org/guide/images/data_performance/naive.svg)\n", "\n", "이를 포함한 훈련 스텝을 수행하는 것을 볼 수 있습니다:\n", "\n", "- 아직 열지 않은 경우 파일 열기,\n", "- 파일에서 데이터 항목을 가져오기,\n", "- 훈련할 데이터 사용하기.\n", "\n", "그러나 여기와 같은 추상적 동기 구현에서는 파이프라인이 데이터를 가져 오는 동안 모델이 유휴 상태입니다. 반대로, 모델이 훈련하는 동안 입력 파이프라인이 유휴 상태입니다. 따라서 훈련 스텝 시간은 모두 열기, 읽기 및 훈련 시간의 합계입니다.\n", "\n", "다음 섹션에서는 이 입력 파이프라인을 구축하여 성능이 뛰어난 텐서플로 입력 파이프라인 설계를 위한 모범 사례를 보여줍니다." ] }, { "cell_type": "markdown", "metadata": { "id": "mfukBGNz-jKh" }, "source": [ "### 가져오기(Prefetching)\n", "\n", "Prefetching overlaps the preprocessing and model execution of a training step. While the model is executing training step `s`, the input pipeline is reading the data for step `s+1`. Doing so reduces the step time to the maximum (as opposed to the sum) of the training and the time it takes to extract the data.\n", "\n", "`tf.data` API는 `tf.data.Dataset.prefetch` 변환을 제공합니다. 이를 이용해 데이터가 생성되는 시간과 데이터가 소비되는 시간을 분리할 수 있습니다. 특히 변환에 백그라운드 스레드와 내부 버퍼를 사용하여 요청된 시간보다 앞서 입력 데이터세트에서 요소를 미리 가져옵니다. 미리 가져올 요소의 수는 단일 학습 단계에서 사용되는 배치 수와 같거나 더 많아야 합니다. 이 값을 수동으로 조정하거나, `tf.data` 런타임이 런타임에 값을 동적으로 조정하도록 `tf.data.AUTOTUNE`로 설정할 수 있습니다.\n", "\n", "프리페치 변환은 \"생산자\"의 작업과 \"소비자\"의 작업을 겹칠 기회가 있을 때마다 이점을 제공합니다." ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "execution": { "iopub.execute_input": "2022-12-14T22:09:49.679164Z", "iopub.status.busy": "2022-12-14T22:09:49.678485Z", "iopub.status.idle": "2022-12-14T22:09:49.938858Z", "shell.execute_reply": "2022-12-14T22:09:49.938031Z" }, "id": "DHpUVqH1-jKi" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Execution time: 0.2330480959999477\n" ] } ], "source": [ "benchmark(\n", " ArtificialDataset()\n", " .prefetch(tf.data.AUTOTUNE)\n", ")" ] }, { "cell_type": "markdown", "metadata": { "id": "h7z_kzo--jKn" }, "source": [ "![Prefetched](https://www.tensorflow.org/guide/images/data_performance/prefetched.svg)\n", "\n", "이번에는 훈련 스텝이 샘플 0에 대해 실행되는 동안 입력 파이프라인이 샘플 1에 대한 데이터를 읽고 등등 하는 방식을 볼 수 있습니다." ] }, { "cell_type": "markdown", "metadata": { "id": "52QMKfaY-jKq" }, "source": [ "### 데이터 추출 병렬화\n", "\n", "실제 환경에서는 입력 데이터가 로컬에 맞지 않거나 학습이 분산되어 있고 입력 데이터를 모든 컴퓨터에 복제하는 것은 적절하지 않기 때문에 입력 데이터를 원격으로(이를테면, GCS나 HDFS) 저장할 수 있습니다. 데이터를 로컬에서 읽는 데이터셋 파이프라인은 다음과 같은 로컬과 원격 저장소의 차이 때문에 원격으로 데이터를 읽을 때 입출력에 병목이 발생할 수 있습니다:\n", "\n", "- **첫 번째 바이트(Time-to-first-byte):** 원격 저장소에서 파일의 첫 번째 바이트를 읽는 것은 로컬 저장소에서 읽어 들이는 것보다 훨씬 오래 걸립니다.\n", "- **읽기 처리량(Read throughput):** 원격 저장소는 보통 큰 총 대역폭을 가지지만 하나의 파일을 읽을 때 이 대역폭의 일부만 활용할 수 있습니다.\n", "\n", "게다가 바이트들이 메모리로 읽혀지면 데이터를 역직렬화 그리고/또는 해독할 필요가 있을 수 있습니다(예를 들면, [protobuf](https://developers.google.com/protocol-buffers/)). 이 작업은 추가적인 계산이 필요합니다. 이 오버헤드는 데이터가 로컬 또는 원격으로 저장되는지와는 관계없이 존재하지만 데이터가 효과적으로 프리페치되지 않으면 원격의 경우에 나빠질 수 있습니다.\n", "\n", "다양한 데이터 추출 오버헤드의 영향을 줄이기 위해 `tf.data.Dataset.interleave` 변환은 (데이터 파일 판독기와 같은)다른 데이터셋의 내용을 인터리빙(interleaving)하여 데이터 추출 단계를 병렬화하는데 사용할 수 있습니다. 중첩할 데이터셋은 `cycle_length` 매개변수에 의해 지정될 수 있는 반면, 병렬처리 수준은 `num_parallel_calls` 매개변수에 의해 지정될 수 있습니다. `prefetch`와 `map` 변환과 비슷하게 `interleave` 변환은 `tf.data.experimental.AUTOTUNE`을 지원합니다. 이것은 어떤 수준의 병렬처리가 tf.data 런타임에 사용되는지에 대해 결정합니다." ] }, { "cell_type": "markdown", "metadata": { "id": "gs8O8Vbu-jKu" }, "source": [ "#### 순차적 인터리브\n", "\n", "`tf.data.Dataset.interleave` 변환의 기본 인수는 두 개의 데이터셋에서 단일 샘플을 순차적으로 인터리브합니다." ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "execution": { "iopub.execute_input": "2022-12-14T22:09:49.942956Z", "iopub.status.busy": "2022-12-14T22:09:49.942280Z", "iopub.status.idle": "2022-12-14T22:09:50.397883Z", "shell.execute_reply": "2022-12-14T22:09:50.397199Z" }, "id": "fDH12GiK-jKw" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "WARNING:tensorflow:From /tmpfs/src/tf_docs_env/lib/python3.9/site-packages/tensorflow/python/autograph/pyct/static_analysis/liveness.py:83: Analyzer.lamba_check (from tensorflow.python.autograph.pyct.static_analysis.liveness) is deprecated and will be removed after 2023-09-23.\n", "Instructions for updating:\n", "Lambda fuctions will be no more assumed to be used in the statement where they are used, or at least in the same block. https://github.com/tensorflow/tensorflow/issues/56089\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Execution time: 0.4113717780001025\n" ] } ], "source": [ "benchmark(\n", " tf.data.Dataset.range(2)\n", " .interleave(lambda _: ArtificialDataset())\n", ")" ] }, { "cell_type": "markdown", "metadata": { "id": "78CsSOnf-jK0" }, "source": [ "![순차적 인터리브](https://www.tensorflow.org/guide/images/data_performance/sequential_interleave.svg)\n", "\n", "이 그림을 사용하면 `interleave` 변환의 결과를 나타낼 수 있으며 사용가능한 두 데이터셋에서 샘플을 가져오는 것이 가능합니다. 그러나 여기에는 성능 향상이 포함되지 않습니다." ] }, { "cell_type": "markdown", "metadata": { "id": "j3cqqmYl-jK2" }, "source": [ "#### 병렬 인터리브\n", "\n", "이제 `interleave` 변환의 `num_parallel_calls` 인수를 사용합니다. 이는 여러 병렬 데이터셋을 불러오고, 파일을 여는 데 기다리는 시간을 단축할 수 있습니다." ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "execution": { "iopub.execute_input": "2022-12-14T22:09:50.401639Z", "iopub.status.busy": "2022-12-14T22:09:50.401135Z", "iopub.status.idle": "2022-12-14T22:09:50.748274Z", "shell.execute_reply": "2022-12-14T22:09:50.747624Z" }, "id": "a3FQcTPY-jK4" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Execution time: 0.3092104200004542\n" ] } ], "source": [ "benchmark(\n", " tf.data.Dataset.range(2)\n", " .interleave(\n", " lambda _: ArtificialDataset(),\n", " num_parallel_calls=tf.data.AUTOTUNE\n", " )\n", ")" ] }, { "cell_type": "markdown", "metadata": { "id": "RxRLPB6C-jLA" }, "source": [ "![병렬 인터리브](https://www.tensorflow.org/guide/images/data_performance/parallel_interleave.svg)\n", "\n", "이번에는 읽은 두 데이터셋이 병렬화되어 전역 데이터 처리 시간이 줄어듭니다." ] }, { "cell_type": "markdown", "metadata": { "id": "5ZCLFWyv-jLB" }, "source": [ "### 데이터 변환 병렬화\n", "\n", "데이터를 준비할 때 입력 요소를 사전 처리해야 할 수 있습니다. 이를 위해 `tf.data` API는 입력 데이터세트의 각 요소에 사용자 정의 함수를 적용하는 `tf.data.Dataset.map` 변환을 제공합니다. 입력 요소는 서로 독립적이므로 전처리를 여러 CPU 코어에 걸쳐 병렬화할 수 있습니다. 이를 가능하게 하기 위해 `prefetch` 및 `interleave` 변환과 마찬가지로 `map` 변환은 병렬 처리 수준을 지정하는 `num_parallel_calls` 인수를 제공합니다.\n", "\n", "`num_parallel_calls` 인수에 가장 적합한 값을 선택하는 것은 하드웨어, 훈련 데이터의 특성(크기 및 형상 등), 맵 함수 비용, CPU에서 동시에 발생하는 기타 처리 등의 요인에 따라 다릅니다. 간단한 경험적 방법은 사용 가능한 CPU 코어 수를 사용하는 것입니다. 그러나 `prefetch` 및 `interleave` 변환의 경우와 같이 `map` 변환은 `tf.data` 런타임에 사용할 병렬 처리 수준에 대한 결정을 위임하는 `tf.data.AUTOTUNE`을 지원합니다." ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "execution": { "iopub.execute_input": "2022-12-14T22:09:50.752065Z", "iopub.status.busy": "2022-12-14T22:09:50.751442Z", "iopub.status.idle": "2022-12-14T22:09:50.755190Z", "shell.execute_reply": "2022-12-14T22:09:50.754522Z" }, "id": "GSkKetpx-jLD" }, "outputs": [], "source": [ "def mapped_function(s):\n", " # Do some hard pre-processing\n", " tf.py_function(lambda: time.sleep(0.03), [], ())\n", " return s" ] }, { "cell_type": "markdown", "metadata": { "id": "wiU7W_QC-jLI" }, "source": [ "#### 순차적 매핑\n", "\n", "병렬 처리 없이 `map` 변환을 기본 예제로 사용하여 시작하십시오." ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "execution": { "iopub.execute_input": "2022-12-14T22:09:50.758591Z", "iopub.status.busy": "2022-12-14T22:09:50.757949Z", "iopub.status.idle": "2022-12-14T22:09:51.234409Z", "shell.execute_reply": "2022-12-14T22:09:51.233622Z" }, "id": "ZSBvDpJG-jLL" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Execution time: 0.4225687520001884\n" ] } ], "source": [ "benchmark(\n", " ArtificialDataset()\n", " .map(mapped_function)\n", ")" ] }, { "cell_type": "markdown", "metadata": { "id": "ngwMTDb6-jLR" }, "source": [ "![순차적 매핑](https://www.tensorflow.org/guide/images/data_performance/sequential_map.svg)\n", "\n", "[추상적 접근](#The-naive-approach)의 경우 여기에서 열기, 읽기, 전처리(매핑) 및 단일 반복을 위해 훈련 스텝에 소요된 시간이 합산됩니다." ] }, { "cell_type": "markdown", "metadata": { "id": "U-10PE1D-jLU" }, "source": [ "#### 병렬 매핑\n", "\n", "이제 동일한 전처리 함수를 사용하지만 여러 샘플에 병렬로 적용하십시오." ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "execution": { "iopub.execute_input": "2022-12-14T22:09:51.238107Z", "iopub.status.busy": "2022-12-14T22:09:51.237607Z", "iopub.status.idle": "2022-12-14T22:09:51.569285Z", "shell.execute_reply": "2022-12-14T22:09:51.568577Z" }, "id": "F8AYLZbg-jLV" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Execution time: 0.30099615800008905\n" ] } ], "source": [ "benchmark(\n", " ArtificialDataset()\n", " .map(\n", " mapped_function,\n", " num_parallel_calls=tf.data.AUTOTUNE\n", " )\n", ")" ] }, { "cell_type": "markdown", "metadata": { "id": "-MoJklzP-jLe" }, "source": [ "![병렬 매핑](https://www.tensorflow.org/guide/images/data_performance/parallel_map.svg)\n", "\n", "이제 그림(plot)에서 전처리 단계가 겹치므로 단일 반복의 전체 시간이 줄어 듭니다." ] }, { "cell_type": "markdown", "metadata": { "id": "ZY1Q9kJO-jLh" }, "source": [ "### 캐시하기\n", "\n", "`tf.data.Dataset.cache` 변환은 데이터셋을 메모리 또는 로컬 저장소에 캐시할 수 있습니다. 이렇게하면 각 에포크 동안 실행되는 일부 작업(파일 열기 및 데이터 읽기 등)이 저장됩니다." ] }, { "cell_type": "code", "execution_count": 12, "metadata": { "execution": { "iopub.execute_input": "2022-12-14T22:09:51.572902Z", "iopub.status.busy": "2022-12-14T22:09:51.572644Z", "iopub.status.idle": "2022-12-14T22:09:51.956809Z", "shell.execute_reply": "2022-12-14T22:09:51.956119Z" }, "id": "xieLApaI-jLi" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Execution time: 0.3519092630003797\n" ] } ], "source": [ "benchmark(\n", " ArtificialDataset()\n", " .map( # Apply time consuming operations before cache\n", " mapped_function\n", " ).cache(\n", " ),\n", " 5\n", ")" ] }, { "cell_type": "markdown", "metadata": { "id": "KeMgW9XI-jLn" }, "source": [ "![캐시된 데이터셋](https://www.tensorflow.org/guide/images/data_performance/cached_dataset.svg)\n", "\n", "데이터셋을 캐시할 때, `cache` 이전의 변환(파일 열기 및 데이터 읽기와 같은)은 첫 번째 에포크 동안에만 실행됩니다. 다음 에포크에는 `cache` 변환에 의해 캐시된 데이터를 재사용 할 것입니다.\n", "\n", "`map` 변환에 전달된 사용자 정의 함수가 비싸면 결과 데이터셋이 여전히 메모리 또는 로컬 스토리지에 적합할 수 있는 한 `map` 변환 후 `cache` 변환을 적용합니다.사용자 정의 함수가 캐시 용량을 넘어서 데이터셋을 저장하는 데 필요한 공간을 늘리면 `cache` 변환 후 데이터셋을 적용하거나 훈련 작업 전에 데이터를 전처리하여 리소스 사용량을 줄입니다." ] }, { "cell_type": "markdown", "metadata": { "id": "i3NtGI3r-jLp" }, "source": [ "### 매핑 벡터화\n", "\n", "`map` 변환으로 전달된 사용자 정의 함수를 호출하면 사용자 정의 함수의 스케줄링 및 실행과 관련된 오버헤드가 있습니다. 사용자 정의 함수를 벡터화(즉, 한 번에 여러 입력에 대해 작동하도록)하고 `맵`을 변환하기 *전에* `배치` 변환을 적용하는 것이 좋습니다.\n", "\n", "이 모범 사례를 설명하는 데 인공 데이터셋은 적합하지 않습니다. 스케줄링 지연은 약 10 마이크로초(10e-6초)로, `ArtificialDataset`에 사용된 수십 밀리초보다 훨씬 짧으므로 그 영향을 보기가 어렵습니다.\n", "\n", "이 예제에서는 기본 `tf.data.Dataset.range` 함수를 사용하고 훈련 루프를 가장 간단한 형태로 단순화하십시오." ] }, { "cell_type": "code", "execution_count": 13, "metadata": { "execution": { "iopub.execute_input": "2022-12-14T22:09:51.960313Z", "iopub.status.busy": "2022-12-14T22:09:51.960067Z", "iopub.status.idle": "2022-12-14T22:09:51.965034Z", "shell.execute_reply": "2022-12-14T22:09:51.964395Z" }, "id": "xqtiYPmb-jLt" }, "outputs": [], "source": [ "fast_dataset = tf.data.Dataset.range(10000)\n", "\n", "def fast_benchmark(dataset, num_epochs=2):\n", " start_time = time.perf_counter()\n", " for _ in tf.data.Dataset.range(num_epochs):\n", " for _ in dataset:\n", " pass\n", " tf.print(\"Execution time:\", time.perf_counter() - start_time)\n", " \n", "def increment(x):\n", " return x+1" ] }, { "cell_type": "markdown", "metadata": { "id": "Fj2gmsMT-jL5" }, "source": [ "#### 스칼라 매핑" ] }, { "cell_type": "code", "execution_count": 14, "metadata": { "execution": { "iopub.execute_input": "2022-12-14T22:09:51.968322Z", "iopub.status.busy": "2022-12-14T22:09:51.967743Z", "iopub.status.idle": "2022-12-14T22:09:52.212624Z", "shell.execute_reply": "2022-12-14T22:09:52.211936Z" }, "id": "Imn3SslJ-jMA" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Execution time: 0.21174333700037096\n" ] } ], "source": [ "fast_benchmark(\n", " fast_dataset\n", " # Apply function one item at a time\n", " .map(increment)\n", " # Batch\n", " .batch(256)\n", ")" ] }, { "cell_type": "markdown", "metadata": { "id": "BWUNbPqv-jMF" }, "source": [ "![스칼라 맵](https://www.tensorflow.org/guide/images/data_performance/scalar_map.svg)\n", "\n", "위의 그림은 (샘플이 적은) 진행 상황을 보여줍니다. 매핑된 함수가 각 샘플에 적용되어 있음을 알 수 있습니다. 이 기능은 매우 빠르지만 시간 성능에 영향을 주는 약간의 오버헤드가 있습니다." ] }, { "cell_type": "markdown", "metadata": { "id": "tDVSM0A--jMG" }, "source": [ "#### 매핑 벡터화됨" ] }, { "cell_type": "code", "execution_count": 15, "metadata": { "execution": { "iopub.execute_input": "2022-12-14T22:09:52.216425Z", "iopub.status.busy": "2022-12-14T22:09:52.215792Z", "iopub.status.idle": "2022-12-14T22:09:52.262806Z", "shell.execute_reply": "2022-12-14T22:09:52.262081Z" }, "id": "nAw1mDLw-jMI" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Execution time: 0.03488941399973555\n" ] } ], "source": [ "fast_benchmark(\n", " fast_dataset\n", " .batch(256)\n", " # Apply function on a batch of items\n", " # The tf.Tensor.__add__ method already handle batches\n", " .map(increment)\n", ")" ] }, { "cell_type": "markdown", "metadata": { "id": "DbMteMY9-jMO" }, "source": [ "![벡터화된 맵](https://www.tensorflow.org/guide/images/data_performance/vectorized_map.svg)\n", "\n", "이번에는 매핑된 함수가 한 번 호출되어 샘플 배치에 적용됩니다. 이 함수를 실행하는 데 시간이 더 걸릴 수 있지만 오버헤드는 한 번만 나타나므로 전체 시간 성능이 향상됩니다." ] }, { "cell_type": "markdown", "metadata": { "id": "hfueG0Wj-jMR" }, "source": [ "### 메모리 사용량(footprint) 줄이기\n", "\n", "`interleave`, `prefetch`, `shuffle`을 포함한 많은 변환은 요소들의 내부 버퍼를 유지합니다. 사용자 정의 함수가 `map` 변환에 전달된 경우 요소의 크기가 변경되고 맵 변환의 순서와 버퍼 요소가 메모리 사용에 영향을 줍니다. 일반적으로 순서를 다르게 하는 것이 성능에 도움이 되는 경우 메모리 사용량이 낮아지는 순서를 선택하는 것이 좋습니다.\n", "\n", "#### 부분 계산 캐싱\n", "\n", "이 변환으로 인해 데이터가 너무 커서 메모리에 맞지 않는 경우를 제외하고 `map` 변환 후 데이터셋을 캐시하는 것이 좋습니다. 매핑된 기능을 시간 소모적인 부분과 메모리 소모적인 부분의 두 부분으로 나눌 수 있다면 교환이 성사될 수 있습니다. 이 경우 아래와 같이 변환을 연결할 수 있습니다:\n", "\n", "```python\n", "dataset.map(time_consuming_mapping).cache().map(memory_consuming_mapping)\n", "```\n", "\n", "이런 식으로 시간이 많이 걸리는 부분은 첫 번째 에포크(epoch) 동안에만 실행되며 너무 많은 캐시 공간을 사용하지 않습니다." ] }, { "cell_type": "markdown", "metadata": { "id": "MYOHG69M-jMT" }, "source": [ "## 가장 좋은 예제 요약\n", "\n", "다음은 성능이 좋은 텐서플로 입력 파이프라인을 설계하기 위한 가장 좋은 예제를 요약한 것입니다:\n", "\n", "- [`prefetch` 변환](#Pipelining)을 사용하여 프로듀서와 컨슈머의 작업을 오버랩하세요.\n", "- `interleave` 변환을 이용해 [데이터 읽기 변환을 병렬화하세요](#Parallelizing-data-extraction).\n", "- `num_parallel_calls` 매개변수를 설정하여 [`map` 변환을 병렬 처리하세요](#Parallelizing-data-transformation).\n", "- 데이터가 메모리에 저장될 수 있는 경우, [`cache` 변환을 사용](#Caching)하여 첫 번째 에포크동안 데이터를 메모리에 캐시하세요.\n", "- `map` 변환에 전달된 [사용자 정의 함수를 벡터화](#Map-and-batch)하세요.\n", "- `interleave`, `prefetch`, 그리고 `shuffle` 변환을 적용하여 [메모리 사용을 줄이세요](#Reducing-memory-footprint)." ] }, { "cell_type": "markdown", "metadata": { "id": "mP_EMFsQ-jMU" }, "source": [ "## 그림 재현\n", "\n", "참고: 이 노트북의 나머지 부분은 위의 그림을 재현하는 방법에 대한 것이며, 이 코드로 자유롭게 놀아볼 수 있지만 이해하는 것은 이 자습서의 필수적인 부분이 아닙니다.\n", "\n", "`tf.data.Dataset` API에 대해 더 깊이 이해하기 위해 자신만의 파이프라인을 사용할 수 있습니다. 다음은 이 안내서의 이미지를 그리는 데 사용되는 코드입니다. 다음과 같은 일반적인 어려움에 대한 해결 방법을 보여주는 좋은 출발점이 될 수 있습니다:\n", "\n", "- 실행 시간 재현성;\n", "- 매핑 된 기능 즉시 실행;\n", "- `interleave` 변환 호출 가능." ] }, { "cell_type": "code", "execution_count": 16, "metadata": { "execution": { "iopub.execute_input": "2022-12-14T22:09:52.266624Z", "iopub.status.busy": "2022-12-14T22:09:52.266029Z", "iopub.status.idle": "2022-12-14T22:09:52.608987Z", "shell.execute_reply": "2022-12-14T22:09:52.608326Z" }, "id": "7M_jFLer-jMV" }, "outputs": [], "source": [ "import itertools\n", "from collections import defaultdict\n", "\n", "import numpy as np\n", "import matplotlib as mpl\n", "import matplotlib.pyplot as plt" ] }, { "cell_type": "markdown", "metadata": { "id": "Z3pjnxtK-jMa" }, "source": [ "### 데이터셋\n", "\n", "`ArtificialDataset`과 비슷하게 각 단계에서 소요된 시간을 리턴하는 데이터셋을 빌드할 수 있습니다." ] }, { "cell_type": "code", "execution_count": 17, "metadata": { "execution": { "iopub.execute_input": "2022-12-14T22:09:52.612930Z", "iopub.status.busy": "2022-12-14T22:09:52.612670Z", "iopub.status.idle": "2022-12-14T22:09:52.619675Z", "shell.execute_reply": "2022-12-14T22:09:52.619065Z" }, "id": "OgGl4U7t-jMc" }, "outputs": [], "source": [ "class TimeMeasuredDataset(tf.data.Dataset):\n", " # OUTPUT: (steps, timings, counters)\n", " OUTPUT_TYPES = (tf.dtypes.string, tf.dtypes.float32, tf.dtypes.int32)\n", " OUTPUT_SHAPES = ((2, 1), (2, 2), (2, 3))\n", " \n", " _INSTANCES_COUNTER = itertools.count() # Number of datasets generated\n", " _EPOCHS_COUNTER = defaultdict(itertools.count) # Number of epochs done for each dataset\n", " \n", " def _generator(instance_idx, num_samples):\n", " epoch_idx = next(TimeMeasuredDataset._EPOCHS_COUNTER[instance_idx])\n", " \n", " # Opening the file\n", " open_enter = time.perf_counter()\n", " time.sleep(0.03)\n", " open_elapsed = time.perf_counter() - open_enter\n", " \n", " for sample_idx in range(num_samples):\n", " # Reading data (line, record) from the file\n", " read_enter = time.perf_counter()\n", " time.sleep(0.015)\n", " read_elapsed = time.perf_counter() - read_enter\n", " \n", " yield (\n", " [(\"Open\",), (\"Read\",)],\n", " [(open_enter, open_elapsed), (read_enter, read_elapsed)],\n", " [(instance_idx, epoch_idx, -1), (instance_idx, epoch_idx, sample_idx)]\n", " )\n", " open_enter, open_elapsed = -1., -1. # Negative values will be filtered\n", " \n", " \n", " def __new__(cls, num_samples=3):\n", " return tf.data.Dataset.from_generator(\n", " cls._generator,\n", " output_types=cls.OUTPUT_TYPES,\n", " output_shapes=cls.OUTPUT_SHAPES,\n", " args=(next(cls._INSTANCES_COUNTER), num_samples)\n", " )" ] }, { "cell_type": "markdown", "metadata": { "id": "YQqDP4jk-jMj" }, "source": [ "이 데이터셋은 `[[2, 1], [2, 2], [2, 3]]`의 크기와 `[tf.dtypes.string, tf.dtypes.float32, tf.dtypes.int32]`의 타입을 가진 샘플을 제공합니다. 각 샘플은:\n", "\n", "```\n", "(\n", " [(\"Open\"), (\"Read\")],\n", " [(t0, d), (t0, d)],\n", " [(i, e, -1), (i, e, s)]\n", ")\n", "```\n", "\n", "이며,\n", "\n", "- `Open`과 `Read`는 스텝 식별자\n", "- `t0`는 해당 스텝이 시작된 타임스탬프\n", "- `d`는 해당 스텝에서 소비된 시간\n", "- `i`는 인스턴스의 인덱스\n", "- `e`는 에포크 인덱스(데이터셋이 반복된 횟수)\n", "- `s`는 샘플 인덱스입니다." ] }, { "cell_type": "markdown", "metadata": { "id": "IQK913bB-jMm" }, "source": [ "### 반복 루프\n", "\n", "반복 루프를 조금 더 복잡하게 하여 모든 타이밍을 집계하십시오. 위에서 설명한 대로 샘플을 생성하는 데이터셋에서만 작동합니다." ] }, { "cell_type": "code", "execution_count": 18, "metadata": { "execution": { "iopub.execute_input": "2022-12-14T22:09:52.623119Z", "iopub.status.busy": "2022-12-14T22:09:52.622610Z", "iopub.status.idle": "2022-12-14T22:09:52.630104Z", "shell.execute_reply": "2022-12-14T22:09:52.629528Z" }, "id": "zAy-K_Cq-jMn" }, "outputs": [], "source": [ "def timelined_benchmark(dataset, num_epochs=2):\n", " # Initialize accumulators\n", " steps_acc = tf.zeros([0, 1], dtype=tf.dtypes.string)\n", " times_acc = tf.zeros([0, 2], dtype=tf.dtypes.float32)\n", " values_acc = tf.zeros([0, 3], dtype=tf.dtypes.int32)\n", " \n", " start_time = time.perf_counter()\n", " for epoch_num in range(num_epochs):\n", " epoch_enter = time.perf_counter()\n", " for (steps, times, values) in dataset:\n", " # Record dataset preparation informations\n", " steps_acc = tf.concat((steps_acc, steps), axis=0)\n", " times_acc = tf.concat((times_acc, times), axis=0)\n", " values_acc = tf.concat((values_acc, values), axis=0)\n", " \n", " # Simulate training time\n", " train_enter = time.perf_counter()\n", " time.sleep(0.01)\n", " train_elapsed = time.perf_counter() - train_enter\n", " \n", " # Record training informations\n", " steps_acc = tf.concat((steps_acc, [[\"Train\"]]), axis=0)\n", " times_acc = tf.concat((times_acc, [(train_enter, train_elapsed)]), axis=0)\n", " values_acc = tf.concat((values_acc, [values[-1]]), axis=0)\n", " \n", " epoch_elapsed = time.perf_counter() - epoch_enter\n", " # Record epoch informations\n", " steps_acc = tf.concat((steps_acc, [[\"Epoch\"]]), axis=0)\n", " times_acc = tf.concat((times_acc, [(epoch_enter, epoch_elapsed)]), axis=0)\n", " values_acc = tf.concat((values_acc, [[-1, epoch_num, -1]]), axis=0)\n", " time.sleep(0.001)\n", " \n", " tf.print(\"Execution time:\", time.perf_counter() - start_time)\n", " return {\"steps\": steps_acc, \"times\": times_acc, \"values\": values_acc}" ] }, { "cell_type": "markdown", "metadata": { "id": "jw_WSQC8-jMs" }, "source": [ "### 그리기(plotting) 메서드\n", "\n", "마지막으로, `timelined_benchmark` 함수에 의해 리턴된 값이 주어지면 타임라인을 그릴 수 있는 함수를 정의하십시오." ] }, { "cell_type": "code", "execution_count": 19, "metadata": { "execution": { "iopub.execute_input": "2022-12-14T22:09:52.633395Z", "iopub.status.busy": "2022-12-14T22:09:52.632800Z", "iopub.status.idle": "2022-12-14T22:09:52.642661Z", "shell.execute_reply": "2022-12-14T22:09:52.642039Z" }, "id": "1j73RxiP-jMw" }, "outputs": [], "source": [ "def draw_timeline(timeline, title, width=0.5, annotate=False, save=False):\n", " # Remove invalid entries (negative times, or empty steps) from the timelines\n", " invalid_mask = np.logical_and(timeline['times'] > 0, timeline['steps'] != b'')[:,0]\n", " steps = timeline['steps'][invalid_mask].numpy()\n", " times = timeline['times'][invalid_mask].numpy()\n", " values = timeline['values'][invalid_mask].numpy()\n", " \n", " # Get a set of different steps, ordered by the first time they are encountered\n", " step_ids, indices = np.stack(np.unique(steps, return_index=True))\n", " step_ids = step_ids[np.argsort(indices)]\n", "\n", " # Shift the starting time to 0 and compute the maximal time value\n", " min_time = times[:,0].min()\n", " times[:,0] = (times[:,0] - min_time)\n", " end = max(width, (times[:,0]+times[:,1]).max() + 0.01)\n", " \n", " cmap = mpl.cm.get_cmap(\"plasma\")\n", " plt.close()\n", " fig, axs = plt.subplots(len(step_ids), sharex=True, gridspec_kw={'hspace': 0})\n", " fig.suptitle(title)\n", " fig.set_size_inches(17.0, len(step_ids))\n", " plt.xlim(-0.01, end)\n", " \n", " for i, step in enumerate(step_ids):\n", " step_name = step.decode()\n", " ax = axs[i]\n", " ax.set_ylabel(step_name)\n", " ax.set_ylim(0, 1)\n", " ax.set_yticks([])\n", " ax.set_xlabel(\"time (s)\")\n", " ax.set_xticklabels([])\n", " ax.grid(which=\"both\", axis=\"x\", color=\"k\", linestyle=\":\")\n", " \n", " # Get timings and annotation for the given step\n", " entries_mask = np.squeeze(steps==step)\n", " serie = np.unique(times[entries_mask], axis=0)\n", " annotations = values[entries_mask]\n", " \n", " ax.broken_barh(serie, (0, 1), color=cmap(i / len(step_ids)), linewidth=1, alpha=0.66)\n", " if annotate:\n", " for j, (start, width) in enumerate(serie):\n", " annotation = \"\\n\".join([f\"{l}: {v}\" for l,v in zip((\"i\", \"e\", \"s\"), annotations[j])])\n", " ax.text(start + 0.001 + (0.001 * (j % 2)), 0.55 - (0.1 * (j % 2)), annotation,\n", " horizontalalignment='left', verticalalignment='center')\n", " if save:\n", " plt.savefig(title.lower().translate(str.maketrans(\" \", \"_\")) + \".svg\")" ] }, { "cell_type": "markdown", "metadata": { "id": "xto6GNdO-jM1" }, "source": [ "### 매핑된 함수용 래퍼(wrappers) 사용\n", "\n", "eager 컨텍스트에서 매핑된 함수를 실행하려면 `tf.py_function` 호출 내에서 래핑해야 합니다." ] }, { "cell_type": "code", "execution_count": 20, "metadata": { "execution": { "iopub.execute_input": "2022-12-14T22:09:52.645728Z", "iopub.status.busy": "2022-12-14T22:09:52.645206Z", "iopub.status.idle": "2022-12-14T22:09:52.648673Z", "shell.execute_reply": "2022-12-14T22:09:52.648104Z" }, "id": "39v7JD4L-jM2" }, "outputs": [], "source": [ "def map_decorator(func):\n", " def wrapper(steps, times, values):\n", " # Use a tf.py_function to prevent auto-graph from compiling the method\n", " return tf.py_function(\n", " func,\n", " inp=(steps, times, values),\n", " Tout=(steps.dtype, times.dtype, values.dtype)\n", " )\n", " return wrapper" ] }, { "cell_type": "markdown", "metadata": { "id": "7eJRCinb-jM5" }, "source": [ "### 파이프라인 비교" ] }, { "cell_type": "code", "execution_count": 21, "metadata": { "execution": { "iopub.execute_input": "2022-12-14T22:09:52.651868Z", "iopub.status.busy": "2022-12-14T22:09:52.651341Z", "iopub.status.idle": "2022-12-14T22:09:52.654473Z", "shell.execute_reply": "2022-12-14T22:09:52.653903Z" }, "id": "YwX4ndHE-jM6" }, "outputs": [], "source": [ "_batch_map_num_items = 50\n", "\n", "def dataset_generator_fun(*args):\n", " return TimeMeasuredDataset(num_samples=_batch_map_num_items)" ] }, { "cell_type": "markdown", "metadata": { "id": "EwxJT2aR-jNA" }, "source": [ "#### Naive" ] }, { "cell_type": "code", "execution_count": 22, "metadata": { "execution": { "iopub.execute_input": "2022-12-14T22:09:52.657458Z", "iopub.status.busy": "2022-12-14T22:09:52.657027Z", "iopub.status.idle": "2022-12-14T22:10:05.704326Z", "shell.execute_reply": "2022-12-14T22:10:05.703566Z" }, "id": "wLKgurx_-jNC" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "WARNING:tensorflow:From /tmpfs/tmp/ipykernel_180937/64197174.py:32: calling DatasetV2.from_generator (from tensorflow.python.data.ops.dataset_ops) with output_types is deprecated and will be removed in a future version.\n", "Instructions for updating:\n", "Use output_signature instead\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "WARNING:tensorflow:From /tmpfs/tmp/ipykernel_180937/64197174.py:32: calling DatasetV2.from_generator (from tensorflow.python.data.ops.dataset_ops) with output_shapes is deprecated and will be removed in a future version.\n", "Instructions for updating:\n", "Use output_signature instead\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Execution time: 12.946511227999508\n" ] } ], "source": [ "@map_decorator\n", "def naive_map(steps, times, values):\n", " map_enter = time.perf_counter()\n", " time.sleep(0.001) # Time consuming step\n", " time.sleep(0.0001) # Memory consuming step\n", " map_elapsed = time.perf_counter() - map_enter\n", "\n", " return (\n", " tf.concat((steps, [[\"Map\"]]), axis=0),\n", " tf.concat((times, [[map_enter, map_elapsed]]), axis=0),\n", " tf.concat((values, [values[-1]]), axis=0)\n", " )\n", "\n", "naive_timeline = timelined_benchmark(\n", " tf.data.Dataset.range(2)\n", " .flat_map(dataset_generator_fun)\n", " .map(naive_map)\n", " .batch(_batch_map_num_items, drop_remainder=True)\n", " .unbatch(),\n", " 5\n", ")" ] }, { "cell_type": "markdown", "metadata": { "id": "EJqUMDsO-jNG" }, "source": [ "### Optimized" ] }, { "cell_type": "code", "execution_count": 23, "metadata": { "execution": { "iopub.execute_input": "2022-12-14T22:10:05.708058Z", "iopub.status.busy": "2022-12-14T22:10:05.707572Z", "iopub.status.idle": "2022-12-14T22:10:12.459026Z", "shell.execute_reply": "2022-12-14T22:10:12.458225Z" }, "id": "HYHcwabr-jNH" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Execution time: 6.684641828000167\n" ] } ], "source": [ "@map_decorator\n", "def time_consuming_map(steps, times, values):\n", " map_enter = time.perf_counter()\n", " time.sleep(0.001 * values.shape[0]) # Time consuming step\n", " map_elapsed = time.perf_counter() - map_enter\n", "\n", " return (\n", " tf.concat((steps, tf.tile([[[\"1st map\"]]], [steps.shape[0], 1, 1])), axis=1),\n", " tf.concat((times, tf.tile([[[map_enter, map_elapsed]]], [times.shape[0], 1, 1])), axis=1),\n", " tf.concat((values, tf.tile([[values[:][-1][0]]], [values.shape[0], 1, 1])), axis=1)\n", " )\n", "\n", "\n", "@map_decorator\n", "def memory_consuming_map(steps, times, values):\n", " map_enter = time.perf_counter()\n", " time.sleep(0.0001 * values.shape[0]) # Memory consuming step\n", " map_elapsed = time.perf_counter() - map_enter\n", "\n", " # Use tf.tile to handle batch dimension\n", " return (\n", " tf.concat((steps, tf.tile([[[\"2nd map\"]]], [steps.shape[0], 1, 1])), axis=1),\n", " tf.concat((times, tf.tile([[[map_enter, map_elapsed]]], [times.shape[0], 1, 1])), axis=1),\n", " tf.concat((values, tf.tile([[values[:][-1][0]]], [values.shape[0], 1, 1])), axis=1)\n", " )\n", "\n", "\n", "optimized_timeline = timelined_benchmark(\n", " tf.data.Dataset.range(2)\n", " .interleave( # Parallelize data reading\n", " dataset_generator_fun,\n", " num_parallel_calls=tf.data.AUTOTUNE\n", " )\n", " .batch( # Vectorize your mapped function\n", " _batch_map_num_items,\n", " drop_remainder=True)\n", " .map( # Parallelize map transformation\n", " time_consuming_map,\n", " num_parallel_calls=tf.data.AUTOTUNE\n", " )\n", " .cache() # Cache data\n", " .map( # Reduce memory usage\n", " memory_consuming_map,\n", " num_parallel_calls=tf.data.AUTOTUNE\n", " )\n", " .prefetch( # Overlap producer and consumer works\n", " tf.data.AUTOTUNE\n", " )\n", " .unbatch(),\n", " 5\n", ")" ] }, { "cell_type": "code", "execution_count": 24, "metadata": { "execution": { "iopub.execute_input": "2022-12-14T22:10:12.462715Z", "iopub.status.busy": "2022-12-14T22:10:12.462437Z", "iopub.status.idle": "2022-12-14T22:10:12.980043Z", "shell.execute_reply": "2022-12-14T22:10:12.979355Z" }, "id": "b_CSUbxL-jNK" }, "outputs": [ { "data": { "image/png": "iVBORw0KGgoAAAANSUhEUgAABVEAAAHkCAYAAAA3qG8GAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjYuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8o6BhiAAAACXBIWXMAAA9hAAAPYQGoP6dpAAB7jklEQVR4nO3deZyVdfn/8ffZ9xkhXAHNHcH2oCYrMSnMzMDCJRWXsEVJc1z6glZIBuo3x7JJTXFD+6qNCmpaGPxSU8cGs8zYMlBZpBRZZubM2e5zzu8PkyRknPvMDPd9wev5ePB46Jlz7rk4vO9rPny4r/sEqtVqVQAAAAAAAACAdxT0ugAAAAAAAAAA8DM2UQEAAAAAAACgG2yiAgAAAAAAAEA32EQFAAAAAAAAgG6wiQoAAAAAAAAA3WATFQAAAAAAAAC6wSYqAAAAAAAAAHSDTVQAAAAAAAAA6AabqAAAAAAAAADQDTZRAQAAsF2NHj1ao0eP9roMAAAAoMfYRAUAAMA7uu222xQIBBSPx7VmzZqtvj569GgdeuihHlQGAAAAbF9sogIAAKBbhUJBV1xxRZ8d79FHH9Wjjz7aZ8cDAAAA+hubqAAAAOjWBz/4Qd1000169dVX++R40WhU0Wi0T44FAAAAbA9sogIAAKBbU6dOVblcfterUW+99VZ95jOf0W677aZYLKbhw4fr+uuv3+p5b78n6r/+9S+Fw2FddtllWz1v2bJlCgQCam5u3vzYxo0b9Z3vfEdDhw5VLBbTAQccoCuvvFKVSqV3v0kAAACgG2yiAgAAoFv77ruvJk6c+K5Xo15//fXaZ599NHXqVF199dUaOnSozj77bP385z/f5mt23313HX744frVr3611dfuuecehUIhTZgwQZLU1dWlww8/XHfeeacmTpyoa6+9VocddpimTJmixsbG3v9GAQAAgG0Ie10AAAAA/O+SSy7R7NmzdeWVV+qnP/3pOz7n8ccfVyKR2Pz/kydP1lFHHaWmpiadc8452zz2CSecoG984xv629/+tsUHVd1zzz06/PDDtfvuu0uSmpqatHz5cv35z3/WgQceKEn6xje+ob322kv/+7//qwsuuEBDhw7ti98uAAAAsAWuRAUAAMC72m+//XTqqafqxhtv1Nq1a9/xOW/fQN20aZPWrVunww8/XCtWrNCmTZu2eezjjjtO4XBY99xzz+bH/va3v2nx4sU64YQTNj/W0tKiT33qUxowYIDWrVu3+deYMWNULpf1xBNP9MHvFAAAANgam6gAAADokUsvvVSO42zz3qhPPfWUxowZo1QqpV122UW77rqrpk6dKkndbqIOGjRIRx555BYj/ffcc4/C4bCOO+64zY+9+OKL+u1vf6tdd911i19jxoyRJL322mt98dsEAAAAtsI4PwAAAHpkv/320ymnnKIbb7xR//M//7PF15YvX64jjzxSw4YNU1NTk4YOHapoNKpHHnlE11xzzbt+8NOJJ56oM844Q3/5y1/0wQ9+UL/61a905JFHatCgQZufU6lU9NnPflYXX3zxOx7joIMO6v1vEgAAAHgHbKICAACgxy699FLdeeeduvLKK7d4/KGHHlKhUNCDDz6ovffee/Pjv//973t03HHjxukb3/jG5pH+v//975oyZcoWz9l///3V2dm5+cpTAAAAYHthnB8AAAA9tv/+++uUU07RL37xC/3zn//c/HgoFJIkVavVzY9t2rRJt956a4+Ou8suu2js2LH61a9+pbvvvlvRaFTjxo3b4jnHH3+8WltbNW/evK1ev3HjRjmOU8PvCAAAAHh3bKICAADAlUsuuUSlUknLli3b/NjnPvc5RaNRffGLX9TPf/5zXXnllfrIRz6i3XbbrcfHPeGEE7RixQpdd911Gjt2rHbZZZctvn7RRRfpwx/+sI455hidddZZuuGGG3T11Vfr9NNP15AhQ7Rx48Y++h0CAAAAW2ITFQAAAK4ccMABOuWUU7Z47OCDD9a9996rQCCgCy+8UDfccIO+/vWv67zzzuvxcY899lglEgl1dHTohBNO2OrryWRSjz/+uC666CI99thjOu+883TFFVfoxRdf1GWXXab6+vpe/94AAACAdxKovn3mCgAAAAAAAACwBa5EBQAAAAAAAIBusIkKAAAAAAAAAN1gExUAAAAAAAAAusEmKgAAAAAAAAB0g01UAAAAAAAAAOgGm6gAAAAAAAAA0A02UQEAAAAAAACgG2yiAgAAAAAAAEA32EQFAAAAAAAAgG6wiQoAAAAAAAAA3WATFQAAAAAAAAC6wSYqAAAAAAAAAHSDTVQAAAAAAAAA6AabqAAAAAAAAADQDTZRAQAAAAAAAKAbbKICAAAAAAAAQDfYRAUAAAAAAACAbrCJCgAAAAAAAADd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sJLJpL7yla+oq6tLt99+u9773vdqwIABOvfcc7cYwS8UCrrwwgs1ePBgpVIpfexjH9Njjz3WbU133323PvvZzyoej29+7Pnnn9cRRxyhTCajuro6feQjH9Gzzz67+etf/OIX9eyzz2r58uV9+v4AAADAjrDXBQAAAGDH9NOf/lR///vfdeihh2r69OmSpF133VUvv/zyVs/t6urStddeq7vvvlsdHR067rjjNH78eO2yyy565JFHtGLFCn35y1/WYYcdphNOOEGSNHnyZC1evFh333239tprL82ZM0dHHXWUXnjhBR144IHvWNMf/vAHffWrX93isZNPPlkf+tCHdP311ysUCukvf/mLIpHI5q/vvffe2n333fWHP/xB+++/fx+9OwAAALCETVQAAAD0i/r6ekWjUSWTSe2xxx7dPrdUKun666/fvEn5la98RXfccYf+9a9/KZ1Oa/jw4TriiCP0+9//XieccIJWrlypW2+9VStXrtRee+0lSbrwwgv129/+VrfeeqtmzJjxjt/nlVde2fz8t6xcuVIXXXSRhg0bJknvuAG711576ZVXXnH9HgAAAGDHwCYqAAAAPJdMJre4ynP33XfXe9/7XqXT6S0ee+211yRJL7zwgsrlsg466KAtjlMoFPSe97xnm98nl8ttMcovSY2NjZo0aZLuuOMOjRkzRhMmTNjqitNEIqGurq6af38AAACwjU1UAAAAeO7t4/OSFAgE3vGxSqUiSers7FQoFNKf/vQnhUKhLZ739o3X/zZo0CBt2LBhi8emTZumr371q3r44Yf1m9/8Rj/4wQ909913a/z48Zufs379eu266641/d4AAABgH5uoAAAA6DfRaHSLD4PqKx/60IdULpf12muv6VOf+pSr1y1evHirxw866CAddNBBOv/883XSSSfp1ltv3byJms/ntXz5cn3oQx/qs/oBAABgS9DrAgAAALDjeu9736s//vGPevnll7Vu3brNV5L21kEHHaSTTz5ZEydO1P3336+XXnpJbW1tmjlzph5++OFtvm7s2LF68sknN/9/LpfT5MmT9dhjj+mVV17RU089pYULF+qQQw7Z/JxnnnlGsVhMDQ0NfVI7AAAA7GETFQAAAP3mwgsvVCgU0vDhw7Xrrrtq5cqVfXbsW2+9VRMnTtQFF1yggw8+WOPGjdPChQu19957b/M1J598shYtWqRly5ZJkkKhkN544w1NnDhRBx10kI4//nh9/vOf12WXXbb5NXfddZdOPvlkJZPJPqsdAAAAtgSq1WrV6yIAAACA7eWiiy5Se3u7fvGLX7zrc9etW6eDDz5Yzz77rPbdd9/tUB0AAAD8iCtRAQAAsFO55JJLtM8++/To1gIvv/yyrrvuOjZQAQAAdnJciQoAAAAAAAAA3eBKVAAAAAAAAADoBpuoAAAAAAAAANANNlEBAAAAAAAAoBtsogIAAAAAAABAN9hEBQAAAAAAAIBusIkKAAAAAAAAAN1gExUAAAAAAAAAusEmKgAAAAAAAAB0g01UAAAAAAAAAOjG/wf8nSBcVv355AAAAABJRU5ErkJggg==\n", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "draw_timeline(naive_timeline, \"Naive\", 15)" ] }, { "cell_type": "code", "execution_count": 25, "metadata": { "execution": { "iopub.execute_input": "2022-12-14T22:10:12.983972Z", "iopub.status.busy": "2022-12-14T22:10:12.983726Z", "iopub.status.idle": "2022-12-14T22:10:13.503920Z", "shell.execute_reply": "2022-12-14T22:10:13.503291Z" }, "id": "DoovY7qr-jNR" }, "outputs": [ { "data": { "image/png": "iVBORw0KGgoAAAANSUhEUgAABVEAAAI7CAYAAADlKNthAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjYuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8o6BhiAAAACXBIWXMAAA9hAAAPYQGoP6dpAAB5OElEQVR4nO3dfXzVdf3/8efZ2fV2zoRQU5gXmMqlFynkvCQ1MTUCdZRhEw3TilCHF1/IEvkalNVIWmaFGWRfrakgfrMwSE1yNtS+asDIUGSblCCws4tzfc7vD38uJjD3OWfj83nB43677VY7Z58PLzyP7XZ8+3l/5kun02kBAAAAAAAAAPYox+0BAAAAAAAAAMDLWEQFAAAAAAAAgB6wiAoAAAAAAAAAPWARFQAAAAAAAAB6wCIqAAAAAAAAAPSARVQAAAAAAAAA6AGLqAAAAAAAAADQAxZRAQAAAAAAAKAHLKICAAAAAAAAQA9YRAUAAEBWfvnLX8rn82nTpk19ds45c+bI5/P12fl6y+fzac6cOfv8zwUAAIC3sYgKAACwH1q7dq2uvPJKDR48WAUFBTr88MM1ZcoUrV27NuNzzps3T8uWLeu7IQEAAAAjfOl0Ou32EAAAAOg7jz32mK644goNHDhQX/rSl3T00Udr06ZNuv/++/Xuu+/q4Ycf1qRJkxyft7S0VJdffrl++ctfdns8mUwqHo+roKCgz64eTSQSSiQSKiws7JPz9ZbP59Mdd9zB1agAAADoJtftAQAAANB3Nm7cqC9+8YsaOnSo/vznP+vggw/ueu6GG27QWWedpS9+8Yt69dVXNXTo0D75M/1+v/x+f5+c6325ubnKzeWtKgAAALyB7fwAAAD7ke9973vq7OzUz372s24LqJI0aNAg/fSnP1VHR4fuvvtuSf+592hjY6MmT56sYDCoj3zkI7rhhhsUiUS6jvX5fOro6NDixYvl8/nk8/k0depUSXu+J+pRRx2lSy65RM8884xOPfVUFRUVafTo0XrmmWckvXe17OjRo1VYWKhTTjlFf/vb37rN+sF7ok6dOrXrz/3gx65XjUajUd1xxx362Mc+poKCApWXl+vWW29VNBrtdv5oNKqbbrpJBx98sAKBgCZMmKDm5uZM/7EDAABgP8d/3gcAANiPPPHEEzrqqKN01lln7fH5s88+W0cddZR+97vfdXt88uTJOuqoozR//ny98MILWrhwoXbs2KElS5ZIkn71q19p2rRpGjt2rL785S9Lko455pgeZ/nnP/+pL3zhC7ruuut05ZVX6vvf/74+85nP6L777tPs2bP11a9+VZI0f/58TZ48WRs2bFBOzp7/G/91112n888/v9tjf/jDH/TrX/9ahxxyiCQplUppwoQJWr16tb785S9r+PDheu2117RgwQL94x//6HY/12nTpunBBx/UF77wBZ1++un605/+pIsvvrjHvw8AAAAOXCyiAgAA7CdaW1v19ttv67Of/WyPX3fCCSdo+fLlamtr63rs6KOP1uOPPy5J+trXvqZgMKh7771XN998s0444QRdeeWVuv766zV06FBdeeWVvZpnw4YNev7551VRUSFJGjFihMaPH69rr71WjY2NOuKIIyRJAwYM0HXXXac///nPGjdu3B7PVVFR0XUe6b0F2unTp+tTn/qUrrvuOknS//zP/2jlypV69tlndeaZZ3Z97ahRo3T99dfr+eef1+mnn65XXnlFDz74oL761a/qxz/+cdffecqUKXr11Vd79XcDAADAgYXt/AAAAPuJ9xdFA4FAj1/3/vOhUKjrsa997WvdvubrX/+6JOnJJ5/MeJ4RI0Z0W/j8xCc+IUk699xzuxZQd338jTfe6NV5Ozo6NGnSJA0YMEAPPfRQ1/1Y6+rqNHz4cA0bNkzbtm3r+jj33HMlSU8//XS3v9OMGTO6nffGG2/M4G8JAACAAwFXogIAAOwn3l8c3fUK0z3Z02Lrscce2+1rjjnmGOXk5HS7z6lTuy6USlJZWZkkqby8fI+P79ixo1fnvfbaa7Vx40Y9//zz+shHPtL1+Ouvv67169fvdi/Y973zzjuSpLfeeks5OTm73Y7g+OOP79WfDwAAgAMPi6gAAAD7ibKyMh122GEfuiX91Vdf1eDBgxUMBvf6Nbv+UqdMvX+FaG8fT6fTH3rOe+65Rw899JAefPBBnXTSSd2eS6VSGj16tGpqavZ47AcXbwEAAIDeYhEVAABgP3LJJZfo5z//uVavXt3tvqDve+6557Rp06au+4i+7/XXX9fRRx/d9fk///lPpVIpHXXUUV2P9cXCajaee+453Xzzzbrxxhs1ZcqU3Z4/5phj9Morr+i8887rcdYjjzxSqVRKGzdu7Hb16YYNG/plbgAAANjHPVEBAAD2I7fc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c/SFL3xBv/vd7/T73/9ed9xxhx5++GFNmjSp62u2b9+ugw8+OKO/GwAAAOxjERUAAAD9Jj8/v9svg+orJ598spLJpN555x2dddZZjo5bt27dbo8fd9xxOu6443TTTTfpiiuu0AMPPNC1iBqJRLRx40adfPLJfTY/AAAAbMlxewAAAADsv4466ij99a9/1aZNm7Rt27auK0mzddxxx2nKlCmqqqrSY489pjfffFMNDQ2aP3++fve73+31uPHjx2v16tVdn4fDYU2fPl3PPPOM3nrrLf3lL3/RmjVrNHz48K6veeGFF1RQUKCKioo+mR0AAAD2sIgKAACAfnPzzTfL7/drxIgROvjgg7V58+Y+O/cDDzygqqoqzZw5U8cff7wmTpyoNWvW6IgjjtjrMVOmTNHatWu1YcMGSZLf79e7776rqqoqHXfccZo8ebI+/elP68477+w65qGHHtKUKVNUXFzcZ7MDAADAFl86nU67PQQAAACwr9xyyy0KhUL66U9/+qFfu23bNh1//PF68cUXdfTRR++D6QAAAOBFXIkKAACAA8o3vvENHXnkkb26tcCmTZt07733soAKAABwgONKVAAAAAAAAADoAVeiAgAAAAAAAEAPWEQFAAAAAAAAgB6wiAoAAAAAAAAAPWARFQAAAAAAAAB6wCIqAAAAAAAAAPSARVQAAAAAAAAA6AGLqAAAAAAAAADQAxZRAQAAAAAAAKAHLKICAAAAAAAAQA/+HyqwYwVRrF83AAAAAElFTkSuQmCC\n", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "draw_timeline(optimized_timeline, \"Optimized\", 15)" ] } ], "metadata": { "colab": { "collapsed_sections": [], "name": "data_performance.ipynb", "toc_visible": true }, "kernelspec": { "display_name": "Python 3", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.9.16" } }, "nbformat": 4, "nbformat_minor": 0 }