{ "cells": [ { "cell_type": "markdown", "metadata": { "id": "1Z6Wtb_jisbA" }, "source": [ "##### Copyright 2019 The TensorFlow Authors." ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "cellView": "form", "execution": { "iopub.execute_input": "2022-12-14T22:31:11.183641Z", "iopub.status.busy": "2022-12-14T22:31:11.182992Z", "iopub.status.idle": "2022-12-14T22:31:11.187620Z", "shell.execute_reply": "2022-12-14T22:31:11.187013Z" }, "id": "QUyRGn9riopB" }, "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": "H1yCdGFW4j_F" }, "source": [ "# 사전 제작 Estimator" ] }, { "cell_type": "markdown", "metadata": { "id": "PS6_yKSoyLAl" }, "source": [ "\n", " \n", " \n", " \n", " \n", "
TensorFlow.org에서 보기Google Colab에서 실행GitHub에서 소그 보기노트북 다운론드하기
" ] }, { "cell_type": "markdown", "metadata": { "id": "stQiPWL6ni6_" }, "source": [ "> 경고: Estimator는 새 코드에 권장되지 않습니다. Estimator는 `v1.Session` 스타일 코드를 실행하는데, 이 코드는 올바르게 작성하기가 좀 더 어렵고 특히 TF 2 코드와 결합할 경우 예기치 않게 작동할 수 있습니다. Estimator는 [호환성 보장](https://tensorflow.org/guide/versions)이 적용되지만 보안 취약점 외에는 수정 사항이 제공되지 않습니다. 자세한 내용은 [마이그레이션 가이드](https://tensorflow.org/guide/migrate)를 참조하세요." ] }, { "cell_type": "markdown", "metadata": { "id": "R4YZ_ievcY7p" }, "source": [ "이 튜토리얼은 Estimator를 사용하여 TensorFlow의 홍채 분류 문제를 해결하는 방법을 보여줍니다. Estimator는 완전한 모델에 대한 기존 TensorFlow의 고차원적 표현입니다. 자세한 내용은 [Estimator](https://www.tensorflow.org/guide/estimator)를 참조하세요.\n", "\n", "참고: TensorFlow 2.0에서 [Keras API](https://www.tensorflow.org/guide/keras)는 이와 동일한 작업을 수행할 수 있으며 배우기 더 쉬운 API로 여겨집니다. 새로 시작하는 경우 Keras로 시작하는 것이 좋습니다.\n" ] }, { "cell_type": "markdown", "metadata": { "id": "8IFct0yedsTy" }, "source": [ "## 시작을 위한 준비\n", "\n", "시작하려면 먼저 TensorFlow와 필요한 여러 라이브러리를 가져옵니다." ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "execution": { "iopub.execute_input": "2022-12-14T22:31:11.191867Z", "iopub.status.busy": "2022-12-14T22:31:11.191284Z", "iopub.status.idle": "2022-12-14T22:31:13.299397Z", "shell.execute_reply": "2022-12-14T22:31:13.298599Z" }, "id": "jPo5bQwndr9P" }, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "2022-12-14 22:31:12.214526: 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:31:12.214616: 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:31:12.214626: 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 pandas as pd" ] }, { "cell_type": "markdown", "metadata": { "id": "c5w4m5gncnGh" }, "source": [ "## 데이터세트\n", "\n", "이 문서의 샘플 프로그램은 아이리스 꽃을 [꽃받침잎](https://en.wikipedia.org/wiki/Sepal)과 [꽃잎](https://en.wikipedia.org/wiki/Petal)의 크기에 따라 세 가지 종으로 분류하는 모델을 빌드하고 테스트합니다.\n", "\n", "Iris 데이터세트를 사용하여 모델을 훈련합니다. Iris 데이터세트에는 네 가지 특성과 하나의 [레이블](https://developers.google.com/machine-learning/glossary/#label)이 있습니다. 이 네 가지 특성은 개별 아이리스 꽃의 다음과 같은 식물 특성을 식별합니다.\n", "\n", "- 꽃받침잎 길이\n", "- 꽃받침잎 너비\n", "- 꽃잎 길이\n", "- 꽃잎 너비\n", "\n", "이 정보를 바탕으로 데이터를 구문 분석하는 데 도움이 되는 몇 가지 상수를 정의할 수 있습니다.\n" ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "execution": { "iopub.execute_input": "2022-12-14T22:31:13.304292Z", "iopub.status.busy": "2022-12-14T22:31:13.303393Z", "iopub.status.idle": "2022-12-14T22:31:13.307174Z", "shell.execute_reply": "2022-12-14T22:31:13.306538Z" }, "id": "lSyrXp_He_UE" }, "outputs": [], "source": [ "CSV_COLUMN_NAMES = ['SepalLength', 'SepalWidth', 'PetalLength', 'PetalWidth', 'Species']\n", "SPECIES = ['Setosa', 'Versicolor', 'Virginica']" ] }, { "cell_type": "markdown", "metadata": { "id": "j6mTfIQzfC9w" }, "source": [ "그 다음, Keras 및 Pandas를 사용하여 Iris 데이터세트를 다운로드하고 구문 분석합니다. 훈련 및 테스트를 위해 별도의 데이터세트를 유지합니다." ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "execution": { "iopub.execute_input": "2022-12-14T22:31:13.310808Z", "iopub.status.busy": "2022-12-14T22:31:13.310160Z", "iopub.status.idle": "2022-12-14T22:31:13.397230Z", "shell.execute_reply": "2022-12-14T22:31:13.396335Z" }, "id": "PumyCN8VdGGc" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Downloading data from https://storage.googleapis.com/download.tensorflow.org/data/iris_training.csv\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "\r", "2194/2194 [==============================] - 0s 0us/step\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Downloading data from https://storage.googleapis.com/download.tensorflow.org/data/iris_test.csv\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "\r", "573/573 [==============================] - 0s 0us/step\n" ] } ], "source": [ "train_path = tf.keras.utils.get_file(\n", " \"iris_training.csv\", \"https://storage.googleapis.com/download.tensorflow.org/data/iris_training.csv\")\n", "test_path = tf.keras.utils.get_file(\n", " \"iris_test.csv\", \"https://storage.googleapis.com/download.tensorflow.org/data/iris_test.csv\")\n", "\n", "train = pd.read_csv(train_path, names=CSV_COLUMN_NAMES, header=0)\n", "test = pd.read_csv(test_path, names=CSV_COLUMN_NAMES, header=0)" ] }, { "cell_type": "markdown", "metadata": { "id": "wHFxNLszhQjz" }, "source": [ "데이터를 검사하여 네 개의 float 특성 열과 하나의 int32 레이블이 있는지 확인할 수 있습니다." ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "execution": { "iopub.execute_input": "2022-12-14T22:31:13.401465Z", "iopub.status.busy": "2022-12-14T22:31:13.400889Z", "iopub.status.idle": "2022-12-14T22:31:13.415494Z", "shell.execute_reply": "2022-12-14T22:31:13.414762Z" }, "id": "WOJt-ML4hAwI" }, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
SepalLengthSepalWidthPetalLengthPetalWidthSpecies
06.42.85.62.22
15.02.33.31.01
24.92.54.51.72
34.93.11.50.10
45.73.81.70.30
\n", "
" ], "text/plain": [ " SepalLength SepalWidth PetalLength PetalWidth Species\n", "0 6.4 2.8 5.6 2.2 2\n", "1 5.0 2.3 3.3 1.0 1\n", "2 4.9 2.5 4.5 1.7 2\n", "3 4.9 3.1 1.5 0.1 0\n", "4 5.7 3.8 1.7 0.3 0" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "train.head()" ] }, { "cell_type": "markdown", "metadata": { "id": "jQJEYfVvfznP" }, "source": [ "각 데이터세트에 대해 예측하도록 모델을 훈련할 레이블을 분할합니다." ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "execution": { "iopub.execute_input": "2022-12-14T22:31:13.419402Z", "iopub.status.busy": "2022-12-14T22:31:13.418753Z", "iopub.status.idle": "2022-12-14T22:31:13.429539Z", "shell.execute_reply": "2022-12-14T22:31:13.428820Z" }, "id": "zM0wz2TueuA6" }, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
SepalLengthSepalWidthPetalLengthPetalWidth
06.42.85.62.2
15.02.33.31.0
24.92.54.51.7
34.93.11.50.1
45.73.81.70.3
\n", "
" ], "text/plain": [ " SepalLength SepalWidth PetalLength PetalWidth\n", "0 6.4 2.8 5.6 2.2\n", "1 5.0 2.3 3.3 1.0\n", "2 4.9 2.5 4.5 1.7\n", "3 4.9 3.1 1.5 0.1\n", "4 5.7 3.8 1.7 0.3" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "train_y = train.pop('Species')\n", "test_y = test.pop('Species')\n", "\n", "# The label column has now been removed from the features.\n", "train.head()" ] }, { "cell_type": "markdown", "metadata": { "id": "jZx1L_1Vcmxv" }, "source": [ "## Estimator를 사용한 프로그래밍 개요\n", "\n", "이제 데이터가 설정되었으므로 TensorFlow Estimator를 사용하여 모델을 정의할 수 있습니다. Estimator는 `tf.estimator.Estimator`에서 파생된 클래스입니다. TensorFlow는 일반적인 ML 알고리즘을 구현하기 위해 `tf.estimator`(예: `LinearRegressor`) 모음을 제공합니다. 그 외에도 고유한 [사용자 정의 Estimator](https://www.tensorflow.org/guide/estimator#custom_estimators)를 작성할 수 있습니다. 처음 시작할 때는 미리 만들어진 Estimator를 사용하는 것이 좋습니다.\n", "\n", "사전 제작된 Estimator를 기초로 TensorFlow 프로그램을 작성하려면 다음 작업을 수행해야 합니다.\n", "\n", "- 하나 이상의 입력 함수를 작성합니다.\n", "- 모델의 특성 열을 정의합니다.\n", "- 특성 열과 다양한 하이퍼 매개변수를 지정하여 Estimator를 인스턴스화합니다.\n", "- Estimator 객체에서 하나 이상의 메서드를 호출하여 적합한 입력 함수를 데이터 소스로 전달합니다.\n", "\n", "이러한 작업이 Iris 분류를 위해 어떻게 구현되는지 알아보겠습니다." ] }, { "cell_type": "markdown", "metadata": { "id": "2OcguDfBcmmg" }, "source": [ "## 입력 함수 작성하기\n", "\n", "훈련, 평가 및 예측을 위한 데이터를 제공하려면 입력 함수를 작성해야 합니다.\n", "\n", "**입력 함수**는 다음 두 요소 튜플을 출력하는 `tf.data.Dataset` 객체를 반환하는 함수입니다.\n", "\n", "- [`features`](https://developers.google.com/machine-learning/glossary/#feature) -다음과 같은 Python 사전:\n", " - 각 키가 특성의 이름입니다.\n", " - 각 값은 해당 특성 값을 모두 포함하는 배열입니다.\n", "- `label` - 모든 예제의 [레이블](https://developers.google.com/machine-learning/glossary/#label) 값을 포함하는 배열입니다.\n", "\n", "입력 함수의 형식을 보여주기 위해 여기에 간단한 구현을 나타냈습니다." ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "execution": { "iopub.execute_input": "2022-12-14T22:31:13.433308Z", "iopub.status.busy": "2022-12-14T22:31:13.432854Z", "iopub.status.idle": "2022-12-14T22:31:13.437271Z", "shell.execute_reply": "2022-12-14T22:31:13.436605Z" }, "id": "nzr5vRr5caGF" }, "outputs": [], "source": [ "def input_evaluation_set():\n", " features = {'SepalLength': np.array([6.4, 5.0]),\n", " 'SepalWidth': np.array([2.8, 2.3]),\n", " 'PetalLength': np.array([5.6, 3.3]),\n", " 'PetalWidth': np.array([2.2, 1.0])}\n", " labels = np.array([2, 1])\n", " return features, labels" ] }, { "cell_type": "markdown", "metadata": { "id": "NpXvGjfnjHgY" }, "source": [ "입력 함수는 원하는 방식으로 `features` 사전 및 `label` 목록을 생성할 수 있습니다. 그러나 모든 종류의 데이터를 구문 분석할 수 있는 TensorFlow의 [Dataset API](https://www.tensorflow.org/guide/datasets)를 사용하는 것이 좋습니다.\n", "\n", "Dataset API는 많은 일반적인 경우를 자동으로 처리할 수 있습니다. 예를 들어, Dataset API를 사용하면 대규모 파일 모음에서 레코드를 병렬로 쉽게 읽고 이를 단일 스트림으로 결합할 수 있습니다.\n", "\n", "이 예제에서는 작업을 단순화하기 위해 [pandas](https://pandas.pydata.org/) 데이터를 로드하고 이 인메모리 데이터에서 입력 파이프라인을 빌드합니다.\n" ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "execution": { "iopub.execute_input": "2022-12-14T22:31:13.440881Z", "iopub.status.busy": "2022-12-14T22:31:13.440339Z", "iopub.status.idle": "2022-12-14T22:31:13.444867Z", "shell.execute_reply": "2022-12-14T22:31:13.444187Z" }, "id": "T20u1anCi8NP" }, "outputs": [], "source": [ "def input_fn(features, labels, training=True, batch_size=256):\n", " \"\"\"An input function for training or evaluating\"\"\"\n", " # Convert the inputs to a Dataset.\n", " dataset = tf.data.Dataset.from_tensor_slices((dict(features), labels))\n", "\n", " # Shuffle and repeat if you are in training mode.\n", " if training:\n", " dataset = dataset.shuffle(1000).repeat()\n", " \n", " return dataset.batch(batch_size)\n" ] }, { "cell_type": "markdown", "metadata": { "id": "xIwcFT4MlZEi" }, "source": [ "## 특성 열 정의하기\n", "\n", "[**특성 열**](https://developers.google.com/machine-learning/glossary/#feature_columns)은 모델이 특성 사전의 원시 입력 데이터를 사용하는 방식을 설명하는 객체입니다. Estimator 모델을 빌드할 때는 모델에서 사용할 각 특성을 설명하는 특성 열 목록을 전달합니다. `tf.feature_column` 모듈은 모델에 데이터를 나타내기 위한 많은 옵션을 제공합니다.\n", "\n", "Iris의 경우 4개의 원시 특성이 숫자 값이므로, 네 개의 특성 각각을 32비트 부동 소수점 값으로 나타내도록 Estimator 모델에 알려주는 특성 열 목록을 빌드합니다. 따라서 특성 열을 작성하는 코드는 다음과 같습니다." ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "execution": { "iopub.execute_input": "2022-12-14T22:31:13.448347Z", "iopub.status.busy": "2022-12-14T22:31:13.447830Z", "iopub.status.idle": "2022-12-14T22:31:13.451444Z", "shell.execute_reply": "2022-12-14T22:31:13.450810Z" }, "id": "ZTTriO8FlSML" }, "outputs": [], "source": [ "# Feature columns describe how to use the input.\n", "my_feature_columns = []\n", "for key in train.keys():\n", " my_feature_columns.append(tf.feature_column.numeric_column(key=key))" ] }, { "cell_type": "markdown", "metadata": { "id": "jpKkhMoZljco" }, "source": [ "특성 열은 여기에 나타낸 것보다 훨씬 정교할 수 있습니다. [이 튜토리얼](https://www.tensorflow.org/guide/feature_columns)에서 특성 열에 대한 자세한 내용을 읽을 수 있습니다.\n", "\n", "모델이 원시 특성을 나타내도록 할 방식에 대한 설명이 준비되었으므로 Estimator를 빌드할 수 있습니다." ] }, { "cell_type": "markdown", "metadata": { "id": "kuE59XHEl22K" }, "source": [ "## Estimator 인스턴스화하기\n", "\n", "Iris 문제는 고전적인 분류 문제입니다. 다행히도 TensorFlow는 다음을 포함하여 여러 가지 사전 제작된 분류자 Estimator를 제공합니다.\n", "\n", "- 다중 클래스 분류를 수행하는 심층 모델을 위한 `tf.estimator.DNNClassifier`\n", "- 넓고 깊은 모델을 위한 `tf.estimator.DNNLinearCombinedClassifier`\n", "- 선형 모델에 기초한 분류자를 위한 `tf.estimator.LinearClassifier`\n", "\n", "Iris 문제의 경우 `tf.estimator.DNNClassifier`가 최선의 선택인 것으로 여겨집니다. 이 Estimator를 인스턴스화하는 방법은 다음과 같습니다." ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "execution": { "iopub.execute_input": "2022-12-14T22:31:13.455080Z", "iopub.status.busy": "2022-12-14T22:31:13.454496Z", "iopub.status.idle": "2022-12-14T22:31:17.140868Z", "shell.execute_reply": "2022-12-14T22:31:17.140123Z" }, "id": "qnf4o2V5lcPn" }, "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/tmpqgqcecuu\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Using config: {'_model_dir': '/tmpfs/tmp/tmpqgqcecuu', '_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" ] } ], "source": [ "# Build a DNN with 2 hidden layers with 30 and 10 hidden nodes each.\n", "classifier = tf.estimator.DNNClassifier(\n", " feature_columns=my_feature_columns,\n", " # Two hidden layers of 30 and 10 nodes respectively.\n", " hidden_units=[30, 10],\n", " # The model must choose between 3 classes.\n", " n_classes=3)" ] }, { "cell_type": "markdown", "metadata": { "id": "tzzt5nUpmEe3" }, "source": [ "## 훈련, 평가 및 예측하기\n", "\n", "이제 Estimator 객체가 준비되었으므로 메서드를 호출하여 다음을 수행할 수 있습니다.\n", "\n", "- 모델을 훈련합니다.\n", "- 훈련한 모델을 평가합니다.\n", "- 훈련한 모델을 사용하여 예측을 수행합니다." ] }, { "cell_type": "markdown", "metadata": { "id": "rnihuLdWmE75" }, "source": [ "### 모델 훈련하기\n", "\n", "다음과 같이 Estimator의 `train` 메서드를 호출하여 모델을 훈련합니다." ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "execution": { "iopub.execute_input": "2022-12-14T22:31:17.144855Z", "iopub.status.busy": "2022-12-14T22:31:17.144182Z", "iopub.status.idle": "2022-12-14T22:31:27.327820Z", "shell.execute_reply": "2022-12-14T22:31:27.326879Z" }, "id": "4jW08YtPl1iS" }, "outputs": [ { "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/keras/optimizers/optimizer_v2/adagrad.py:93: 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": "stderr", "output_type": "stream", "text": [ "2022-12-14 22:31:17.844452: W tensorflow/core/common_runtime/type_inference.cc:339] Type inference failed. This indicates an invalid graph that escaped type checking. Error message: INVALID_ARGUMENT: expected compatible input types, but input 1:\n", "type_id: TFT_OPTIONAL\n", "args {\n", " type_id: TFT_PRODUCT\n", " args {\n", " type_id: TFT_TENSOR\n", " args {\n", " type_id: TFT_INT64\n", " }\n", " }\n", "}\n", " is neither a subtype nor a supertype of the combined inputs preceding it:\n", "type_id: TFT_OPTIONAL\n", "args {\n", " type_id: TFT_PRODUCT\n", " args {\n", " type_id: TFT_TENSOR\n", " args {\n", " type_id: TFT_INT32\n", " }\n", " }\n", "}\n", "\n", "\twhile inferring type of node 'dnn/zero_fraction/cond/output/_18'\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/tmpqgqcecuu/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": [ "INFO:tensorflow:loss = 1.3337237, step = 0\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:global_step/sec: 436.985\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:loss = 1.0032561, step = 100 (0.230 sec)\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:global_step/sec: 574.759\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:loss = 0.95959777, step = 200 (0.174 sec)\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:global_step/sec: 567.753\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:loss = 0.9370698, step = 300 (0.176 sec)\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:global_step/sec: 557.205\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:loss = 0.9286161, step = 400 (0.180 sec)\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:global_step/sec: 561.847\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:loss = 0.8905446, step = 500 (0.178 sec)\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:global_step/sec: 577.76\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:loss = 0.8747095, step = 600 (0.173 sec)\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:global_step/sec: 577.063\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:loss = 0.8266728, step = 700 (0.173 sec)\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:global_step/sec: 557.376\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:loss = 0.82472736, step = 800 (0.179 sec)\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:global_step/sec: 574.454\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:loss = 0.80091727, step = 900 (0.174 sec)\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:global_step/sec: 586.222\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:loss = 0.78718907, step = 1000 (0.171 sec)\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:global_step/sec: 577.173\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:loss = 0.7678418, step = 1100 (0.173 sec)\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:global_step/sec: 572.52\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:loss = 0.7386337, step = 1200 (0.175 sec)\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:global_step/sec: 604.218\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:loss = 0.72882044, step = 1300 (0.165 sec)\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:global_step/sec: 592.209\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:loss = 0.6970418, step = 1400 (0.169 sec)\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:global_step/sec: 583.268\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:loss = 0.6894151, step = 1500 (0.171 sec)\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:global_step/sec: 596.389\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:loss = 0.6778809, step = 1600 (0.167 sec)\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:global_step/sec: 608.237\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:loss = 0.6436718, step = 1700 (0.164 sec)\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:global_step/sec: 604.922\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:loss = 0.6392429, step = 1800 (0.165 sec)\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:global_step/sec: 589.543\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:loss = 0.6441903, step = 1900 (0.170 sec)\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:global_step/sec: 597.559\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:loss = 0.60938257, step = 2000 (0.167 sec)\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:global_step/sec: 589.247\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:loss = 0.6022556, step = 2100 (0.170 sec)\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:global_step/sec: 597.602\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:loss = 0.6050482, step = 2200 (0.167 sec)\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:global_step/sec: 603.46\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:loss = 0.5700802, step = 2300 (0.166 sec)\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:global_step/sec: 596.966\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:loss = 0.5673893, step = 2400 (0.168 sec)\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:global_step/sec: 601.571\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:loss = 0.57152855, step = 2500 (0.166 sec)\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:global_step/sec: 611.919\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:loss = 0.5541911, step = 2600 (0.163 sec)\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:global_step/sec: 607.58\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:loss = 0.5345268, step = 2700 (0.165 sec)\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:global_step/sec: 612.626\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:loss = 0.5243232, step = 2800 (0.163 sec)\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:global_step/sec: 603.113\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:loss = 0.51460403, step = 2900 (0.166 sec)\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:global_step/sec: 591.917\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:loss = 0.504513, step = 3000 (0.169 sec)\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:global_step/sec: 621.164\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:loss = 0.49160516, step = 3100 (0.161 sec)\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:global_step/sec: 584.335\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:loss = 0.49130887, step = 3200 (0.171 sec)\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:global_step/sec: 587.232\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:loss = 0.47286582, step = 3300 (0.170 sec)\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:global_step/sec: 581.522\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:loss = 0.4713994, step = 3400 (0.172 sec)\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:global_step/sec: 587.683\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:loss = 0.45537138, step = 3500 (0.170 sec)\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:global_step/sec: 578.348\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:loss = 0.454673, step = 3600 (0.173 sec)\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:global_step/sec: 566.947\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:loss = 0.44462803, step = 3700 (0.177 sec)\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:global_step/sec: 568.836\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:loss = 0.4355138, step = 3800 (0.176 sec)\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:global_step/sec: 552.221\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:loss = 0.4255464, step = 3900 (0.181 sec)\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:global_step/sec: 561.817\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:loss = 0.43385836, step = 4000 (0.178 sec)\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:global_step/sec: 583.059\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:loss = 0.41482735, step = 4100 (0.172 sec)\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:global_step/sec: 548.398\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:loss = 0.4114343, step = 4200 (0.182 sec)\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:global_step/sec: 562.111\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:loss = 0.41115963, step = 4300 (0.178 sec)\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:global_step/sec: 566.882\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:loss = 0.3994022, step = 4400 (0.177 sec)\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:global_step/sec: 563.74\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:loss = 0.39092743, step = 4500 (0.177 sec)\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:global_step/sec: 544.05\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:loss = 0.387455, step = 4600 (0.184 sec)\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:global_step/sec: 530.907\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:loss = 0.37600335, step = 4700 (0.188 sec)\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:global_step/sec: 560.602\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:loss = 0.37626752, step = 4800 (0.178 sec)\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:global_step/sec: 563.058\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:loss = 0.37746906, step = 4900 (0.178 sec)\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Calling checkpoint listeners before saving checkpoint 5000...\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Saving checkpoints for 5000 into /tmpfs/tmp/tmpqgqcecuu/model.ckpt.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Calling checkpoint listeners after saving checkpoint 5000...\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Loss for final step: 0.36055747.\n" ] }, { "data": { "text/plain": [ "" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Train the Model.\n", "classifier.train(\n", " input_fn=lambda: input_fn(train, train_y, training=True),\n", " steps=5000)" ] }, { "cell_type": "markdown", "metadata": { "id": "ybiTFDmlmes8" }, "source": [ "Estimator가 예상한 대로 인수를 사용하지 않는 입력 함수를 제공하면서 인수를 포착하기 위해 [`lambda`](https://docs.python.org/3/tutorial/controlflow.html)에서 `input_fn` 호출을 래핑합니다. `steps` 인수는 여러 훈련 단계를 거친 후에 훈련을 중지하도록 메서드에 지시합니다.\n" ] }, { "cell_type": "markdown", "metadata": { "id": "HNvJLH8hmsdf" }, "source": [ "### 훈련한 모델 평가하기\n", "\n", "모델을 훈련했으므로 성능에 대한 통계를 얻을 수 있습니다. 다음 코드 블록은 테스트 데이터에서 훈련한 모델의 정확도를 평가합니다.\n" ] }, { "cell_type": "code", "execution_count": 12, "metadata": { "execution": { "iopub.execute_input": "2022-12-14T22:31:27.331919Z", "iopub.status.busy": "2022-12-14T22:31:27.331299Z", "iopub.status.idle": "2022-12-14T22:31:28.264968Z", "shell.execute_reply": "2022-12-14T22:31:28.264222Z" }, "id": "A169XuO4mKxF" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Calling model_fn.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Done calling model_fn.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Starting evaluation at 2022-12-14T22:31:27\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Graph was finalized.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Restoring parameters from /tmpfs/tmp/tmpqgqcecuu/model.ckpt-5000\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:Inference Time : 0.51326s\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Finished evaluation at 2022-12-14-22:31:28\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Saving dict for global step 5000: accuracy = 0.96666664, average_loss = 0.39743164, global_step = 5000, loss = 0.39743164\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Saving 'checkpoint_path' summary for global step 5000: /tmpfs/tmp/tmpqgqcecuu/model.ckpt-5000\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "\n", "Test set accuracy: 0.967\n", "\n" ] } ], "source": [ "eval_result = classifier.evaluate(\n", " input_fn=lambda: input_fn(test, test_y, training=False))\n", "\n", "print('\\nTest set accuracy: {accuracy:0.3f}\\n'.format(**eval_result))" ] }, { "cell_type": "markdown", "metadata": { "id": "VnPMP5EHph17" }, "source": [ "`train` 메서드에 대한 호출과 달리 평가할 `steps` 인수를 전달하지 않았습니다. eval에 대한 `input_fn`은 단 하나의 데이터 [epoch](https://developers.google.com/machine-learning/glossary/#epoch)만 생성합니다.\n", "\n", "`eval_result` 사전에는 `average_loss`(샘플당 평균 손실), `loss`(미니 배치당 평균 손실) 및 Estimator의 `global_step` 값(받은 훈련 반복 횟수)도 포함됩니다.\n" ] }, { "cell_type": "markdown", "metadata": { "id": "ur624ibpp52X" }, "source": [ "### 훈련한 모델에서 예측(추론)하기\n", "\n", "우수한 평가 결과를 생성하는 훈련한 모델을 만들었습니다. 이제 훈련한 모델을 사용하여 레이블이 지정되지 않은 일부 측정을 바탕으로 아이리스 꽃의 종을 예측할 수 있습니다. 훈련 및 평가와 마찬가지로 단일 함수 호출을 사용하여 예측합니다." ] }, { "cell_type": "code", "execution_count": 13, "metadata": { "execution": { "iopub.execute_input": "2022-12-14T22:31:28.269075Z", "iopub.status.busy": "2022-12-14T22:31:28.268534Z", "iopub.status.idle": "2022-12-14T22:31:28.273215Z", "shell.execute_reply": "2022-12-14T22:31:28.272605Z" }, "id": "wltc0jpgng38" }, "outputs": [], "source": [ "# Generate predictions from the model\n", "expected = ['Setosa', 'Versicolor', 'Virginica']\n", "predict_x = {\n", " 'SepalLength': [5.1, 5.9, 6.9],\n", " 'SepalWidth': [3.3, 3.0, 3.1],\n", " 'PetalLength': [1.7, 4.2, 5.4],\n", " 'PetalWidth': [0.5, 1.5, 2.1],\n", "}\n", "\n", "def input_fn(features, batch_size=256):\n", " \"\"\"An input function for prediction.\"\"\"\n", " # Convert the inputs to a Dataset without labels.\n", " return tf.data.Dataset.from_tensor_slices(dict(features)).batch(batch_size)\n", "\n", "predictions = classifier.predict(\n", " input_fn=lambda: input_fn(predict_x))" ] }, { "cell_type": "markdown", "metadata": { "id": "JsETKQo0rHvi" }, "source": [ "`predict` 메서드는 Python iterable을 반환하여 각 예제에 대한 예측 결과 사전을 생성합니다. 다음 코드는 몇 가지 예측과 해당 확률을 출력합니다." ] }, { "cell_type": "code", "execution_count": 14, "metadata": { "execution": { "iopub.execute_input": "2022-12-14T22:31:28.276778Z", "iopub.status.busy": "2022-12-14T22:31:28.276282Z", "iopub.status.idle": "2022-12-14T22:31:28.634793Z", "shell.execute_reply": "2022-12-14T22:31:28.633921Z" }, "id": "Efm4mLzkrCxp" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Calling model_fn.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Done calling model_fn.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Graph was finalized.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "INFO:tensorflow:Restoring parameters from /tmpfs/tmp/tmpqgqcecuu/model.ckpt-5000\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": [ "Prediction is \"Setosa\" (85.4%), expected \"Setosa\"\n", "Prediction is \"Versicolor\" (62.1%), expected \"Versicolor\"\n", "Prediction is \"Virginica\" (58.5%), expected \"Virginica\"\n" ] } ], "source": [ "for pred_dict, expec in zip(predictions, expected):\n", " class_id = pred_dict['class_ids'][0]\n", " probability = pred_dict['probabilities'][class_id]\n", "\n", " print('Prediction is \"{}\" ({:.1f}%), expected \"{}\"'.format(\n", " SPECIES[class_id], 100 * probability, expec))" ] } ], "metadata": { "colab": { "collapsed_sections": [], "name": "premade.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 }