{ "cells": [ { "cell_type": "markdown", "metadata": { "id": "wJcYs_ERTnnI" }, "source": [ "##### Copyright 2021 The TensorFlow Authors." ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "cellView": "form", "execution": { "iopub.execute_input": "2022-12-14T20:23:13.978589Z", "iopub.status.busy": "2022-12-14T20:23:13.978068Z", "iopub.status.idle": "2022-12-14T20:23:13.981748Z", "shell.execute_reply": "2022-12-14T20:23:13.981219Z" }, "id": "HMUDt0CiUJk9" }, "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": "77z2OchJTk0l" }, "source": [ "# SessionRunHook을 Keras 콜백으로 마이그레이션하기\n", "\n", "\n", " \n", " \n", " \n", " \n", "
TensorFlow.org에서보기 Google Colab에서 실행하기\n", "GitHub에서 소스 보기 노트북 다운로드하기
\n" ] }, { "cell_type": "markdown", "metadata": { "id": "KZHPY55aFyXT" }, "source": [ "TensorFlow 1에서 훈련 동작을 사용자 정의하려면 `tf.estimator.Estimator`와 함께 `tf.estimator.SessionRunHook`를 사용해야 합니다. 이 가이드는 `tf.keras.callbacks.Callback` API를 사용하여 `SessionRunHook`에서 TensorFlow 2의 사용자 정의 콜백으로 마이그레이션하는 방법을 설명합니다. 이 API는 훈련용 Keras `Model.fit`(`Model.evaluate` 및 `Model.predict`도 동일)와 함께 작동합니다. 훈련을 진행하는 동안 초당 예제를 측정하는 `SessionRunHook` 및 `Callback` 작업을 구현하여 이를 수행하는 방법을 배우게 됩니다.\n", "\n", "콜백의 예제로는 체크포인트 저장(`tf.keras.callbacks.ModelCheckpoint`) 및 [TensorBoard](%60tf.keras.callbacks.TensorBoard%60) 요약문 작성이 있습니다. Keras [콜백](https://www.tensorflow.org/guide/keras/custom_callback)은 내장된 Keras `Model.fit`/`Model.evaluate`/`Model.predict` API에서 훈련/평가/예측을 진행하는 동안 서로 다른 지점에서 호출되는 객체입니다. 콜백에 대한 자세한 내용은 `tf.keras.callbacks.Callback` API 문서와 [직접 콜백 작성하기](https://www.tensorflow.org/guide/keras/custom_callback.ipynb/) 및 [내장 메서드를 사용하여 훈련하고 평가하기](https://www.tensorflow.org/guide/keras/train_and_evaluate)(*콜백 사용하기* 섹션) 가이드를 참조하세요." ] }, { "cell_type": "markdown", "metadata": { "id": "29da56bf859d" }, "source": [ "## 설치하기\n", "\n", "데모를 위해 가져오기 및 간단한 데이터세트로 시작해 보겠습니다." ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "execution": { "iopub.execute_input": "2022-12-14T20:23:13.985303Z", "iopub.status.busy": "2022-12-14T20:23:13.984774Z", "iopub.status.idle": "2022-12-14T20:23:15.896340Z", "shell.execute_reply": "2022-12-14T20:23:15.895598Z" }, "id": "296d8b0DoKpV" }, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "2022-12-14 20:23:14.924051: 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 20:23:14.924145: 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 20:23:14.924155: 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", "import tensorflow.compat.v1 as tf1\n", "\n", "import time\n", "from datetime import datetime\n", "from absl import flags" ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "execution": { "iopub.execute_input": "2022-12-14T20:23:15.900469Z", "iopub.status.busy": "2022-12-14T20:23:15.900064Z", "iopub.status.idle": "2022-12-14T20:23:15.904344Z", "shell.execute_reply": "2022-12-14T20:23:15.903722Z" }, "id": "xVGYtUXyXNuE" }, "outputs": [], "source": [ "features = [[1., 1.5], [2., 2.5], [3., 3.5]]\n", "labels = [[0.3], [0.5], [0.7]]\n", "eval_features = [[4., 4.5], [5., 5.5], [6., 6.5]]\n", "eval_labels = [[0.8], [0.9], [1.]]" ] }, { "cell_type": "markdown", "metadata": { "id": "ON4zQifT0Vec" }, "source": [ "## TensorFlow 1: tf.estimator API로 사용자 정의 SessionRunHook 생성하기\n", "\n", "다음 TensorFlow 1 예제는 훈련을 진행하는 동안 초당 예제를 측정하는 사용자 정의 `SessionRunHook`를 설정하는 방법을 보여줍니다. 후크(`LoggerHook`)를 생성한 후 `tf.estimator.Estimator.train`의 `hooks` 매개변수에 전달합니다." ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "execution": { "iopub.execute_input": "2022-12-14T20:23:15.907694Z", "iopub.status.busy": "2022-12-14T20:23:15.907312Z", "iopub.status.idle": "2022-12-14T20:23:15.911798Z", "shell.execute_reply": "2022-12-14T20:23:15.911232Z" }, "id": "S-myEclbXUL7" }, "outputs": [], "source": [ "def _input_fn():\n", " return tf1.data.Dataset.from_tensor_slices(\n", " (features, labels)).batch(1).repeat(100)\n", "\n", "def _model_fn(features, labels, mode):\n", " logits = tf1.layers.Dense(1)(features)\n", " loss = tf1.losses.mean_squared_error(labels=labels, predictions=logits)\n", " optimizer = tf1.train.AdagradOptimizer(0.05)\n", " train_op = optimizer.minimize(loss, global_step=tf1.train.get_global_step())\n", " return tf1.estimator.EstimatorSpec(mode, loss=loss, train_op=train_op)" ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "execution": { "iopub.execute_input": "2022-12-14T20:23:15.914958Z", "iopub.status.busy": "2022-12-14T20:23:15.914447Z", "iopub.status.idle": "2022-12-14T20:23:20.496861Z", "shell.execute_reply": "2022-12-14T20:23:20.496239Z" }, "id": "Xd9sPTkO0ZTD" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Using default config.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "WARNING:tensorflow:Using temporary folder as model directory: /tmpfs/tmp/tmpi0__a12q\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Using config: {'_model_dir': '/tmpfs/tmp/tmpi0__a12q', '_tf_random_seed': None, '_save_summary_steps': 100, '_save_checkpoints_steps': None, '_save_checkpoints_secs': 600, '_session_config': allow_soft_placement: true\n", "graph_options {\n", " rewrite_options {\n", " meta_optimizer_iterations: ONE\n", " }\n", "}\n", ", '_keep_checkpoint_max': 5, '_keep_checkpoint_every_n_hours': 10000, '_log_step_count_steps': 100, '_train_distribute': None, '_device_fn': None, '_protocol': None, '_eval_distribute': None, '_experimental_distribute': None, '_experimental_max_worker_delay_secs': None, '_session_creation_timeout_secs': 7200, '_checkpoint_save_graph_def': True, '_service': None, '_cluster_spec': ClusterSpec({}), '_task_type': 'worker', '_task_id': 0, '_global_id_in_cluster': 0, '_master': '', '_evaluation_master': '', '_is_chief': True, '_num_ps_replicas': 0, '_num_worker_replicas': 1}\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "WARNING:tensorflow:From /tmpfs/src/tf_docs_env/lib/python3.9/site-packages/tensorflow/python/training/training_util.py:396: Variable.initialized_value (from tensorflow.python.ops.variables) is deprecated and will be removed in a future version.\n", "Instructions for updating:\n", "Use Variable.read_value. Variables in 2.X are initialized automatically both in eager and graph (inside tf.defun) contexts.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Calling model_fn.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "WARNING:tensorflow:From /tmpfs/src/tf_docs_env/lib/python3.9/site-packages/tensorflow/python/training/adagrad.py:138: calling Constant.__init__ (from tensorflow.python.ops.init_ops) with dtype is deprecated and will be removed in a future version.\n", "Instructions for updating:\n", "Call initializer instance with the dtype argument instead of passing it to the constructor\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Done calling model_fn.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Create CheckpointSaverHook.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Graph was finalized.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Running local_init_op.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Done running local_init_op.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Calling checkpoint listeners before saving checkpoint 0...\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Saving checkpoints for 0 into /tmpfs/tmp/tmpi0__a12q/model.ckpt.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Calling checkpoint listeners after saving checkpoint 0...\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Time: 2022-12-14 20:23:19.989508 , Step #: 0 , Examples per second: 2.6514216127414776\n", "INFO:tensorflow:loss = 1.8147948, step = 0\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Time: 2022-12-14 20:23:20.022854 , Step #: 10 , Examples per second: 299.86730725234503\n", "Time: 2022-12-14 20:23:20.029819 , Step #: 20 , Examples per second: 1435.7171219278428\n", "Time: 2022-12-14 20:23:20.036575 , Step #: 30 , Examples per second: 1479.8899160256863\n", "Time: 2022-12-14 20:23:20.043157 , Step #: 40 , Examples per second: 1519.345069912338\n", "Time: 2022-12-14 20:23:20.049921 , Step #: 50 , Examples per second: 1478.2730060268566\n", "Time: 2022-12-14 20:23:20.056636 , Step #: 60 , Examples per second: 1489.2958846713773\n", "Time: 2022-12-14 20:23:20.063276 , Step #: 70 , Examples per second: 1505.9256067786873\n", "Time: 2022-12-14 20:23:20.069780 , Step #: 80 , Examples per second: 1537.5578283661425\n", "Time: 2022-12-14 20:23:20.076458 , Step #: 90 , Examples per second: 1497.5378463296202\n", "INFO:tensorflow:global_step/sec: 1055.75\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Time: 2022-12-14 20:23:20.084892 , Step #: 100 , Examples per second: 1185.6689752650177\n", "INFO:tensorflow:loss = 1.4855434e-05, step = 100 (0.095 sec)\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Time: 2022-12-14 20:23:20.092704 , Step #: 110 , Examples per second: 1280.0390636921293\n", "Time: 2022-12-14 20:23:20.099669 , Step #: 120 , Examples per second: 1435.7662684421318\n", "Time: 2022-12-14 20:23:20.106356 , Step #: 130 , Examples per second: 1495.5621322873953\n", "Time: 2022-12-14 20:23:20.113093 , Step #: 140 , Examples per second: 1484.289050888244\n", "Time: 2022-12-14 20:23:20.119825 , Step #: 150 , Examples per second: 1485.4455305284034\n", "Time: 2022-12-14 20:23:20.126593 , Step #: 160 , Examples per second: 1477.491897985064\n", "Time: 2022-12-14 20:23:20.133065 , Step #: 170 , Examples per second: 1545.2617617802011\n", "Time: 2022-12-14 20:23:20.139650 , Step #: 180 , Examples per second: 1518.410020634978\n", "Time: 2022-12-14 20:23:20.146197 , Step #: 190 , Examples per second: 1527.6456876456878\n", "INFO:tensorflow:global_step/sec: 1437.75\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Time: 2022-12-14 20:23:20.154350 , Step #: 200 , Examples per second: 1226.4405391970527\n", "INFO:tensorflow:loss = 0.00070948945, step = 200 (0.069 sec)\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Time: 2022-12-14 20:23:20.161802 , Step #: 210 , Examples per second: 1341.9196314307653\n", "Time: 2022-12-14 20:23:20.168527 , Step #: 220 , Examples per second: 1487.0254555768277\n", "Time: 2022-12-14 20:23:20.175179 , Step #: 230 , Examples per second: 1503.388651923008\n", "Time: 2022-12-14 20:23:20.181607 , Step #: 240 , Examples per second: 1555.4622659002412\n", "Time: 2022-12-14 20:23:20.188128 , Step #: 250 , Examples per second: 1533.7345961165759\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Time: 2022-12-14 20:23:20.195322 , Step #: 260 , Examples per second: 1389.9009179176194\n", "Time: 2022-12-14 20:23:20.201964 , Step #: 270 , Examples per second: 1505.709362435382\n", "Time: 2022-12-14 20:23:20.208528 , Step #: 280 , Examples per second: 1523.4287374691269\n", "Time: 2022-12-14 20:23:20.214937 , Step #: 290 , Examples per second: 1560.1487873828298\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Calling checkpoint listeners before saving checkpoint 300...\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Saving checkpoints for 300 into /tmpfs/tmp/tmpi0__a12q/model.ckpt.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Calling checkpoint listeners after saving checkpoint 300...\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Loss for final step: 0.0005846145.\n" ] }, { "data": { "text/plain": [ "" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "class LoggerHook(tf1.train.SessionRunHook):\n", " \"\"\"Logs loss and runtime.\"\"\"\n", "\n", " def begin(self):\n", " self._step = -1\n", " self._start_time = time.time()\n", " self.log_frequency = 10\n", "\n", " def before_run(self, run_context):\n", " self._step += 1\n", "\n", " def after_run(self, run_context, run_values):\n", " if self._step % self.log_frequency == 0:\n", " current_time = time.time()\n", " duration = current_time - self._start_time\n", " self._start_time = current_time\n", " examples_per_sec = self.log_frequency / duration\n", " print('Time:', datetime.now(), ', Step #:', self._step,\n", " ', Examples per second:', examples_per_sec)\n", "\n", "estimator = tf1.estimator.Estimator(model_fn=_model_fn)\n", "\n", "# Begin training.\n", "estimator.train(_input_fn, hooks=[LoggerHook()])" ] }, { "cell_type": "markdown", "metadata": { "id": "3uZCDMrM2CEg" }, "source": [ "## TensorFlow 2: Model.fit용 사용자 정의 Keras 콜백 생성하기\n", "\n", "TensorFlow 2에서 훈련/평가에 내장 Keras `Model.fit`(또는 `Model.evaluate`)를 사용하는 경우 사용자 정의`tf. keras.callbacks.Callback`을 구성한 후 이를 `Model.fit`(또는 `Model.evaluate`)의 `callbacks` 매개변수로 전달합니다(자세한 내용은 [직접 콜백 작성하기](../..guide/keras/custom_callback.ipynb) 가이드에서 확인).\n", "\n", "아래 예제에서는 다양한 메트릭을 기록하는 사용자 정의 `tf.keras.callbacks.Callback`을 작성합니다. 초당 예제를 측정하며 이전 `SessionRunHook`의 메트릭과 비교할 수 있어야 합니다." ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "execution": { "iopub.execute_input": "2022-12-14T20:23:20.500521Z", "iopub.status.busy": "2022-12-14T20:23:20.499912Z", "iopub.status.idle": "2022-12-14T20:23:21.640382Z", "shell.execute_reply": "2022-12-14T20:23:21.639706Z" }, "id": "UbMPoiB92KRG" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Time: 2022-12-14 20:23:21.131505 , Step #: 0 , Examples per second: 20.585601724471335\n", "Time: 2022-12-14 20:23:21.150332 , Step #: 10 , Examples per second: 531.072450555851\n", "Time: 2022-12-14 20:23:21.166234 , Step #: 20 , Examples per second: 628.8311844077961\n", "Time: 2022-12-14 20:23:21.182892 , Step #: 30 , Examples per second: 600.3269068372765\n", "Time: 2022-12-14 20:23:21.199825 , Step #: 40 , Examples per second: 590.5557354659758\n", "Time: 2022-12-14 20:23:21.216653 , Step #: 50 , Examples per second: 594.2287203898901\n", "Time: 2022-12-14 20:23:21.233343 , Step #: 60 , Examples per second: 599.1777260324852\n", "Time: 2022-12-14 20:23:21.249616 , Step #: 70 , Examples per second: 614.5140211562692\n", "Time: 2022-12-14 20:23:21.266385 , Step #: 80 , Examples per second: 596.3239308462238\n", "Time: 2022-12-14 20:23:21.283419 , Step #: 90 , Examples per second: 587.0675344670726\n", "Time: 2022-12-14 20:23:21.299863 , Step #: 100 , Examples per second: 608.1257340041467\n", "Time: 2022-12-14 20:23:21.315915 , Step #: 110 , Examples per second: 622.9657794676806\n", "Time: 2022-12-14 20:23:21.332126 , Step #: 120 , Examples per second: 616.8638409271406\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Time: 2022-12-14 20:23:21.348881 , Step #: 130 , Examples per second: 596.8500441130433\n", "Time: 2022-12-14 20:23:21.366014 , Step #: 140 , Examples per second: 583.685272547628\n", "Time: 2022-12-14 20:23:21.382683 , Step #: 150 , Examples per second: 599.8804330725554\n", "Time: 2022-12-14 20:23:21.398955 , Step #: 160 , Examples per second: 614.5680459500645\n", "Time: 2022-12-14 20:23:21.415907 , Step #: 170 , Examples per second: 589.9161744022504\n", "Time: 2022-12-14 20:23:21.432503 , Step #: 180 , Examples per second: 602.5260012641498\n", "Time: 2022-12-14 20:23:21.448798 , Step #: 190 , Examples per second: 613.6688710715749\n", "Time: 2022-12-14 20:23:21.464718 , Step #: 200 , Examples per second: 628.1625256473619\n", "Time: 2022-12-14 20:23:21.481145 , Step #: 210 , Examples per second: 608.7700658945107\n", "Time: 2022-12-14 20:23:21.497145 , Step #: 220 , Examples per second: 624.9707950888068\n", "Time: 2022-12-14 20:23:21.512982 , Step #: 230 , Examples per second: 631.4345502446369\n", "Time: 2022-12-14 20:23:21.529601 , Step #: 240 , Examples per second: 601.7480129694987\n", "Time: 2022-12-14 20:23:21.545946 , Step #: 250 , Examples per second: 611.8248388131983\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Time: 2022-12-14 20:23:21.563054 , Step #: 260 , Examples per second: 584.5068145711977\n", "Time: 2022-12-14 20:23:21.580755 , Step #: 270 , Examples per second: 564.9426881995608\n", "Time: 2022-12-14 20:23:21.597789 , Step #: 280 , Examples per second: 587.0428843354607\n", "Time: 2022-12-14 20:23:21.614728 , Step #: 290 , Examples per second: 590.372862270392\n" ] }, { "data": { "text/plain": [ "{'loss': [0.3040516972541809]}" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "class CustomCallback(tf.keras.callbacks.Callback):\n", "\n", " def on_train_begin(self, logs = None):\n", " self._step = -1\n", " self._start_time = time.time()\n", " self.log_frequency = 10\n", "\n", " def on_train_batch_begin(self, batch, logs = None):\n", " self._step += 1\n", "\n", " def on_train_batch_end(self, batch, logs = None):\n", " if self._step % self.log_frequency == 0:\n", " current_time = time.time()\n", " duration = current_time - self._start_time\n", " self._start_time = current_time\n", " examples_per_sec = self.log_frequency / duration\n", " print('Time:', datetime.now(), ', Step #:', self._step,\n", " ', Examples per second:', examples_per_sec)\n", "\n", "callback = CustomCallback()\n", "\n", "dataset = tf.data.Dataset.from_tensor_slices(\n", " (features, labels)).batch(1).repeat(100)\n", "\n", "model = tf.keras.models.Sequential([tf.keras.layers.Dense(1)])\n", "optimizer = tf.keras.optimizers.Adagrad(learning_rate=0.05)\n", "\n", "model.compile(optimizer, \"mse\")\n", "\n", "# Begin training.\n", "result = model.fit(dataset, callbacks=[callback], verbose = 0)\n", "# Provide the results of training metrics.\n", "result.history" ] }, { "cell_type": "markdown", "metadata": { "id": "EFqFi21Ftskq" }, "source": [ "## 다음 단계\n", "\n", "콜백에 대한 자세한 내용:\n", "\n", "- API 문서: `tf.keras.callbacks.Callback`\n", "- 가이드: [직접 콜백 작성하기](../..guide/keras/custom_callback.ipynb/)\n", "- 가이드: [내장 메서드를 사용하여 훈련하고 평가하기](https://www.tensorflow.org/guide/keras/train_and_evaluate)(*콜백 사용하기* 섹션)\n", "\n", "다음과 같은 마이그레이션 관련 리소스도 유용할 수 있습니다.\n", "\n", "- [조기 중단 마이그레이션 가이드](early_stopping.ipynb): 내장 조기 중단 콜백인 `tf.keras.callbacks.EarlyStopping`\n", "- [TensorBoard 마이그레이션 가이드](tensorboard.ipynb): TensorBoard를 사용하여 메트릭 추적 및 표시하기\n", "- [LoggingTensorHook 및 StopAtStepHook에서 Keras 콜백으로의 마이그레이션 가이드](logging_stop_hook.ipynb)" ] } ], "metadata": { "colab": { "collapsed_sections": [], "name": "sessionrunhook_callback.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 }