{ "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": "2024-01-11T18:54:25.376557Z", "iopub.status.busy": "2024-01-11T18:54:25.375966Z", "iopub.status.idle": "2024-01-11T18:54:25.379615Z", "shell.execute_reply": "2024-01-11T18:54:25.378991Z" }, "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": [ "# 混合精度" ] }, { "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", "混合精度とは、トレーニング中に 16 ビットと 32 ビット浮動小数点型の両方をモデルに使ってモデルのトレーニングを高速化し、使用するメモリを少なくする手法です。数値の安定性を保つためにモデルの特定の部分を 32 ビットで保持することにより、モデルのステップ時間を短縮しつつ、精度などの評価指標は同様にトレーニングすることができます。このガイドでは、Keras 混合精度 API でモデルを高速化する使い方を説明します。この API を使用すると、最新の GPU で 3 倍以上、TPU で 60% 以上パフォーマンスを向上させることができます。" ] }, { "cell_type": "markdown", "metadata": { "id": "3vsYi_bv7gS_" }, "source": [ "現在、殆どのモデルでは 32 ビットのメモリを必要とする float32 dtype が使用されています。しかしながら、代わりに必要とするメモリが 16 ビットの float16 と bfloat16 という 2 つの低精度 dtype があります。最近のアクセラレータは、16 ビットの計算実行専門のハードウェアを備えており、16 ビットの dtype はより高速でメモリから読み取ることができるため、 16 ビットの dtype 演算をより高速に実行できます。\n", "\n", "NVIDIA GPU は float32 よりも float16 で速く演算を実行でき、TPU とサポートしている Intel CPU は float32 よりも bfloat16 で速く演算を実行できます。したがって、これらのデバイスでは低精度の dtype を可能な限り使用することが推奨されます。ただし、変数および一部の計算は、モデルのトレーニングの品質を維持するために、数値的理由から float32 のままにする必要があります。Keras 混合精度 API を使用すると、float16 または bfloat16 と float32 の組み合わせが可能になり、float16 / bfloat16 によるパフォーマンスのメリットと float32 による数値的安定性のメリットの両方を得ることができます。\n", "\n", "注意: このガイドでは、「数値的安定性」という用語は、高精度の dtype ではなく低精度の dtype を使用することによって、モデルの品質がどのように影響を受けるかを指します。これらの dtype のいずれかで実行し、モデルの評価精度やその他のメトリクスが float32 での演算実行と比較して低下する場合、その演算は float16 または bfloat16 で「数値的に不安定」と言えます。" ] }, { "cell_type": "markdown", "metadata": { "id": "MUXex9ctTuDB" }, "source": [ "## セットアップ" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "execution": { "iopub.execute_input": "2024-01-11T18:54:25.382979Z", "iopub.status.busy": "2024-01-11T18:54:25.382773Z", "iopub.status.idle": "2024-01-11T18:54:27.757815Z", "shell.execute_reply": "2024-01-11T18:54:27.757069Z" }, "id": "IqR2PQG4ZaZ0" }, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "2024-01-11 18:54:25.810615: E external/local_xla/xla/stream_executor/cuda/cuda_dnn.cc:9261] Unable to register cuDNN factory: Attempting to register factory for plugin cuDNN when one has already been registered\n", "2024-01-11 18:54:25.810663: E external/local_xla/xla/stream_executor/cuda/cuda_fft.cc:607] Unable to register cuFFT factory: Attempting to register factory for plugin cuFFT when one has already been registered\n", "2024-01-11 18:54:25.812179: E external/local_xla/xla/stream_executor/cuda/cuda_blas.cc:1515] Unable to register cuBLAS factory: Attempting to register factory for plugin cuBLAS when one has already been registered\n" ] } ], "source": [ "import tensorflow as tf\n", "\n", "from tensorflow import keras\n", "from tensorflow.keras import layers\n", "from tensorflow.keras import mixed_precision" ] }, { "cell_type": "markdown", "metadata": { "id": "814VXqdh8Q0r" }, "source": [ "## 対応ハードウェア\n", "\n", "混合精度はほとんどのハードウェアで動作しますが、高速化できるのは最近の NVIDIA GPU、Cloud TPU、または最近の Intel CPU のモデルに限ります。NVIDIA GPU は float16 と float32 を組み合わせた使用に、TPU と Intel CPU は bfloat16 と float32 を組み合わせた使用に対応しています。\n", "\n", "NVIDIA GPUの中でも、コンピューティング機能が 7.0 以上の場合、float16 の行列乗算と畳み込みを加速するテンソルコアと呼ばれる特別なハードウェアユニットを備えているため、混合精度のパフォーマンスが最大になります。古い GPU の場合、混合精度の使用による数学的パフォーマンスは期待できませんが、メモリと帯域幅の節約によって幾分かの高速化は可能です。NVIDIA の [CUDA GPU ウェブページ](https://developer.nvidia.com/cuda-gpus)から、お持ちの GPU のコンピューティング機能を確認できます。混合精度が最も有効な GPU には、RTX GPU、V100、A100 などがあります。\n", "\n", "Intel CPU の間では、第 4 世代 Intel Xeon プロセッサ(コード名「Sapphire Rapids」)に混合精度による最大のパフォーマンスメリットが見られます。これらは、AMX 命令を使って bfloat16 の計算を高速化できるためです(Tensorflow 2.12 以降が必要です)。" ] }, { "cell_type": "markdown", "metadata": { "id": "-q2hisD60F0_" }, "source": [ "注意: このガイドを Google Colab で実行する場合、GPU ランタイムには通常 P100 が接続されています。P100 のコンピューティング能力は 6.0 であるため大幅な高速化は期待されません。CPU ランタイムで実行する場合、このランタイムには AMX を使用しない CPU がある可能性があるため、低速化が発生する可能性があります。\n", "\n", "GPU タイプは以下の方法で確認できます。このコマンドは NVIDIA ドライバがインストールされている場合にのみ存在するため、そうでない場合はエラーが生成されます。" ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "execution": { "iopub.execute_input": "2024-01-11T18:54:27.761981Z", "iopub.status.busy": "2024-01-11T18:54:27.761597Z", "iopub.status.idle": "2024-01-11T18:54:27.955680Z", "shell.execute_reply": "2024-01-11T18:54:27.954866Z" }, "id": "j-Yzg_lfkoa_" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "GPU 0: Tesla T4 (UUID: GPU-a5cd732a-9281-3439-08ab-4a1133335283)\r\n", "GPU 1: Tesla T4 (UUID: GPU-25bf817b-a042-aca4-1e88-497783f147e8)\r\n", "GPU 2: Tesla T4 (UUID: GPU-c1f518b2-13c1-3c07-13e9-0e05033f7c53)\r\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "GPU 3: Tesla T4 (UUID: GPU-5e6dae29-da98-3180-fb9f-4654d5d3b4a6)\r\n" ] } ], "source": [ "!nvidia-smi -L" ] }, { "cell_type": "markdown", "metadata": { "id": "hu_pvZDN0El3" }, "source": [ "すべての Cloud TPU は bfloat16 に対応しています。\n", "\n", "高速化が期待できない CPU、AMX を使用しないその他の x86 CPU、および古い GPU でも、単体テスト、デバッグ、または単に API を試す目的で、混合精度 API の使用は可能です。ただし AMX 命令なしの CPU での mixed_bfloat16 とすべての x86 CPU での mixed_bfloat16 の実行は、大幅に遅くなります。" ] }, { "cell_type": "markdown", "metadata": { "id": "HNOmvumB-orT" }, "source": [ "## dtype ポリシーを設定する" ] }, { "cell_type": "markdown", "metadata": { "id": "54ecYY2Hn16E" }, "source": [ "Keras で混合精度を使用するには、通常 dtype ポリシーと呼ばれる`tf.keras.mixed_precision.Policy` を作成する必要があります。dtype ポリシーは、実行される dtypes レイヤーを指定します。このガイドでは、文字列 `'mixed_float16'` からポリシーを作成し、それをグローバルポリシーとして設定します。これにより、その後に作成されるレイヤーは、float16 と float32 を組み合わせた混合精度を使用します。" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "execution": { "iopub.execute_input": "2024-01-11T18:54:27.960086Z", "iopub.status.busy": "2024-01-11T18:54:27.959317Z", "iopub.status.idle": "2024-01-11T18:54:28.555188Z", "shell.execute_reply": "2024-01-11T18:54:28.554507Z" }, "id": "x3kElPVH-siO" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Mixed precision compatibility check (mixed_float16): OK\n", "Your GPUs will likely run quickly with dtype policy mixed_float16 as they all have compute capability of at least 7.0\n" ] } ], "source": [ "policy = mixed_precision.Policy('mixed_float16')\n", "mixed_precision.set_global_policy(policy)" ] }, { "cell_type": "markdown", "metadata": { "id": "6ids1rT_UM5q" }, "source": [ "つまり、文字列を直接 `set_global_policy` に渡すことができ、この方法は一般的に実践で行われます。" ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "execution": { "iopub.execute_input": "2024-01-11T18:54:28.558548Z", "iopub.status.busy": "2024-01-11T18:54:28.558280Z", "iopub.status.idle": "2024-01-11T18:54:28.561841Z", "shell.execute_reply": "2024-01-11T18:54:28.561147Z" }, "id": "6a8iNFoBUSqR" }, "outputs": [], "source": [ "# Equivalent to the two lines above\n", "mixed_precision.set_global_policy('mixed_float16')" ] }, { "cell_type": "markdown", "metadata": { "id": "oGAMaa0Ho3yk" }, "source": [ "ポリシーは、レイヤーの計算が行われる dtype と、レイヤーの変数の dtype という、レイヤーの 2 つの重要な側面を指定します。上記では、`mixed_float16`ポリシー(`'mixed_float16'`をコンストラクタに渡して作成した`mixed_precision.Policy`)を作成しました。このポリシーでは、レイヤーは float16 計算と float32 変数を使用します。パフォーマンスのために計算は float16 で行いますが、数値を安定させるために変数は float32 を保持する必要があります。ポリシーのプロパティに直接クエリすることが可能です。" ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "execution": { "iopub.execute_input": "2024-01-11T18:54:28.565117Z", "iopub.status.busy": "2024-01-11T18:54:28.564861Z", "iopub.status.idle": "2024-01-11T18:54:28.568728Z", "shell.execute_reply": "2024-01-11T18:54:28.568070Z" }, "id": "GQRbYm4f8p-k" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Compute dtype: float16\n", "Variable dtype: float32\n" ] } ], "source": [ "print('Compute dtype: %s' % policy.compute_dtype)\n", "print('Variable dtype: %s' % policy.variable_dtype)" ] }, { "cell_type": "markdown", "metadata": { "id": "MOFEcna28o4T" }, "source": [ "前述したように、`mixed_float16` ポリシーは、7.0 以上のコンピューティング能力を備えた NVIDIA GPU のパフォーマンスを最も大幅に向上させます。ポリシーは他の GPU や CPU でも実行できますが、パフォーマンスは向上しない可能性があります。TPU と CPU の場合は、代わりに `mixed_bfloat16` ポリシーを使用する必要があります。" ] }, { "cell_type": "markdown", "metadata": { "id": "cAHpt128tVpK" }, "source": [ "## モデルを作成する" ] }, { "cell_type": "markdown", "metadata": { "id": "nB6ujaR8qMAy" }, "source": [ "次に、簡単なモデルを作成してみましょう。非常に小さなトイモデルでは、通常 TensorFlow ランタイムのオーバーヘッドが実行時間を支配し、GPU のパフォーマンス向上がごく僅かになってしまうため、混合精度の恩恵を受けることができません。したがって、GPU を使用する場合には、それぞれが 4096 ユニットの 2 つの大きな`Dense`レイヤーを構築しましょう。" ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "execution": { "iopub.execute_input": "2024-01-11T18:54:28.572189Z", "iopub.status.busy": "2024-01-11T18:54:28.571946Z", "iopub.status.idle": "2024-01-11T18:54:30.265850Z", "shell.execute_reply": "2024-01-11T18:54:30.265102Z" }, "id": "0DQM24hL_14Q" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "The model will run with 4096 units on a GPU\n" ] } ], "source": [ "inputs = keras.Input(shape=(784,), name='digits')\n", "if tf.config.list_physical_devices('GPU'):\n", " print('The model will run with 4096 units on a GPU')\n", " num_units = 4096\n", "else:\n", " # Use fewer units on CPUs so the model finishes in a reasonable amount of time\n", " print('The model will run with 64 units on a CPU')\n", " num_units = 64\n", "dense1 = layers.Dense(num_units, activation='relu', name='dense_1')\n", "x = dense1(inputs)\n", "dense2 = layers.Dense(num_units, activation='relu', name='dense_2')\n", "x = dense2(x)" ] }, { "cell_type": "markdown", "metadata": { "id": "2dezdcqnOXHk" }, "source": [ "各レイヤーにはポリシーがあり、グローバルポリシーをデフォルトで使用します。前にグローバルポリシーを`mixed_float16`と設定したため、各`Dense`レイヤーには`mixed_float16`ポリシーがあります。これによって、密なレイヤーは float16 計算を行い、float32 変数を持ちます。これらはfloat16 計算を行うために入力を float16 にキャストし、その結果、出力は float16 になります。変数は float32 なので、dtype の不一致によるエラーを回避するためにレイヤーを呼び出す際に、 float16 にキャストされます。" ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "execution": { "iopub.execute_input": "2024-01-11T18:54:30.270298Z", "iopub.status.busy": "2024-01-11T18:54:30.269577Z", "iopub.status.idle": "2024-01-11T18:54:30.273719Z", "shell.execute_reply": "2024-01-11T18:54:30.273103Z" }, "id": "kC58MzP4PEcC" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "x.dtype: float16\n", "dense1.kernel.dtype: float32\n" ] } ], "source": [ "print(dense1.dtype_policy)\n", "print('x.dtype: %s' % x.dtype.name)\n", "# 'kernel' is dense1's variable\n", "print('dense1.kernel.dtype: %s' % dense1.kernel.dtype.name)" ] }, { "cell_type": "markdown", "metadata": { "id": "_WAZeqDyqZcb" }, "source": [ "次に、出力予測を作成します。 通常、次のように出力予測を作成できますが、これは float16 で常に数値的に安定しているとは限りません。" ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "execution": { "iopub.execute_input": "2024-01-11T18:54:30.276884Z", "iopub.status.busy": "2024-01-11T18:54:30.276613Z", "iopub.status.idle": "2024-01-11T18:54:30.293130Z", "shell.execute_reply": "2024-01-11T18:54:30.292523Z" }, "id": "ybBq1JDwNIbz" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Outputs dtype: float16\n" ] } ], "source": [ "# INCORRECT: softmax and model output will be float16, when it should be float32\n", "outputs = layers.Dense(10, activation='softmax', name='predictions')(x)\n", "print('Outputs dtype: %s' % outputs.dtype.name)" ] }, { "cell_type": "markdown", "metadata": { "id": "D0gSWxc9NN7q" }, "source": [ "モデル最後の softmax(ソフトマックス)のアクティブ化は、float32 にする必要があります。dtype ポリシーは`mixed_float16`であるため、ソフトマックスのアクティブ化は通常 float16 計算 dtype を持ち、float16 テンソルを出力します。\n", "\n", "これは、高密度(Dense)レイヤーとソフトマックスレイヤーを分離して、ソフトマックスレイヤーに`dtype='float32'`を渡して修正することができます。" ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "execution": { "iopub.execute_input": "2024-01-11T18:54:30.296437Z", "iopub.status.busy": "2024-01-11T18:54:30.295936Z", "iopub.status.idle": "2024-01-11T18:54:30.313729Z", "shell.execute_reply": "2024-01-11T18:54:30.312963Z" }, "id": "IGqCGn4BsODw" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Outputs dtype: float32\n" ] } ], "source": [ "# CORRECT: softmax and model output are float32\n", "x = layers.Dense(10, name='dense_logits')(x)\n", "outputs = layers.Activation('softmax', dtype='float32', name='predictions')(x)\n", "print('Outputs dtype: %s' % outputs.dtype.name)" ] }, { "cell_type": "markdown", "metadata": { "id": "tUdkY_DHsP8i" }, "source": [ "`dtype='float32'`をソフトマックス レイヤー コンストラクタ―に渡すと、レイヤーの dtype ポリシーが`float32`ポリシーにオーバーライドされ、計算を行い、変数を float32 で保持します。同様に、その代わりにレイヤーが常に dtype 引数をポリシーに変換する`dtype=mixed_precision.Policy('float32')`を渡すこともできます。`Activation`レイヤーには変数がないため、ポリシーの変数 dtype は無視されますが、ポリシーの計算 dtype float32 はソフトマックスを適用し、モデル出力は float32 になります。\n", "\n", "モデルの中間で float16 ソフトマックスを追加することは問題ありませんが、モデルの最後のソフトマックスは float32 にする必要があります。 その理由は、ソフトマックスから損失に流れる中間テンソルが float16 または bfloat16 である場合、数値の問題が発生する可能性があるためです。\n", "\n", "float16 計算で数値的に安定しないと思われる場合、`dtype='float32'`を渡して任意のレイヤーの dtype を float32 にオーバーライドできます。ただし、ほとんどのレイヤーは `mixed_float16` または `mixed_bfloat16` で十分な精度があるため、通常は、モデルの最後のレイヤーのみで必要です。\n", "\n", "モデルがソフトマックスで終わらない場合でも、出力は float32 である必要があります。 この特定のモデルでは不要ですが、次のようにしてモデル出力を float32 にキャストすることができます。" ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "execution": { "iopub.execute_input": "2024-01-11T18:54:30.317140Z", "iopub.status.busy": "2024-01-11T18:54:30.316578Z", "iopub.status.idle": "2024-01-11T18:54:30.322135Z", "shell.execute_reply": "2024-01-11T18:54:30.321541Z" }, "id": "dzVAoLI56jR8" }, "outputs": [], "source": [ "# The linear activation is an identity function. So this simply casts 'outputs'\n", "# to float32. In this particular case, 'outputs' is already float32 so this is a\n", "# no-op.\n", "outputs = layers.Activation('linear', dtype='float32')(outputs)" ] }, { "cell_type": "markdown", "metadata": { "id": "tpY4ZP7us5hA" }, "source": [ "次に、モデルを完成させてコンパイルし、入力データを生成します。" ] }, { "cell_type": "code", "execution_count": 12, "metadata": { "execution": { "iopub.execute_input": "2024-01-11T18:54:30.325561Z", "iopub.status.busy": "2024-01-11T18:54:30.324935Z", "iopub.status.idle": "2024-01-11T18:54:30.705189Z", "shell.execute_reply": "2024-01-11T18:54:30.704419Z" }, "id": "g4OT3Z6kqYAL" }, "outputs": [], "source": [ "model = keras.Model(inputs=inputs, outputs=outputs)\n", "model.compile(loss='sparse_categorical_crossentropy',\n", " optimizer=keras.optimizers.RMSprop(),\n", " metrics=['accuracy'])\n", "\n", "(x_train, y_train), (x_test, y_test) = keras.datasets.mnist.load_data()\n", "x_train = x_train.reshape(60000, 784).astype('float32') / 255\n", "x_test = x_test.reshape(10000, 784).astype('float32') / 255" ] }, { "cell_type": "markdown", "metadata": { "id": "0Sm8FJHegVRN" }, "source": [ "この例では、入力データを int8 から float32 にキャストします。255 による除算は CPU で行われ、CPU の float16 演算は float32 演算よりも実行速度が遅いため、float16 にはキャストしません。この場合のパフォーマンスの違いはごくわずかですが、一般に CPU で実行する場合には float32 で入力処理演算を実行する必要があります。各レイヤーが浮動小数点入力を dtype 計算にキャストするため、モデルの最初のレイヤーは、入力を float16 にキャストします。\n", "\n", "モデルの重みの初期値が取得されます。これによって、重みをロードして最初からトレーニングを再開できます。" ] }, { "cell_type": "code", "execution_count": 13, "metadata": { "execution": { "iopub.execute_input": "2024-01-11T18:54:30.709238Z", "iopub.status.busy": "2024-01-11T18:54:30.708956Z", "iopub.status.idle": "2024-01-11T18:54:30.831171Z", "shell.execute_reply": "2024-01-11T18:54:30.830425Z" }, "id": "0UYs-u_DgiA5" }, "outputs": [], "source": [ "initial_weights = model.get_weights()" ] }, { "cell_type": "markdown", "metadata": { "id": "zlqz6eVKs9aU" }, "source": [ "## Model.fit でモデルをトレーニングする\n", "\n", "次に、モデルをトレーニングします。" ] }, { "cell_type": "code", "execution_count": 14, "metadata": { "execution": { "iopub.execute_input": "2024-01-11T18:54:30.835490Z", "iopub.status.busy": "2024-01-11T18:54:30.834855Z", "iopub.status.idle": "2024-01-11T18:54:35.747121Z", "shell.execute_reply": "2024-01-11T18:54:35.746311Z" }, "id": "hxI7-0ewmC0A" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Epoch 1/5\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "\r", "1/6 [====>.........................] - ETA: 7s - loss: 2.3131 - accuracy: 0.0955" ] }, { "name": "stderr", "output_type": "stream", "text": [ "WARNING: All log messages before absl::InitializeLog() is called are written to STDERR\n", "I0000 00:00:1704999272.626079 116647 device_compiler.h:186] Compiled cluster using XLA! This line is logged at most once for the lifetime of the process.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\r", "2/6 [=========>....................] - ETA: 0s - loss: 2.2804 - accuracy: 0.1605" ] }, { "name": "stdout", "output_type": "stream", "text": [ "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\r", "3/6 [==============>...............] - ETA: 0s - loss: 2.7117 - accuracy: 0.2744" ] }, { "name": "stdout", "output_type": "stream", "text": [ "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\r", "4/6 [===================>..........] - ETA: 0s - loss: 2.7067 - accuracy: 0.2541" ] }, { "name": "stdout", "output_type": "stream", "text": [ "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\r", "5/6 [========================>.....] - ETA: 0s - loss: 2.5054 - accuracy: 0.3154" ] }, { "name": "stdout", "output_type": "stream", "text": [ "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\r", "6/6 [==============================] - 2s 133ms/step - loss: 2.3236 - accuracy: 0.3688 - val_loss: 0.9058 - val_accuracy: 0.7247\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Epoch 2/5\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "\r", "1/6 [====>.........................] - ETA: 0s - loss: 0.9434 - accuracy: 0.7095" ] }, { "name": "stdout", "output_type": "stream", "text": [ "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\r", "3/6 [==============>...............] - ETA: 0s - loss: 1.1943 - accuracy: 0.6430" ] }, { "name": "stdout", "output_type": "stream", "text": [ "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\r", "5/6 [========================>.....] - ETA: 0s - loss: 1.1652 - accuracy: 0.6354" ] }, { "name": "stdout", "output_type": "stream", "text": [ "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\r", "6/6 [==============================] - 0s 65ms/step - loss: 1.0910 - accuracy: 0.6608 - val_loss: 0.4745 - val_accuracy: 0.8775\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Epoch 3/5\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "\r", "1/6 [====>.........................] - ETA: 0s - loss: 0.5070 - accuracy: 0.8683" ] }, { "name": "stdout", "output_type": "stream", "text": [ "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\r", "3/6 [==============>...............] - ETA: 0s - loss: 0.5019 - accuracy: 0.8522" ] }, { "name": "stdout", "output_type": "stream", "text": [ "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\r", "5/6 [========================>.....] - ETA: 0s - loss: 0.5287 - accuracy: 0.8330" ] }, { "name": "stdout", "output_type": "stream", "text": [ "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\r", "6/6 [==============================] - 0s 65ms/step - loss: 0.5150 - accuracy: 0.8365 - val_loss: 0.4096 - val_accuracy: 0.8535\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Epoch 4/5\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "\r", "1/6 [====>.........................] - ETA: 0s - loss: 0.4420 - accuracy: 0.8497" ] }, { "name": "stdout", "output_type": "stream", "text": [ "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\r", "3/6 [==============>...............] - ETA: 0s - loss: 0.4252 - accuracy: 0.8556" ] }, { "name": "stdout", "output_type": "stream", "text": [ "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\r", "5/6 [========================>.....] - ETA: 0s - loss: 0.4100 - accuracy: 0.8650" ] }, { "name": "stdout", "output_type": "stream", "text": [ "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\r", "6/6 [==============================] - 0s 66ms/step - loss: 0.4104 - accuracy: 0.8651 - val_loss: 0.4787 - val_accuracy: 0.8520\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Epoch 5/5\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "\r", "1/6 [====>.........................] - ETA: 0s - loss: 0.4818 - accuracy: 0.8522" ] }, { "name": "stdout", "output_type": "stream", "text": [ "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\r", "3/6 [==============>...............] - ETA: 0s - loss: 0.4268 - accuracy: 0.8704" ] }, { "name": "stdout", "output_type": "stream", "text": [ "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\r", "5/6 [========================>.....] - ETA: 0s - loss: 0.3648 - accuracy: 0.8910" ] }, { "name": "stdout", "output_type": "stream", "text": [ "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\r", "6/6 [==============================] - 0s 65ms/step - loss: 0.3500 - accuracy: 0.8956 - val_loss: 0.2303 - val_accuracy: 0.9357\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "313/313 - 1s - loss: 0.2358 - accuracy: 0.9315 - 661ms/epoch - 2ms/step\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Test loss: 0.23582278192043304\n", "Test accuracy: 0.9315000176429749\n" ] } ], "source": [ "history = model.fit(x_train, y_train,\n", " batch_size=8192,\n", " epochs=5,\n", " validation_split=0.2)\n", "test_scores = model.evaluate(x_test, y_test, verbose=2)\n", "print('Test loss:', test_scores[0])\n", "print('Test accuracy:', test_scores[1])\n" ] }, { "cell_type": "markdown", "metadata": { "id": "MPhJ9OPWt4x5" }, "source": [ "モデルはステップあたりの時間をログに出力します(例:「25ms/step」)。TensorFlow はモデルの最適化にある程度の時間を費やすため、最初のエポックは遅くなる可能性がありますが、その後はステップあたりの時間が安定するはずです。\n", "\n", "このガイドを Colab で実行している場合は、混合精度と float32 のパフォーマンスの比較ができます。これを行うには、「dtype ポリシーを設定する」のセクションに従ってポリシーを`mixed_float16` から `float32` に変更し、この時点までのすべてのセルを再実行します。コンピューティング機能が 7.X の GPU では、ステップあたりの時間が大幅に増加し、混合精度がモデルを高速化していることが分かるはずです。ガイドを続行する前に、必ずポリシーを `mixed_float16` に戻し、セルを再実行してください。\n", "\n", "コンピューティング機能が 8.0 以上の GPU(Ampere GPU 以上)では、混合精度を使った場合、 float32 に比べ、このガイドのトイモデルにおけるパフォーマンスの改善は見られません。これは、[TensorFloat-32](https://www.tensorflow.org/api_docs/python/tf/config/experimental/enable_tensor_float_32_execution) の使用に起因するもので、特定の float32 演算で `tf.linalg.matmul` などのより精度の低い算術を自動的に使用するためです。TensorFloat-32 は float32 を使用した場合に混合精度のパフォーマンスメリットを提供しますが、実世界モデルでは、メモリ帯域幅の節約と TensorFloat-32 がサポートしない演算により、通常は混同精度からの大幅なパフォーマンスの改善が見られます。\n", "\n", "TPU で混合精度を実行している場合は、GPU で実行する場合に比べそれほどパフォーマンスのゲインは見られません。これは、TPU が、デフォルトの dtype ポリシーが float32 であっても内部的には bfloat16 で特定の演算を行うためです。これは Ampere GPU が TensorFloat-32 をデフォルトで使用する方法に似ています。Ampere GPU に比べ、TPU では通常は、実世界モデルの混合精度でパフォーマンスのゲインをあまり得られません。\n", "\n", "float16 テンソルの使用メモリは半分で済むため、実世界の多くのモデルでは、バッチサイズを 2 倍にしてもメモリ不足にならずに混合精度の使用が可能です。ただし、60,000 枚の画像から成る MNIST データセット全体で構成されるバッチは任意の dtype でモデルを実行できるため、これはこのトイモデルには適用されません。" ] }, { "cell_type": "markdown", "metadata": { "id": "mNKMXlCvHgHb" }, "source": [ "## 損失スケーリング\n", "\n", "損失スケーリングは、`tf.keras.Model.fit` が `mixed_float16` ポリシーを使用して自動的に実行し、数値のアンダーフローを回避する手法です。このセクションでは、損失スケーリングをカスタムトレーニングループと使用する方法について説明します。\n", "\n", "注意: `mixed_bfloat16` ポリシーを使用する場合、損失スケーリングを行う必要はありません。" ] }, { "cell_type": "markdown", "metadata": { "id": "1xQX62t2ow0g" }, "source": [ "### アンダーフローとオーバーフロー\n", "\n", "float16 データ型は、float32 と比較するとダイナミックレンジが狭いです。これは、$65504$ を超える値はオーバーフローして無限大になり、$6.0 \\times 10^{-8}$ 未満の値はアンダーフローしてゼロになることを意味します。float32 および bfloat16 はダイナミックレンジがはるかに高いため、オーバーフローとアンダーフローは問題になりません。\n", "\n", "例:" ] }, { "cell_type": "code", "execution_count": 15, "metadata": { "execution": { "iopub.execute_input": "2024-01-11T18:54:35.750796Z", "iopub.status.busy": "2024-01-11T18:54:35.750512Z", "iopub.status.idle": "2024-01-11T18:54:36.095289Z", "shell.execute_reply": "2024-01-11T18:54:36.094626Z" }, "id": "CHmXRb-yRWbE" }, "outputs": [ { "data": { "text/plain": [ "inf" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "x = tf.constant(256, dtype='float16')\n", "(x ** 2).numpy() # Overflow" ] }, { "cell_type": "code", "execution_count": 16, "metadata": { "execution": { "iopub.execute_input": "2024-01-11T18:54:36.098582Z", "iopub.status.busy": "2024-01-11T18:54:36.098315Z", "iopub.status.idle": "2024-01-11T18:54:36.103782Z", "shell.execute_reply": "2024-01-11T18:54:36.103152Z" }, "id": "5unZLhN0RfQM" }, "outputs": [ { "data": { "text/plain": [ "0.0" ] }, "execution_count": 16, "metadata": {}, "output_type": "execute_result" } ], "source": [ "x = tf.constant(1e-5, dtype='float16')\n", "(x ** 2).numpy() # Underflow" ] }, { "cell_type": "markdown", "metadata": { "id": "pUIbhQypRVe_" }, "source": [ "実際には、float16 によるオーバーフローは滅多に発生しません。また、フォワードパス中にアンダーフローが発生することもほとんどありません。ただし、バックワードパス(逆方向パス)中に、勾配がアンダーフローしてゼロになる可能性があります。損失スケーリングは、このアンダーフローを防ぐための手法です。" ] }, { "cell_type": "markdown", "metadata": { "id": "FAL5qij_oNqJ" }, "source": [ "### 損失スケーリングの概要\n", "\n", "損失スケーリングの基本的概念は単純です。単純に、損失に大きな数値($1024$ など)を掛け、その数値を *損失スケール*と呼びます。これによって、勾配も $1024$ だけスケーリングされ、アンダーフローの可能性が大幅に減少します。最終的な勾配が計算されたら、それを $1024$ で除算して、正しい値に戻します。\n", "\n", "このプロセスの擬似コードは次のようになります。\n", "\n", "```\n", "loss_scale = 1024\n", "loss = model(inputs)\n", "loss *= loss_scale\n", "# Assume `grads` are float32. You do not want to divide float16 gradients.\n", "grads = compute_gradient(loss, model.trainable_variables)\n", "grads /= loss_scale\n", "```\n", "\n", "損失スケールの選択は難しい場合があります。損失スケールが低すぎると、勾配はアンダーフローしてゼロになる可能性があります。高すぎると、反対の問題が発生し、勾配がオーバーフローして無限大になる可能性があります。\n", "\n", "これを解決するために、TensorFlow は動的に損失スケールを決定します。手動で選択する必要はありません。`tf.keras.Model.fit` を使用すると損失スケーリングが行われるため、追加の作業を行う必要はありません。またカスタムトレーニングループを使用する場合は、損失スケーリングを使用するために、特別なオプティマイザラッパーである `tf.keras.mixed_precision.LossScaleOptimizer` を明示的に使用する必要があります。これについては、次のセクションで詳しく説明します。\n" ] }, { "cell_type": "markdown", "metadata": { "id": "yqzbn8Ks9Q98" }, "source": [ "## カスタムトレーニングループでモデルをトレーニングする" ] }, { "cell_type": "markdown", "metadata": { "id": "CRANRZZ69nA7" }, "source": [ "これまでに、`tf.keras.Model.fit`を使用し、混合精度で Keras モデルをトレーニングしました。次は、カスタムトレーニングループで混合精度を使用します。カスタムトレーニングループについてまだ知らない方は、まず[カスタムトレーニングガイド](../tutorials/customization/custom_training_walkthrough.ipynb)をお読みください。" ] }, { "cell_type": "markdown", "metadata": { "id": "wXTaM8EEyEuo" }, "source": [ "混合精度でカスタムトレーニングループを実行するには、float32 のみで実行する場合に比べ、2 つの変更が必要です。\n", "\n", "1. 混合精度でモデルを構築する(既に構築済み)\n", "2. `mixed_float16`が使用されている場合は、明示的に損失スケーリングを使用する\n" ] }, { "cell_type": "markdown", "metadata": { "id": "M2zpp7_65mTZ" }, "source": [ "手順 (2) では、`tf.keras.mixed_precision.LossScaleOptimizer` クラスを使用し、オプティマイザをラップして損失スケーリングを適用します。デフォルトでは、損失スケールが動的に決定されるようになっているため、何も選択する必要はありません。次のようにして、これにはオプティマイザと損失スケールの 2 つの引数が必要です。`LossScaleOptimizer` は次のようにして作成します。" ] }, { "cell_type": "code", "execution_count": 17, "metadata": { "execution": { "iopub.execute_input": "2024-01-11T18:54:36.107493Z", "iopub.status.busy": "2024-01-11T18:54:36.106947Z", "iopub.status.idle": "2024-01-11T18:54:36.112466Z", "shell.execute_reply": "2024-01-11T18:54:36.111902Z" }, "id": "ogZN3rIH0vpj" }, "outputs": [], "source": [ "optimizer = keras.optimizers.RMSprop()\n", "optimizer = mixed_precision.LossScaleOptimizer(optimizer)" ] }, { "cell_type": "markdown", "metadata": { "id": "FVy5gnBqTE9z" }, "source": [ "必要であれば、明示的な損失スケールを選択するか、損失スケーリングの動作をカスタマイズすることもできますが、すべての既知のモデルで十分に動作することがわかっているため、損失スケーリングのデフォルトの動作を維持することを強くお勧めします。損失スケーリングの動作をカスタマイズする場合は、`tf.keras.mixed_precision.LossScaleOptimizer` ドキュメントをご覧ください。" ] }, { "cell_type": "markdown", "metadata": { "id": "JZYEr5hA3MXZ" }, "source": [ "次に、損失オブジェクトと `tf.data.Dataset` を定義します。" ] }, { "cell_type": "code", "execution_count": 18, "metadata": { "execution": { "iopub.execute_input": "2024-01-11T18:54:36.116104Z", "iopub.status.busy": "2024-01-11T18:54:36.115432Z", "iopub.status.idle": "2024-01-11T18:54:36.622150Z", "shell.execute_reply": "2024-01-11T18:54:36.621410Z" }, "id": "9cE7Mm533hxe" }, "outputs": [], "source": [ "loss_object = tf.keras.losses.SparseCategoricalCrossentropy()\n", "train_dataset = (tf.data.Dataset.from_tensor_slices((x_train, y_train))\n", " .shuffle(10000).batch(8192))\n", "test_dataset = tf.data.Dataset.from_tensor_slices((x_test, y_test)).batch(8192)" ] }, { "cell_type": "markdown", "metadata": { "id": "4W0zxrxC3nww" }, "source": [ "次に、トレーニングステップ関数を定義します。 損失をスケーリングし、勾配をスケーリング解除するために、損失スケールオプティマイザの 2 つの新しいメソッドを使用します。\n", "\n", "- `get_scaled_loss(loss)`: 損失スケールで損失を乗算する\n", "- `get_unscaled_gradients(gradients)`: スケーリングされた勾配のリストを入力として取り込み、それぞれを損失スケールで除算してスケーリング解除する\n", "\n", "これらの関数は、勾配のアンダーフローを防ぐために使用する必要があります。勾配に Inf や NaN がなければ、`LossScaleOptimizer.apply_gradients`がそれらを適用します。 損失スケールも更新されるので、勾配に Inf または NaN があった場合は半分に、そうでない場合は高くなる可能性もあります。" ] }, { "cell_type": "code", "execution_count": 19, "metadata": { "execution": { "iopub.execute_input": "2024-01-11T18:54:36.626463Z", "iopub.status.busy": "2024-01-11T18:54:36.625819Z", "iopub.status.idle": "2024-01-11T18:54:36.630626Z", "shell.execute_reply": "2024-01-11T18:54:36.629959Z" }, "id": "V0vHlust4Rug" }, "outputs": [], "source": [ "@tf.function\n", "def train_step(x, y):\n", " with tf.GradientTape() as tape:\n", " predictions = model(x)\n", " loss = loss_object(y, predictions)\n", " scaled_loss = optimizer.get_scaled_loss(loss)\n", " scaled_gradients = tape.gradient(scaled_loss, model.trainable_variables)\n", " gradients = optimizer.get_unscaled_gradients(scaled_gradients)\n", " optimizer.apply_gradients(zip(gradients, model.trainable_variables))\n", " return loss" ] }, { "cell_type": "markdown", "metadata": { "id": "rcFxEjia6YPQ" }, "source": [ "`LossScaleOptimizer`は、トレーニングの開始時に最初の数ステップを省略する可能性があります。最適な損失スケールを素早く決定するために、最初の損失スケールは高めです。いくらかのステップを踏むと、損失スケールが安定化し、省略されるステップが大幅に少なくなります。 このプロセスは自動的に行われ、トレーニングの品質に影響はありません。" ] }, { "cell_type": "markdown", "metadata": { "id": "IHIvKKhg4Y-G" }, "source": [ "次に、テストステップを定義します。\n" ] }, { "cell_type": "code", "execution_count": 20, "metadata": { "execution": { "iopub.execute_input": "2024-01-11T18:54:36.634095Z", "iopub.status.busy": "2024-01-11T18:54:36.633574Z", "iopub.status.idle": "2024-01-11T18:54:36.636862Z", "shell.execute_reply": "2024-01-11T18:54:36.636296Z" }, "id": "nyk_xiZf42Tt" }, "outputs": [], "source": [ "@tf.function\n", "def test_step(x):\n", " return model(x, training=False)" ] }, { "cell_type": "markdown", "metadata": { "id": "hBs98MZyhBOB" }, "source": [ "モデルの初期の重み値を読み込み、最初から再トレーニングできるようにします。" ] }, { "cell_type": "code", "execution_count": 21, "metadata": { "execution": { "iopub.execute_input": "2024-01-11T18:54:36.640179Z", "iopub.status.busy": "2024-01-11T18:54:36.639644Z", "iopub.status.idle": "2024-01-11T18:54:36.771593Z", "shell.execute_reply": "2024-01-11T18:54:36.770882Z" }, "id": "jpzOe3WEhFUJ" }, "outputs": [], "source": [ "model.set_weights(initial_weights)" ] }, { "cell_type": "markdown", "metadata": { "id": "s9Pi1ADM47Ud" }, "source": [ "最後に、カスタムトレーニングループを実行します。" ] }, { "cell_type": "code", "execution_count": 22, "metadata": { "execution": { "iopub.execute_input": "2024-01-11T18:54:36.775494Z", "iopub.status.busy": "2024-01-11T18:54:36.775019Z", "iopub.status.idle": "2024-01-11T18:54:40.553076Z", "shell.execute_reply": "2024-01-11T18:54:40.552295Z" }, "id": "N274tJ3e4_6t" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Epoch 0: loss=1.8230167627334595, test accuracy=0.5349000096321106\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Epoch 1: loss=0.8649435639381409, test accuracy=0.7617999911308289\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Epoch 2: loss=0.43499863147735596, test accuracy=0.8953999876976013\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Epoch 3: loss=0.42511460185050964, test accuracy=0.9345999956130981\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Epoch 4: loss=0.25523805618286133, test accuracy=0.8705999851226807\n" ] } ], "source": [ "for epoch in range(5):\n", " epoch_loss_avg = tf.keras.metrics.Mean()\n", " test_accuracy = tf.keras.metrics.SparseCategoricalAccuracy(\n", " name='test_accuracy')\n", " for x, y in train_dataset:\n", " loss = train_step(x, y)\n", " epoch_loss_avg(loss)\n", " for x, y in test_dataset:\n", " predictions = test_step(x)\n", " test_accuracy.update_state(y, predictions)\n", " print('Epoch {}: loss={}, test accuracy={}'.format(epoch, epoch_loss_avg.result(), test_accuracy.result()))" ] }, { "cell_type": "markdown", "metadata": { "id": "d7daQKGerOFE" }, "source": [ "## GPU パフォーマンスに関するヒント\n", "\n", "GPU で混合精度を使用する場合のパフォーマンスに関するヒントをいくつか紹介します。\n", "\n", "### バッチサイズを大きくする\n", "\n", "モデルの品質に影響がない場合は、混合精度の使用時にバッチサイズを 2 倍にして実行してみてください。float16 テンソルは半分のメモリを使用するため、これにより多くの場合はメモリを使い果たすことなくバッチサイズを 2 倍にすることができます。バッチサイズを増やすと、通常はトレーニングスループット、すなわちモデルで実行できる 1 秒あたりのトレーニング要素が増加します。\n", "\n", "### GPU のテンソルコアを使用する\n", "\n", "前述したように、最近の NVIDIA GPU は、float16 行列を非常に速く乗算できる Tensor Core と呼ばれる特殊なハードウェアユニットを使用しています。ただしテンソルコアは、テンソルの特定の次元を 8 の倍数にする必要があります。以下の例では、テンソルコアを使用するために引数を 8 の倍数にする必要がある部分のみ、引数を太字で表示しています。\n", "\n", "- tf.keras.layers.Dense(**units=64**)\n", "- tf.keras.layers.Conv2d(**filters=48**, kernel_size=7, stride=3)\n", " - tf.keras.layers.Conv3d など、他の畳み込みレイヤーについても同様\n", "- tf.keras.layers.LSTM(**units=64**)\n", " - tf.keras.layers.GRU など、他の RNN についても同様\n", "- tf.keras.Model.fit(epochs=2, **batch_size=128**)\n", "\n", "可能な場合はできる限りテンソルコアを使用するようにしてください。詳細については、[NVIDIA ディープラーニングパフォーマンスガイド(英語)](https://docs.nvidia.com/deeplearning/sdk/dl-performance-guide/index.html)において、テンソルコア使用のための正確な要件、その他テンソルコア関連のパフォーマンス情報などが記載されています。\n", "\n", "### XLA\n", "\n", "XLA は、混合精度のパフォーマンスをさらに向上させるだけでなく、比較的規模は小さいですが float32 のパフォーマンスもさらに向上させることができるコンパイラです。詳しくは、[XLA ガイド](https://www.tensorflow.org/xla)をご覧ください。" ] }, { "cell_type": "markdown", "metadata": { "id": "2tFDX8fm6o_3" }, "source": [ "## Cloud TPU パフォーマンスに関するヒント\n", "\n", "GPU と同様に、bfloat16 テンソルは半分のメモリを使用するため、Cloud TPU を使用する場合はバッチサイズを 2 倍にしてみてください。バッチサイズを 2 倍にすると、トレーニングのスループットが向上する場合があります。\n", "\n", "TPU は、パフォーマンスの最適化に他の混合精度固有の調整は必要ありません。TPU は既に XLA の使用が必須です。$128$ の倍数という特定の次元を持っていることが有効で、これは混合精度の場合と同様に float32 にも同じように適用されます。混合精度と float32 テンソルに適用される一般的な TPU パフォーマンスに関するヒントについては、[Cloud TPU パフォーマンスガイド](https://cloud.google.com/tpu/docs/performance-guide)をご覧ください。" ] }, { "cell_type": "markdown", "metadata": { "id": "--wSEU91wO9w" }, "source": [ "## まとめ\n", "\n", "- TPU、コンピューティング能力が 7.0 以上の NVIDIA GPU、または AMX 命令がサポートされた Intel CPUを使用する場合は、3 倍以上のパフォーマンス改善を得られるように、混合精度を使用することをお勧めします。\n", "\n", "- 混合精度は次の行で使用できます。\n", "\n", " ```python\n", " # On TPUs and CPUs, use 'mixed_bfloat16' instead\n", " mixed_precision.set_global_policy('mixed_float16')\n", " ```\n", "\n", "- モデルが softmax(ソフトマックス)で終了する場合、float32 であることを確認します。また、モデルが何で終わるかを問わず、出力は必ず float32 にします。\n", "- 上記の行に加え、`mixed_float16` を用いたカスタムトレーニングループを使用する場合は、オプティマイザを `tf.keras.mixed_precision.LossScaleOptimizer` でラップする必要があります。その後、`optimizer.get_scaled_loss` を呼び出して損失をスケーリングし、`optimizer.get_unscaled_gradients` を呼び出して勾配をスケーリング解除します。\n", "- `mixed_bfloat16` を使ってカスタムトレーニングループを使用する場合は、上述の global_policy を設定するだけで十分です。\n", "- Double the training batch size if it does not reduce evaluation accuracy\n", "- GPU では、パフォーマンスを最大化するためには、テンソルの次元の大部分を $8$ の倍数にします。\n", "\n", "`tf.keras.mixed_precision` API を使った混合精度の例については、[トレーニングパフォーマンスに関連する関数とクラス](https://github.com/tensorflow/models/blob/master/official/modeling/performance.py)をご覧ください。詳細は、[Transformer](https://github.com/tensorflow/models/blob/master/official/nlp/modeling/layers/transformer_encoder_block.py) などの公式モデルをご覧ください。\n" ] } ], "metadata": { "accelerator": "GPU", "colab": { "collapsed_sections": [], "name": "mixed_precision.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.18" } }, "nbformat": 4, "nbformat_minor": 0 }