diff --git a/site/ko/beta/guide/saved_model.ipynb b/site/ko/beta/guide/saved_model.ipynb new file mode 100644 index 00000000000..2801015721c --- /dev/null +++ b/site/ko/beta/guide/saved_model.ipynb @@ -0,0 +1,1170 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "6bYaCABobL5q" + }, + "source": [ + "##### Copyright 2018 The TensorFlow Authors." + ] + }, + { + "cell_type": "code", + "execution_count": 0, + "metadata": { + "cellView": "form", + "colab": {}, + "colab_type": "code", + "id": "FlUw7tSKbtg4" + }, + "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": { + "colab_type": "text", + "id": "xc1srSc51n_4" + }, + "source": [ + "# SavedModel 포맷 사용하기" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "-nBUqG2rchGH" + }, + "source": [ + "
\n",
+ " \n",
+ " \n",
+ " TensorFlow.org에서 보기\n",
+ " | \n",
+ " \n",
+ " \n",
+ " \n",
+ " 구글 코랩(Colab)에서 실행하기\n",
+ " | \n",
+ " \n",
+ " \n",
+ " \n",
+ " 깃허브(GitHub) 소스 보기\n",
+ " | \n",
+ "
\n", + "ValueError: Could not find matching function to call for canonicalized inputs ((" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "WrJqD-epPGnr" + }, + "source": [ + "`get_concrete_function`을 사용해 입력 크기를 함수 호출 없이 추가할 수 있습니다. 이 함수는 매개변수 값으로 `Tensor` 대신 입력 크기와 데이터 타입을 나타내는 `tf.TensorSpec` 객체를 받습니다. 크기가 `None`이면 모든 크기가 수용 가능합니다. 또는 각 축의 크기(axis size)를 담은 리스트일 수도 있습니다. 축 크기가 'None'이면 그 축에 대해 임의의 크기를 사용할 수 있습니다. 또한 `tf.TensorSpecs`는 이름을 가질 수 있는데, 기본값은 함수의 매개변수 키워드(여기서는 \"x\")입니다." + ] + }, + { + "cell_type": "code", + "execution_count": 0, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "1m9Okb75PFmb" + }, + "outputs": [], + "source": [ + "module.__call__.get_concrete_function(x=tf.TensorSpec([None], tf.float32))\n", + "tf.saved_model.save(module, \"/tmp/module_no_signatures\")\n", + "imported = tf.saved_model.load(\"/tmp/module_no_signatures\")\n", + "assert [3.] == imported(tf.constant([3.])).numpy()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "gvy3GFl4IfSW" + }, + "source": [ + "`tf.keras.Model`과 `tf.Module`과 같은 객체에 포함된 함수와 변수는 가져올 때 사용할 수 있지만 많은 파이썬의 타입과 속성은 잃어버립니다. 파이썬 프로그램 자체는 SavedModel에 저장되지 않습니다.\n", + "\n", + "내보낼 함수를 시그니처로 지정하지 못했기에 시그니처는 없습니다." + ] + }, + { + "cell_type": "code", + "execution_count": 0, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "uNTV6o_TIeRu" + }, + "outputs": [], + "source": [ + "!saved_model_cli show --dir /tmp/module_no_signatures --tag_set serve" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "BiNtaMZSI8Tb" + }, + "source": [ + "## 내보낼 시그니처 지정하기\n", + "\n", + "어떤 함수가 시그니처라는 것을 나타내려면 저장할 때 `signatures` 매개변수를 지정합니다." + ] + }, + { + "cell_type": "code", + "execution_count": 0, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "_pAdgIORR2yH" + }, + "outputs": [], + "source": [ + "call = module.__call__.get_concrete_function(tf.TensorSpec(None, tf.float32))\n", + "tf.saved_model.save(module, \"/tmp/module_with_signature\", signatures=call)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "lHiBm-kdKBmG" + }, + "source": [ + "먼저 `tf.function` 객체를 `get_concrete_function` 메서드를 사용해 `ConcreteFunction` 객체로 바꾸었습니다. 이것은 함수가 고정된 `input_signature` 없이 만들어지고 함수와 연관된 명시적인 `Tensor` 입력이 없었으므로 필수적입니다." + ] + }, + { + "cell_type": "code", + "execution_count": 0, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "nAzRHR0UT4hv" + }, + "outputs": [], + "source": [ + "!saved_model_cli show --dir /tmp/module_with_signature --tag_set serve --signature_def serving_default" + ] + }, + { + "cell_type": "code", + "execution_count": 0, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "0B25WsscTZoC" + }, + "outputs": [], + "source": [ + "imported = tf.saved_model.load(\"/tmp/module_with_signature\")\n", + "signature = imported.signatures[\"serving_default\"]\n", + "assert [3.] == signature(x=tf.constant([3.]))[\"output_0\"].numpy()\n", + "imported.mutate(tf.constant(2.))\n", + "assert [6.] == signature(x=tf.constant([3.]))[\"output_0\"].numpy()\n", + "assert 2. == imported.v.numpy()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "_gH91j1IR4tq" + }, + "source": [ + "하나의 시그니처를 내보냈고 키는 기본값인 \"serving_default\"가 됩니다. 여러 시그니처를 내보내려면 딕셔너리로 전달합니다." + ] + }, + { + "cell_type": "code", + "execution_count": 0, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "6VYAiQmLUiox" + }, + "outputs": [], + "source": [ + "@tf.function(input_signature=[tf.TensorSpec([], tf.string)])\n", + "def parse_string(string_input):\n", + " return imported(tf.strings.to_number(string_input))\n", + "\n", + "signatures = {\"serving_default\": parse_string,\n", + " \"from_float\": imported.signatures[\"serving_default\"]}\n", + "\n", + "tf.saved_model.save(imported, \"/tmp/module_with_multiple_signatures\", signatures)" + ] + }, + { + "cell_type": "code", + "execution_count": 0, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "8IPx_0RWEx07" + }, + "outputs": [], + "source": [ + "!saved_model_cli show --dir /tmp/module_with_multiple_signatures --tag_set serve" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "sRdSPjKFfQpx" + }, + "source": [ + "`saved_model_cli`는 커맨드 라인에서 SavedModel을 직접 실행할 수도 있습니다." + ] + }, + { + "cell_type": "code", + "execution_count": 0, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "hcHmgVytfODo" + }, + "outputs": [], + "source": [ + "!saved_model_cli run --dir /tmp/module_with_multiple_signatures --tag_set serve --signature_def serving_default --input_exprs=\"string_input='3.'\"\n", + "!saved_model_cli run --dir /tmp/module_with_multiple_signatures --tag_set serve --signature_def from_float --input_exprs=\"x=3.\"" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "WiNhHa_Ne82K" + }, + "source": [ + "## 가져온 모델 미세 튜닝하기\n", + "\n", + "변수 객체가 사용 가능하므로 imported 함수를 통해 역전파할 수 있습니다." + ] + }, + { + "cell_type": "code", + "execution_count": 0, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "mSchcIB2e-n0" + }, + "outputs": [], + "source": [ + "optimizer = tf.optimizers.SGD(0.05)\n", + "\n", + "def train_step():\n", + " with tf.GradientTape() as tape:\n", + " loss = (10. - imported(tf.constant(2.))) ** 2\n", + " variables = tape.watched_variables()\n", + " grads = tape.gradient(loss, variables)\n", + " optimizer.apply_gradients(zip(grads, variables))\n", + " return loss" + ] + }, + { + "cell_type": "code", + "execution_count": 0, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "Yx9bO2taJJxm" + }, + "outputs": [], + "source": [ + "for _ in range(10):\n", + " # \"v\"는 5로 수렴, \"loss\"는 0으로 수렴\n", + " print(\"loss={:.2f} v={:.2f}\".format(train_step(), imported.v.numpy()))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "qyL9tOPrg5Zw" + }, + "source": [ + "## SavedModel의 제어 흐름\n", + "\n", + "`tf.function`에 들어갈 수 있는 것은 모두 SavedModel에 들어갈 수 있습니다. [AutoGraph](./autograph.ipynb)를 사용하면 Tensor에 의존하는 조건부 논리를 파이썬 제어 흐름으로 표현할 수 있습니다." + ] + }, + { + "cell_type": "code", + "execution_count": 0, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "tfbh3uGMgBpH" + }, + "outputs": [], + "source": [ + "@tf.function(input_signature=[tf.TensorSpec([], tf.int32)])\n", + "def control_flow(x):\n", + " if x < 0:\n", + " tf.print(\"유효하지 않음!\")\n", + " else:\n", + " tf.print(x % 3)\n", + "\n", + "to_export = tf.Module()\n", + "to_export.control_flow = control_flow\n", + "tf.saved_model.save(to_export, \"/tmp/control_flow\")" + ] + }, + { + "cell_type": "code", + "execution_count": 0, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "bv4EXevIjHch" + }, + "outputs": [], + "source": [ + "imported = tf.saved_model.load(\"/tmp/control_flow\")\n", + "imported.control_flow(tf.constant(-1)) # 유효하지 않음!\n", + "imported.control_flow(tf.constant(2)) # 2\n", + "imported.control_flow(tf.constant(3)) # 0" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "Dk5wWyuMpuHx" + }, + "source": [ + "## 추정기(Estimator)의 SavedModel\n", + "\n", + "추정기는 [`tf.Estimator.export_saved_model`](https://www.tensorflow.org/api_docs/python/tf/estimator/Estimator#export_saved_model)을 통해 SavedModel을 내보냅니다. 자세한 내용은 [Estimator 가이드](https://www.tensorflow.org/guide/estimators)를 참조하십시오." + ] + }, + { + "cell_type": "code", + "execution_count": 0, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "B9KQq5qzpzbK" + }, + "outputs": [], + "source": [ + "input_column = tf.feature_column.numeric_column(\"x\")\n", + "estimator = tf.estimator.LinearClassifier(feature_columns=[input_column])\n", + "\n", + "def input_fn():\n", + " return tf.data.Dataset.from_tensor_slices(\n", + " ({\"x\": [1., 2., 3., 4.]}, [1, 1, 0, 0])).repeat(200).shuffle(64).batch(16)\n", + "estimator.train(input_fn)\n", + "\n", + "serving_input_fn = tf.estimator.export.build_parsing_serving_input_receiver_fn(\n", + " tf.feature_column.make_parse_example_spec([input_column]))\n", + "export_path = estimator.export_saved_model(\n", + " \"/tmp/from_estimator/\", serving_input_fn)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "XJ4PJ-Cl4060" + }, + "source": [ + "이 SavedModel은 텐서플로 서빙에 배포하는 데 유용한 직렬화된 `tf.Example` 프로토콜 버퍼를 사용합니다. 그러나 `tf.saved_model.load`로 불러오고 파이썬에서 실행할 수도 있습니다." + ] + }, + { + "cell_type": "code", + "execution_count": 0, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "c_BUBBNB1UH9" + }, + "outputs": [], + "source": [ + "imported = tf.saved_model.load(export_path)\n", + "\n", + "def predict(x):\n", + " example = tf.train.Example()\n", + " example.features.feature[\"x\"].float_list.value.extend([x])\n", + " return imported.signatures[\"predict\"](\n", + " examples=tf.constant([example.SerializeToString()]))" + ] + }, + { + "cell_type": "code", + "execution_count": 0, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "C1ylWZCQ1ahG" + }, + "outputs": [], + "source": [ + "print(predict(1.5))\n", + "print(predict(3.5))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "_IrCCm0-isqA" + }, + "source": [ + "`tf.estimator.export.build_server_input_receiver_fn`를 사용해 `tf.train.Example`이 아닌 원시 텐서를 가지는 입력 함수를 만들 수 있습니다." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "Co6fDbzw_UnD" + }, + "source": [ + "## C++에서 SavedModel 불러오기\n", + "\n", + "SavedModel의 C++ 버전 [loader](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/cc/saved_model/loader.h)는 SessionOptions 및 RunOptions을 허용하며 경로에서 SavedModel을 불러오는 API를 제공합니다. 불러 올 그래프와 연관된 태그를 지정해야합니다. 불러온 SavedModel의 버전은 SavedModelBundle이라고 하며 MetaGraphDef와 불러온 세션을 포함합니다.\n", + "\n", + "```C++\n", + "const string export_dir = ...\n", + "SavedModelBundle bundle;\n", + "...\n", + "LoadSavedModel(session_options, run_options, export_dir, {kSavedModelTagTrain},\n", + " &bundle);\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "b33KuyEuAO3Z" + }, + "source": [ + "\n", + "\n", + "## SavedModel 커맨드 라인 인터페이스 세부 사항\n", + "\n", + "SavedModel 커맨드 라인 인터페이스(CLI)를 사용하여 SavedModel을 검사하고 실행할 수 있습니다.\n", + "예를 들어, CLI를 사용하여 모델의 `SignatureDef`를 검사할 수 있습니다.\n", + "CLI를 사용하면 입력 Tensor 크기 및 데이터 타입이 모델과 일치하는지 신속하게 확인할 수 있습니다.\n", + "또한 모델을 테스트하려는 경우 다양한 형식(예를 들어, 파이썬 표현식)의 샘플 입력을\n", + "전달하고 출력을 가져와 CLI를 사용하여 정확성 검사를 수행할 수 있습니다.\n", + "\n", + "### SavedModel CLI 설치하기\n", + "\n", + "대체로 말하자면 다음 두 가지 방법 중 하나로 텐서플로를 설치할 수 있습니다:\n", + "\n", + "* 사전에 빌드된 텐서플로 바이너리로 설치\n", + "* 소스 코드로 텐서플로 빌드\n", + "\n", + "사전에 빌드된 텐서플로 바이너리를 통해 설치한 경우 SavedModel CLI가 이미 \n", + "시스템 경로 `bin\\saved_model_cli`에 설치되어 있습니다.\n", + "\n", + "소스 코드에서 텐서플로를 빌드하는 경우 다음 추가 명령을 실행하여 `saved_model_cli`를 빌드해야 합니다:\n", + "\n", + "```\n", + "$ bazel build tensorflow/python/tools:saved_model_cli\n", + "```\n", + "\n", + "### 명령 개요\n", + "\n", + "SavedModel CLI는 SavedModel의 `MetaGraphDef`에 대해 다음 두 명령어를 지원합니다:\n", + "\n", + "* SavedModel의 `MetaGraphDef`에 대한 계산을 보여주는 `show`\n", + "* `MetaGraphDef`에 대한 계산을 실행하는 `run`\n", + "\n", + "\n", + "### `show` 명령어\n", + "\n", + "SavedModel은 태그 세트로 식별되는 하나 이상의 `MetaGraphDef`를 포함합니다.\n", + "모델을 텐서플로 서빙에 배포하려면, 각 모델에 어떤 종류의 `SignatureDef`가 있는지, 그리고 입력과 출력은 무엇인지 궁금할 수 있습니다.\n", + "`show` 명령은 SavedModel의 내용을 계층적 순서로 검사합니다. 구문은 다음과 같습니다:\n", + "\n", + "```\n", + "usage: saved_model_cli show [-h] --dir DIR [--all]\n", + "[--tag_set TAG_SET] [--signature_def SIGNATURE_DEF_KEY]\n", + "```\n", + "\n", + "예를 들어, 다음 명령은 SavedModel에서 사용 가능한 모든 `MetaGraphDef` 태그 세트를 보여줍니다:\n", + "\n", + "```\n", + "$ saved_model_cli show --dir /tmp/saved_model_dir\n", + "The given SavedModel contains the following tag-sets:\n", + "serve\n", + "serve, gpu\n", + "```\n", + "\n", + "다음 명령은 `MetaGraphDef`에서 사용 가능한 모든 `SignatureDef` 키를 보여줍니다:\n", + "\n", + "```\n", + "$ saved_model_cli show --dir /tmp/saved_model_dir --tag_set serve\n", + "The given SavedModel `MetaGraphDef` contains `SignatureDefs` with the\n", + "following keys:\n", + "SignatureDef key: \"classify_x2_to_y3\"\n", + "SignatureDef key: \"classify_x_to_y\"\n", + "SignatureDef key: \"regress_x2_to_y3\"\n", + "SignatureDef key: \"regress_x_to_y\"\n", + "SignatureDef key: \"regress_x_to_y2\"\n", + "SignatureDef key: \"serving_default\"\n", + "```\n", + "\n", + "`MetaGraphDef`가 태그 세트에 *여러 개의* 태그를 가지고 있는 경우, 모든 태그를 지정해야 하며,\n", + "각 태그는 쉼표로 구분해야 합니다. 예를 들어:\n", + "\n", + ",), {}). Only existing signatures are [((TensorSpec(shape=(), dtype=tf.float32, name=u'x'),), {})].\n", + "
\n", + "$ saved_model_cli show --dir /tmp/saved_model_dir --tag_set serve,gpu\n", + "\n", + "\n", + "특정 `SignatureDef`에 대한 모든 입력 및 출력 텐서 정보(TensorInfo)를 표시하려면 `SignatureDef` 키를\n", + "`signature_def` 옵션으로 전달하십시오. 이것은 나중에 계산 그래프를 실행하기 위해 입력 텐서의 텐서 키 값,\n", + "크기 및 데이터 타입을 알고자 할 때 매우 유용합니다. 예를 들어:\n", + "\n", + "```\n", + "$ saved_model_cli show --dir \\\n", + "/tmp/saved_model_dir --tag_set serve --signature_def serving_default\n", + "The given SavedModel SignatureDef contains the following input(s):\n", + " inputs['x'] tensor_info:\n", + " dtype: DT_FLOAT\n", + " shape: (-1, 1)\n", + " name: x:0\n", + "The given SavedModel SignatureDef contains the following output(s):\n", + " outputs['y'] tensor_info:\n", + " dtype: DT_FLOAT\n", + " shape: (-1, 1)\n", + " name: y:0\n", + "Method name is: tensorflow/serving/predict\n", + "```\n", + "\n", + "SavedModel에 사용 가능한 모든 정보를 표시하려면 `--all` 옵션을 사용하십시오. 예를 들어:\n", + "\n", + "
\n", + "$ saved_model_cli show --dir /tmp/saved_model_dir --all\n", + "MetaGraphDef with tag-set: 'serve' contains the following SignatureDefs:\n", + "\n", + "signature_def['classify_x2_to_y3']:\n", + " The given SavedModel SignatureDef contains the following input(s):\n", + " inputs['inputs'] tensor_info:\n", + " dtype: DT_FLOAT\n", + " shape: (-1, 1)\n", + " name: x2:0\n", + " The given SavedModel SignatureDef contains the following output(s):\n", + " outputs['scores'] tensor_info:\n", + " dtype: DT_FLOAT\n", + " shape: (-1, 1)\n", + " name: y3:0\n", + " Method name is: tensorflow/serving/classify\n", + "\n", + "...\n", + "\n", + "signature_def['serving_default']:\n", + " The given SavedModel SignatureDef contains the following input(s):\n", + " inputs['x'] tensor_info:\n", + " dtype: DT_FLOAT\n", + " shape: (-1, 1)\n", + " name: x:0\n", + " The given SavedModel SignatureDef contains the following output(s):\n", + " outputs['y'] tensor_info:\n", + " dtype: DT_FLOAT\n", + " shape: (-1, 1)\n", + " name: y:0\n", + " Method name is: tensorflow/serving/predict\n", + "\n", + "\n", + "\n", + "### `run` 명령어\n", + "\n", + "`run` 명령을 호출하여 그래프 계산을 실행하고, 입력을 전달한 다음 출력을 표시(하고 선택적으로 저장)합니다.\n", + "구문은 다음과 같습니다:\n", + "\n", + "```\n", + "usage: saved_model_cli run [-h] --dir DIR --tag_set TAG_SET --signature_def\n", + " SIGNATURE_DEF_KEY [--inputs INPUTS]\n", + " [--input_exprs INPUT_EXPRS]\n", + " [--input_examples INPUT_EXAMPLES] [--outdir OUTDIR]\n", + " [--overwrite] [--tf_debug]\n", + "```\n", + "\n", + "`run` 명령은 입력을 모델에 전달하는 다음 세 가지 방법을 제공합니다:\n", + "\n", + "* `--inputs` 옵션을 사용하여 넘파이(numpy) ndarray를 파일에 전달할 수 있습니다.\n", + "* `--input_exprs` 옵션을 사용하여 파이썬 표현식을 전달할 수 있습니다.\n", + "* `--input_examples` 옵션을 사용하여 `tf.train.Example`을 전달할 수 있습니다.\n", + "\n", + "#### `--inputs`\n", + "\n", + "입력 데이터를 파일에 전달하려면, 다음과 같은 일반적인 형식을 가지는 `--inputs` 옵션을 지정합니다:\n", + "\n", + "```bsh\n", + "--inputs