{ "cells": [ { "cell_type": "markdown", "metadata": { "id": "tDnwEv8FtJm7" }, "source": [ "##### Copyright 2018 The TensorFlow Authors." ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "cellView": "form", "execution": { "iopub.execute_input": "2022-12-15T02:47:40.657088Z", "iopub.status.busy": "2022-12-15T02:47:40.656561Z", "iopub.status.idle": "2022-12-15T02:47:40.661125Z", "shell.execute_reply": "2022-12-15T02:47:40.660478Z" }, "id": "JlknJBWQtKkI" }, "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": "60RdWsg1tETW" }, "source": [ "# カスタムレイヤー" ] }, { "cell_type": "markdown", "metadata": { "id": "BcJg7Enms86w" }, "source": [ "\n", " \n", " \n", " \n", " \n", "
View on TensorFlow.org Run in Google Colab View source on GitHub Download notebook
" ] }, { "cell_type": "markdown", "metadata": { "id": "UEu3q4jmpKVT" }, "source": [ "ニューラルネットワークの構築には、高レベルの API である `tf.keras` を使うことを推奨しますが、TensorFlow API のほとんどは、eager execution でも使用可能です。\n" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "execution": { "iopub.execute_input": "2022-12-15T02:47:40.665125Z", "iopub.status.busy": "2022-12-15T02:47:40.664583Z", "iopub.status.idle": "2022-12-15T02:47:42.826735Z", "shell.execute_reply": "2022-12-15T02:47:42.825896Z" }, "id": "Py0m-N6VgQFJ" }, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "2022-12-15 02:47:41.730717: 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-15 02:47:41.730827: 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-15 02:47:41.730838: 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" ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "execution": { "iopub.execute_input": "2022-12-15T02:47:42.831395Z", "iopub.status.busy": "2022-12-15T02:47:42.830615Z", "iopub.status.idle": "2022-12-15T02:47:42.958296Z", "shell.execute_reply": "2022-12-15T02:47:42.957556Z" }, "id": "TluWFcB_2nP5" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[PhysicalDevice(name='/physical_device:GPU:0', device_type='GPU'), PhysicalDevice(name='/physical_device:GPU:1', device_type='GPU'), PhysicalDevice(name='/physical_device:GPU:2', device_type='GPU'), PhysicalDevice(name='/physical_device:GPU:3', device_type='GPU')]\n" ] } ], "source": [ "print(tf.config.list_physical_devices('GPU'))" ] }, { "cell_type": "markdown", "metadata": { "id": "zSFfVVjkrrsI" }, "source": [ "## レイヤー:有用な演算の共通セット\n", "\n", "機械学習モデルのコーディングでは、個々の演算やひとつひとつの変数を操作するよりは、より高度に抽象化された演算を実行することが望ましい場合が多くあります。\n", "\n", "多くの機械学習モデルは、比較的単純なレイヤーの合成やスタックとして表現することができますが、TensorFlow には一般的なレイヤーが多数用意されているだけでなく、独自のアプリケーション固有のレイヤーをゼロからまたは既存のレイヤーの合成として記述する簡単な方法が提供されています。\n", "\n", "TensorFlow では、tf.keras パッケージに [Keras](https://keras.io) API のすべてが含まれています。Keras のレイヤーは、独自のモデルを構築する際に大変便利です。\n" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "execution": { "iopub.execute_input": "2022-12-15T02:47:42.961933Z", "iopub.status.busy": "2022-12-15T02:47:42.961657Z", "iopub.status.idle": "2022-12-15T02:47:42.972061Z", "shell.execute_reply": "2022-12-15T02:47:42.971345Z" }, "id": "8PyXlPl-4TzQ" }, "outputs": [], "source": [ "# In the tf.keras.layers package, layers are objects. To construct a layer,\n", "# simply construct the object. Most layers take as a first argument the number\n", "# of output dimensions / channels.\n", "layer = tf.keras.layers.Dense(100)\n", "# The number of input dimensions is often unnecessary, as it can be inferred\n", "# the first time the layer is used, but it can be provided if you want to\n", "# specify it manually, which is useful in some complex models.\n", "layer = tf.keras.layers.Dense(10, input_shape=(None, 5))" ] }, { "cell_type": "markdown", "metadata": { "id": "Fn69xxPO5Psr" }, "source": [ "既存のレイヤーのすべての一覧は、[ドキュメント](https://www.tensorflow.org/api_docs/python/tf/keras/layers)を参照してください。Dense(全結合レイヤー)、Conv2D、LSTM、BatchNormalization、Dropoutなどのたくさんのレイヤーが含まれています。" ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "execution": { "iopub.execute_input": "2022-12-15T02:47:42.975646Z", "iopub.status.busy": "2022-12-15T02:47:42.975019Z", "iopub.status.idle": "2022-12-15T02:47:46.891353Z", "shell.execute_reply": "2022-12-15T02:47:46.890659Z" }, "id": "E3XKNknP5Mhb" }, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# To use a layer, simply call it.\n", "layer(tf.zeros([10, 5]))" ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "execution": { "iopub.execute_input": "2022-12-15T02:47:46.895144Z", "iopub.status.busy": "2022-12-15T02:47:46.894869Z", "iopub.status.idle": "2022-12-15T02:47:46.902213Z", "shell.execute_reply": "2022-12-15T02:47:46.901626Z" }, "id": "Wt_Nsv-L5t2s" }, "outputs": [ { "data": { "text/plain": [ "[,\n", " ]" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Layers have many useful methods. For example, you can inspect all variables\n", "# in a layer using `layer.variables` and trainable variables using\n", "# `layer.trainable_variables`. In this case a fully-connected layer\n", "# will have variables for weights and biases.\n", "layer.variables" ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "execution": { "iopub.execute_input": "2022-12-15T02:47:46.905617Z", "iopub.status.busy": "2022-12-15T02:47:46.905015Z", "iopub.status.idle": "2022-12-15T02:47:46.910835Z", "shell.execute_reply": "2022-12-15T02:47:46.910201Z" }, "id": "6ilvKjz8_4MQ" }, "outputs": [ { "data": { "text/plain": [ "(,\n", " )" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# The variables are also accessible through nice accessors\n", "layer.kernel, layer.bias" ] }, { "cell_type": "markdown", "metadata": { "id": "O0kDbE54-5VS" }, "source": [ "## カスタムレイヤーの実装\n", "\n", "独自のレイヤーを実装する最良の方法は、tf.keras.Layer クラスを拡張し、下記のメソッドを実装することです。\n", "\n", "1. `__init__`:入力に依存しないすべての初期化を実行できます\n", "2. `build`:入力テンソルの形状を知っている場合、残りの初期化を行うことができます。\n", "3. `call`:フォワード計算を行います。\n", "\n", "`build` が呼ばれるまで変数の生成を待つ必要はなく、`__init__` で作成できることに注意してください。しかしながら、`build` で変数を生成することの優位な点は、レイヤーが演算する入力の形状に基づいて、後から定義できる点です。これに対して、`__init__` で変数を生成するには、必要な形状を明示的に指定する必要があります。" ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "execution": { "iopub.execute_input": "2022-12-15T02:47:46.914447Z", "iopub.status.busy": "2022-12-15T02:47:46.913881Z", "iopub.status.idle": "2022-12-15T02:47:46.919949Z", "shell.execute_reply": "2022-12-15T02:47:46.919089Z" }, "id": "5Byl3n1k5kIy" }, "outputs": [], "source": [ "class MyDenseLayer(tf.keras.layers.Layer):\n", " def __init__(self, num_outputs):\n", " super(MyDenseLayer, self).__init__()\n", " self.num_outputs = num_outputs\n", "\n", " def build(self, input_shape):\n", " self.kernel = self.add_weight(\"kernel\",\n", " shape=[int(input_shape[-1]),\n", " self.num_outputs])\n", "\n", " def call(self, inputs):\n", " return tf.matmul(inputs, self.kernel)\n", "\n", "layer = MyDenseLayer(10)" ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "execution": { "iopub.execute_input": "2022-12-15T02:47:46.923530Z", "iopub.status.busy": "2022-12-15T02:47:46.922891Z", "iopub.status.idle": "2022-12-15T02:47:46.929552Z", "shell.execute_reply": "2022-12-15T02:47:46.928906Z" }, "id": "vrmBsYGOnuGO" }, "outputs": [], "source": [ "_ = layer(tf.zeros([10, 5])) # Calling the layer `.builds` it." ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "execution": { "iopub.execute_input": "2022-12-15T02:47:46.933297Z", "iopub.status.busy": "2022-12-15T02:47:46.932717Z", "iopub.status.idle": "2022-12-15T02:47:46.936705Z", "shell.execute_reply": "2022-12-15T02:47:46.936016Z" }, "id": "1bsLjiPfnvat" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "['my_dense_layer/kernel:0']\n" ] } ], "source": [ "print([var.name for var in layer.trainable_variables])" ] }, { "cell_type": "markdown", "metadata": { "id": "tk8E2vY0-z4Z" }, "source": [ "できるだけ標準のレイヤーを使ったほうが、概してコードは読みやすく保守しやすくなります。コードを読む人は標準的なレイヤーの振る舞いに慣れているからです。`tf.keras.layers` にはないレイヤーを使うことを希望する場合には、[github の課題](http://github.com/tensorflow/tensorflow/issues/new)を作成するか、プルリクエスト (推薦) を送ってください。" ] }, { "cell_type": "markdown", "metadata": { "id": "Qhg4KlbKrs3G" }, "source": [ "## モデル:レイヤーの組み合わせ\n", "\n", "機械学習では、多くのレイヤーに類するものが、既存のレイヤーを組み合わせることで実装されています。例えば、ResNet の残差ブロックは、畳込み、バッチ正規化とショートカットの組み合わせです。レイヤーは他のレイヤー内にネストできます。\n", "\n", "通常、`Model.fit`、`Model.evaluate`、および、`Model.save` などのモデルメソッドが必要な場合は、`keras.Model` から継承します。\n", "\n", "`keras.Model`により提供されるもう 1 つの機能(`keras.layers.Layer`の代わりに)として、変数の追跡に加えて、`keras.Model`もその内部レイヤーを追跡し、検査を容易にします。\n", "\n", "たとえば、ResNet ブロックは次のとおりです。" ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "execution": { "iopub.execute_input": "2022-12-15T02:47:46.939949Z", "iopub.status.busy": "2022-12-15T02:47:46.939692Z", "iopub.status.idle": "2022-12-15T02:47:46.960822Z", "shell.execute_reply": "2022-12-15T02:47:46.960087Z" }, "id": "N30DTXiRASlb" }, "outputs": [], "source": [ "class ResnetIdentityBlock(tf.keras.Model):\n", " def __init__(self, kernel_size, filters):\n", " super(ResnetIdentityBlock, self).__init__(name='')\n", " filters1, filters2, filters3 = filters\n", "\n", " self.conv2a = tf.keras.layers.Conv2D(filters1, (1, 1))\n", " self.bn2a = tf.keras.layers.BatchNormalization()\n", "\n", " self.conv2b = tf.keras.layers.Conv2D(filters2, kernel_size, padding='same')\n", " self.bn2b = tf.keras.layers.BatchNormalization()\n", "\n", " self.conv2c = tf.keras.layers.Conv2D(filters3, (1, 1))\n", " self.bn2c = tf.keras.layers.BatchNormalization()\n", "\n", " def call(self, input_tensor, training=False):\n", " x = self.conv2a(input_tensor)\n", " x = self.bn2a(x, training=training)\n", " x = tf.nn.relu(x)\n", "\n", " x = self.conv2b(x)\n", " x = self.bn2b(x, training=training)\n", " x = tf.nn.relu(x)\n", "\n", " x = self.conv2c(x)\n", " x = self.bn2c(x, training=training)\n", "\n", " x += input_tensor\n", " return tf.nn.relu(x)\n", "\n", "\n", "block = ResnetIdentityBlock(1, [1, 2, 3])" ] }, { "cell_type": "code", "execution_count": 12, "metadata": { "execution": { "iopub.execute_input": "2022-12-15T02:47:46.964571Z", "iopub.status.busy": "2022-12-15T02:47:46.964004Z", "iopub.status.idle": "2022-12-15T02:47:47.232674Z", "shell.execute_reply": "2022-12-15T02:47:47.231893Z" }, "id": "7D8ZR5mqtokj" }, "outputs": [], "source": [ "_ = block(tf.zeros([1, 2, 3, 3])) " ] }, { "cell_type": "code", "execution_count": 13, "metadata": { "execution": { "iopub.execute_input": "2022-12-15T02:47:47.236518Z", "iopub.status.busy": "2022-12-15T02:47:47.236244Z", "iopub.status.idle": "2022-12-15T02:47:47.241030Z", "shell.execute_reply": "2022-12-15T02:47:47.240389Z" }, "id": "MJ8rzFpdoE_m" }, "outputs": [ { "data": { "text/plain": [ "[,\n", " ,\n", " ,\n", " ,\n", " ,\n", " ]" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "block.layers" ] }, { "cell_type": "code", "execution_count": 14, "metadata": { "execution": { "iopub.execute_input": "2022-12-15T02:47:47.244115Z", "iopub.status.busy": "2022-12-15T02:47:47.243615Z", "iopub.status.idle": "2022-12-15T02:47:47.248036Z", "shell.execute_reply": "2022-12-15T02:47:47.247382Z" }, "id": "dewldLuDvQRM" }, "outputs": [ { "data": { "text/plain": [ "18" ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ "len(block.variables)" ] }, { "cell_type": "code", "execution_count": 15, "metadata": { "execution": { "iopub.execute_input": "2022-12-15T02:47:47.251277Z", "iopub.status.busy": "2022-12-15T02:47:47.250683Z", "iopub.status.idle": "2022-12-15T02:47:47.267274Z", "shell.execute_reply": "2022-12-15T02:47:47.266632Z" }, "id": "FrqIXeSetaYi" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Model: \"\"\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "_________________________________________________________________\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ " Layer (type) Output Shape Param # \n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "=================================================================\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ " conv2d (Conv2D) multiple 4 \n" ] }, { "name": "stdout", "output_type": "stream", "text": [ " \n" ] }, { "name": "stdout", "output_type": "stream", "text": [ " batch_normalization (BatchN multiple 4 \n" ] }, { "name": "stdout", "output_type": "stream", "text": [ " ormalization) \n" ] }, { "name": "stdout", "output_type": "stream", "text": [ " \n" ] }, { "name": "stdout", "output_type": "stream", "text": [ " conv2d_1 (Conv2D) multiple 4 \n" ] }, { "name": "stdout", "output_type": "stream", "text": [ " \n" ] }, { "name": "stdout", "output_type": "stream", "text": [ " batch_normalization_1 (Batc multiple 8 \n" ] }, { "name": "stdout", "output_type": "stream", "text": [ " hNormalization) \n" ] }, { "name": "stdout", "output_type": "stream", "text": [ " \n" ] }, { "name": "stdout", "output_type": "stream", "text": [ " conv2d_2 (Conv2D) multiple 9 \n" ] }, { "name": "stdout", "output_type": "stream", "text": [ " \n" ] }, { "name": "stdout", "output_type": "stream", "text": [ " batch_normalization_2 (Batc multiple 12 \n" ] }, { "name": "stdout", "output_type": "stream", "text": [ " hNormalization) \n" ] }, { "name": "stdout", "output_type": "stream", "text": [ " \n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "=================================================================\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Total params: 41\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Trainable params: 29\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Non-trainable params: 12\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "_________________________________________________________________\n" ] } ], "source": [ "block.summary()" ] }, { "cell_type": "markdown", "metadata": { "id": "wYfucVw65PMj" }, "source": [ "しかし、ほとんどの場合には、モデルはレイヤーを次々に呼び出すことで構成されます。tf.keras.Sequential クラスを使うことで、これをかなり短いコードで実装できます。" ] }, { "cell_type": "code", "execution_count": 16, "metadata": { "execution": { "iopub.execute_input": "2022-12-15T02:47:47.279293Z", "iopub.status.busy": "2022-12-15T02:47:47.278706Z", "iopub.status.idle": "2022-12-15T02:47:47.355984Z", "shell.execute_reply": "2022-12-15T02:47:47.355160Z" }, "id": "L9frk7Ur4uvJ" }, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 16, "metadata": {}, "output_type": "execute_result" } ], "source": [ "my_seq = tf.keras.Sequential([tf.keras.layers.Conv2D(1, (1, 1),\n", " input_shape=(\n", " None, None, 3)),\n", " tf.keras.layers.BatchNormalization(),\n", " tf.keras.layers.Conv2D(2, 1,\n", " padding='same'),\n", " tf.keras.layers.BatchNormalization(),\n", " tf.keras.layers.Conv2D(3, (1, 1)),\n", " tf.keras.layers.BatchNormalization()])\n", "my_seq(tf.zeros([1, 2, 3, 3]))" ] }, { "cell_type": "code", "execution_count": 17, "metadata": { "execution": { "iopub.execute_input": "2022-12-15T02:47:47.360223Z", "iopub.status.busy": "2022-12-15T02:47:47.359481Z", "iopub.status.idle": "2022-12-15T02:47:47.377534Z", "shell.execute_reply": "2022-12-15T02:47:47.376784Z" }, "id": "tVAsbFITuScB" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Model: \"sequential\"\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "_________________________________________________________________\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ " Layer (type) Output Shape Param # \n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "=================================================================\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ " conv2d_3 (Conv2D) (None, None, None, 1) 4 \n" ] }, { "name": "stdout", "output_type": "stream", "text": [ " \n" ] }, { "name": "stdout", "output_type": "stream", "text": [ " batch_normalization_3 (Batc (None, None, None, 1) 4 \n" ] }, { "name": "stdout", "output_type": "stream", "text": [ " hNormalization) \n" ] }, { "name": "stdout", "output_type": "stream", "text": [ " \n" ] }, { "name": "stdout", "output_type": "stream", "text": [ " conv2d_4 (Conv2D) (None, None, None, 2) 4 \n" ] }, { "name": "stdout", "output_type": "stream", "text": [ " \n" ] }, { "name": "stdout", "output_type": "stream", "text": [ " batch_normalization_4 (Batc (None, None, None, 2) 8 \n" ] }, { "name": "stdout", "output_type": "stream", "text": [ " hNormalization) \n" ] }, { "name": "stdout", "output_type": "stream", "text": [ " \n" ] }, { "name": "stdout", "output_type": "stream", "text": [ " conv2d_5 (Conv2D) (None, None, None, 3) 9 \n" ] }, { "name": "stdout", "output_type": "stream", "text": [ " \n" ] }, { "name": "stdout", "output_type": "stream", "text": [ " batch_normalization_5 (Batc (None, None, None, 3) 12 \n" ] }, { "name": "stdout", "output_type": "stream", "text": [ " hNormalization) \n" ] }, { "name": "stdout", "output_type": "stream", "text": [ " \n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "=================================================================\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Total params: 41\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Trainable params: 29\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Non-trainable params: 12\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "_________________________________________________________________\n" ] } ], "source": [ "my_seq.summary()" ] }, { "cell_type": "markdown", "metadata": { "id": "c5YwYcnuK-wc" }, "source": [ "# 次のステップ\n", "\n", "それでは、前のノートブックに戻り、レイヤーとモデルを使って、線形回帰の例をより構造化された形で実装してみてください。" ] } ], "metadata": { "colab": { "collapsed_sections": [], "name": "custom_layers.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 }