{ "cells": [ { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "# Google Colab\uc5d0\uc11c \ub178\ud2b8\ubd81\uc744 \uc2e4\ud589\ud558\uc2e4 \ub54c\uc5d0\ub294 \n# https://tutorials.pytorch.kr/beginner/colab \ub97c \ucc38\uace0\ud558\uc138\uc694.\n%matplotlib inline" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Flask\ub97c \uc0ac\uc6a9\ud558\uc5ec Python\uc5d0\uc11c PyTorch\ub97c REST API\ub85c \ubc30\ud3ec\ud558\uae30\n=========================================================\n\n**Author**: [Avinash Sajjanshetty](https://avi.im)\n\n: **\ubc88\uc5ed**: [\ubc15\uc815\ud658](http://github.com/9bow)\n\n\uc774 \ud29c\ud1a0\ub9ac\uc5bc\uc5d0\uc11c\ub294 Flask\ub97c \uc0ac\uc6a9\ud558\uc5ec PyTorch \ubaa8\ub378\uc744 \ubc30\ud3ec\ud558\uace0 \ubaa8\ub378\n\ucd94\ub860(inference)\uc744 \ud560 \uc218 \uc788\ub294 REST API\ub97c \ub9cc\ub4e4\uc5b4\ubcf4\uaca0\uc2b5\ub2c8\ub2e4. \ubbf8\ub9ac \ud6c8\ub828\ub41c\nDenseNet 121 \ubaa8\ub378\uc744 \ubc30\ud3ec\ud558\uc5ec \uc774\ubbf8\uc9c0\ub97c \uc778\uc2dd\ud574\ubcf4\uaca0\uc2b5\ub2c8\ub2e4.\n\n
TIP:
\n
\n

\uc5ec\uae30\uc11c \uc0ac\uc6a9\ud55c \ubaa8\ub4e0 \ucf54\ub4dc\ub294 MIT \ub77c\uc774\uc120\uc2a4\ub85c \ubc30\ud3ec\ub418\uba70,GitHub \uc5d0\uc11c \ud655\uc778\ud558\uc2e4 \uc218 \uc788\uc2b5\ub2c8\ub2e4.

\n
\n\n\uc774\uac83\uc740 PyTorch \ubaa8\ub378\uc744 \uc0c1\uc6a9(production)\uc73c\ub85c \ubc30\ud3ec\ud558\ub294 \ud29c\ud1a0\ub9ac\uc5bc \uc2dc\ub9ac\uc988\uc758\n\uccab\ubc88\uc9f8 \ud3b8\uc785\ub2c8\ub2e4. Flask\ub97c \uc5ec\uae30\uc5d0 \uc18c\uac1c\ub41c \uac83\ucc98\ub7fc \uc0ac\uc6a9\ud558\ub294 \uac83\uc774 PyTorch\n\ubaa8\ub378\uc744 \uc81c\uacf5\ud558\ub294 \uac00\uc7a5 \uc26c\uc6b4 \ubc29\ubc95\uc774\uc9c0\ub9cc, \uace0\uc131\ub2a5\uc744 \uc694\uad6c\ud558\ub294 \ub54c\uc5d0\ub294 \uc801\ud569\ud558\uc9c0\n\uc54a\uc2b5\ub2c8\ub2e4. \uadf8\uc5d0 \ub300\ud574\uc11c\ub294:\n\n> - TorchScript\uc5d0 \uc774\ubbf8 \uc775\uc219\ud558\ub2e4\uba74, \ubc14\ub85c [Loading a TorchScript Model\n> in C++](https://tutorials.pytorch.kr/advanced/cpp_export.html)\n> \ubb38\uc11c\ubd80\ud130 \uc77d\uc5b4\ubcf4\uc138\uc694.\n> - TorchScript\uac00 \ubb34\uc5c7\uc778\uc9c0 \uc54c\uc544\ubcf4\ub294 \uac83\uc774 \ud544\uc694\ud558\ub2e4\uba74 [TorchScript\n> \uc18c\uac1c](https://tutorials.pytorch.kr/beginner/Intro_to_TorchScript_tutorial.html)\n> \ubd80\ud130 \uc77d\uc5b4\ubcf4\uc2dc\ub294 \uac83\uc744 \ucd94\ucc9c\ud569\ub2c8\ub2e4.\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "API \uc815\uc758\n========\n\n\uba3c\uc800 API \uc5d4\ub4dc\ud3ec\uc778\ud2b8(endpoint)\uc758 \uc694\uccad(request)\uc640 \uc751\ub2f5(response)\uc744\n\uc815\uc758\ud558\ub294 \uac83\ubd80\ud130 \uc2dc\uc791\ud574\ubcf4\uaca0\uc2b5\ub2c8\ub2e4. \uc0c8\ub85c \ub9cc\ub4e4 API \uc5d4\ub4dc\ud3ec\uc778\ud2b8\ub294 \uc774\ubbf8\uc9c0\uac00\n\ud3ec\ud568\ub41c `file` \ub9e4\uac1c\ubcc0\uc218\ub97c HTTP POST\ub85c `/predict` \uc5d0 \uc694\uccad\ud569\ub2c8\ub2e4. \uc751\ub2f5\uc740\nJSON \ud615\ud0dc\uc774\uba70 \ub2e4\uc74c\uacfc \uac19\uc740 \uc608\uce21 \uacb0\uacfc\ub97c \ud3ec\ud568\ud569\ub2c8\ub2e4:\n\n``` {.sourceCode .sh}\n{\"class_id\": \"n02124075\", \"class_name\": \"Egyptian_cat\"}\n```\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\uc758\uc874\uc131(Dependencies)\n====================\n\n\uc544\ub798 \uba85\ub839\uc5b4\ub97c \uc2e4\ud589\ud558\uc5ec \ud544\uc694\ud55c \ud328\ud0a4\uc9c0\ub4e4\uc744 \uc124\uce58\ud569\ub2c8\ub2e4:\n\n``` {.sourceCode .sh}\n$ pip install Flask==2.0.1 torchvision==0.10.0\n```\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\uac04\ub2e8\ud55c \uc6f9 \uc11c\ubc84\n==============\n\nFlask\uc758 \ubb38\uc11c\ub97c \ucc38\uace0\ud558\uc5ec \uc544\ub798\uc640 \uac19\uc740 \ucf54\ub4dc\ub85c \uac04\ub2e8\ud55c \uc6f9 \uc11c\ubc84\ub97c \uad6c\uc131\ud569\ub2c8\ub2e4.\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "from flask import Flask\napp = Flask(__name__)\n\n\n@app.route('/')\ndef hello():\n return 'Hello World!'" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\ub610\ud55c, ImageNet \ubd84\ub958 ID\uc640 \uc774\ub984\uc744 \ud3ec\ud568\ud558\ub294 JSON\uc744 \ud68c\uc2e0\ud558\ub3c4\ub85d \uc751\ub2f5 \ud615\uc2dd\uc744\n\ubcc0\uacbd\ud558\uaca0\uc2b5\ub2c8\ub2e4. \uc774\uc81c `app.py` \ub294 \uc544\ub798\uc640 \uac19\uc774 \ubcc0\uacbd\ub418\uc5c8\uc2b5\ub2c8\ub2e4:\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "from flask import Flask, jsonify\napp = Flask(__name__)\n\n@app.route('/predict', methods=['POST'])\ndef predict():\n return jsonify({'class_id': 'IMAGE_NET_XXX', 'class_name': 'Cat'})" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\ucd94\ub860(Inference)\n===============\n\n\ub2e4\uc74c \uc139\uc158\uc5d0\uc11c\ub294 \ucd94\ub860 \ucf54\ub4dc \uc791\uc131\uc5d0 \uc9d1\uc911\ud558\uaca0\uc2b5\ub2c8\ub2e4. \uba3c\uc800 \uc774\ubbf8\uc9c0\ub97c\nDenseNet\uc5d0 \uacf5\uae09(feed)\ud560 \uc218 \uc788\ub3c4\ub85d \uc900\ube44\ud558\ub294 \ubc29\ubc95\uc744 \uc0b4\ud3b4\ubcf8 \ub4a4, \ubaa8\ub378\ub85c\ubd80\ud130\n\uc608\uce21 \uacb0\uacfc\ub97c \uc5bb\ub294 \ubc29\ubc95\uc744 \uc0b4\ud3b4\ubcf4\uaca0\uc2b5\ub2c8\ub2e4.\n\n\uc774\ubbf8\uc9c0 \uc900\ube44\ud558\uae30\n---------------\n\nDenseNet \ubaa8\ub378\uc740 224 x 224\uc758 3\ucc44\ub110 RGB \uc774\ubbf8\uc9c0\ub97c \ud544\uc694\ub85c \ud569\ub2c8\ub2e4. \ub610\ud55c\n\uc774\ubbf8\uc9c0 \ud150\uc11c\ub97c \ud3c9\uade0 \ubc0f \ud45c\uc900\ud3b8\ucc28 \uac12\uc73c\ub85c \uc815\uaddc\ud654\ud569\ub2c8\ub2e4. \uc790\uc138\ud55c \ub0b4\uc6a9\uc740\n[\uc5ec\uae30](https://pytorch.org/vision/stable/models.html) \ub97c \ucc38\uace0\ud558\uc138\uc694.\n\n`torchvision` \ub77c\uc774\ube0c\ub7ec\ub9ac\uc758 `transforms` \ub97c \uc0ac\uc6a9\ud558\uc5ec \ubcc0\ud658 \ud30c\uc774\ud504\ub77c\uc778\n(transform pipeline)\uc744 \uad6c\ucd95\ud569\ub2c8\ub2e4. Transforms\uc640 \uad00\ub828\ud55c \ub354 \uc790\uc138\ud55c \ub0b4\uc6a9\uc740\n[\uc5ec\uae30](https://pytorch.org/vision/stable/transforms.html) \uc5d0\uc11c \uc77d\uc5b4\ubcfc \uc218\n\uc788\uc2b5\ub2c8\ub2e4.\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "import io\n\nimport torchvision.transforms as transforms\nfrom PIL import Image\n\ndef transform_image(image_bytes):\n my_transforms = transforms.Compose([transforms.Resize(255),\n transforms.CenterCrop(224),\n transforms.ToTensor(),\n transforms.Normalize(\n [0.485, 0.456, 0.406],\n [0.229, 0.224, 0.225])])\n image = Image.open(io.BytesIO(image_bytes))\n return my_transforms(image).unsqueeze(0)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\uc704 \uba54\uc18c\ub4dc\ub294 \uc774\ubbf8\uc9c0\ub97c byte \ub2e8\uc704\ub85c \uc77d\uc740 \ud6c4, \uc77c\ub828\uc758 \ubcc0\ud658\uc744 \uc801\uc6a9\ud558\uace0\nTensor\ub97c \ubc18\ud658\ud569\ub2c8\ub2e4. \uc704 \uba54\uc18c\ub4dc\ub97c \ud14c\uc2a4\ud2b8\ud558\uae30 \uc704\ud574\uc11c\ub294 \uc774\ubbf8\uc9c0\ub97c byte\n\ubaa8\ub4dc\ub85c \uc77d\uc740 \ud6c4 Tensor\ub97c \ubc18\ud658\ud558\ub294\uc9c0 \ud655\uc778\ud558\uba74 \ub429\ub2c8\ub2e4. (\uba3c\uc800\n[../\\_static/img/sample\\_file.jpeg]{.title-ref} \uc744 \ucef4\ud4e8\ud130 \uc0c1\uc758 \uc2e4\uc81c\n\uacbd\ub85c\ub85c \ubc14\uafd4\uc57c \ud569\ub2c8\ub2e4.)\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "with open(\"../_static/img/sample_file.jpeg\", 'rb') as f:\n image_bytes = f.read()\n tensor = transform_image(image_bytes=image_bytes)\n print(tensor)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\uc608\uce21(Prediction)\n================\n\n\ubbf8\ub9ac \ud559\uc2b5\ub41c DenseNet 121 \ubaa8\ub378\uc744 \uc0ac\uc6a9\ud558\uc5ec \uc774\ubbf8\uc9c0 \ubd84\ub958(class)\ub97c\n\uc608\uce21\ud569\ub2c8\ub2e4. `torchvision` \ub77c\uc774\ube0c\ub7ec\ub9ac\uc758 \ubaa8\ub378\uc744 \uc0ac\uc6a9\ud558\uc5ec \ubaa8\ub378\uc744 \uc77d\uc5b4\uc624\uace0\n\ucd94\ub860\uc744 \ud569\ub2c8\ub2e4. \uc774 \uc608\uc81c\uc5d0\uc11c\ub294 \ubbf8\ub9ac \ud559\uc2b5\ub41c \ubaa8\ub378\uc744 \uc0ac\uc6a9\ud558\uc9c0\ub9cc, \uc9c1\uc811 \ub9cc\ub4e0\n\ubaa8\ub378\uc5d0 \ub300\ud574\uc11c\ub3c4 \uc774\uc640 \ub3d9\uc77c\ud55c \ubc29\ubc95\uc744 \uc0ac\uc6a9\ud560 \uc218 \uc788\uc2b5\ub2c8\ub2e4. \ubaa8\ub378\uc744 \uc77d\uc5b4\uc624\ub294\n\uac83\uc740 \uc774 `\ud29c\ud1a0\ub9ac\uc5bc `{.interpreted-text\nrole=\"doc\"} \uc744 \ucc38\uace0\ud558\uc138\uc694.\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "from torchvision import models\n\n# \uc774\ubbf8 \ud559\uc2b5\ub41c \uac00\uc911\uce58\ub97c \uc0ac\uc6a9\ud558\uae30 \uc704\ud574 `weights` \uc5d0 `IMAGENET1K_V1` \uac12\uc744 \uc804\ub2ec\ud569\ub2c8\ub2e4:\nmodel = models.densenet121(weights='IMAGENET1K_V1')\n# \ubaa8\ub378\uc744 \ucd94\ub860\uc5d0\ub9cc \uc0ac\uc6a9\ud560 \uac83\uc774\ubbc0\ub85c, `eval` \ubaa8\ub4dc\ub85c \ubcc0\uacbd\ud569\ub2c8\ub2e4:\nmodel.eval()\n\n\ndef get_prediction(image_bytes):\n tensor = transform_image(image_bytes=image_bytes)\n outputs = model.forward(tensor)\n _, y_hat = outputs.max(1)\n return y_hat" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`y_hat` Tensor\ub294 \uc608\uce21\ub41c \ubd84\ub958 ID\uc758 \uc778\ub371\uc2a4\ub97c \ud3ec\ud568\ud569\ub2c8\ub2e4. \ud558\uc9c0\ub9cc \uc0ac\ub78c\uc774\n\uc77d\uc744 \uc218 \uc788\ub294 \ubd84\ub958\uba85\uc774 \uc788\uc5b4\uc57c \ud558\uae30 \ub54c\ubb38\uc5d0, \uc774\ub97c \uc704\ud574 \uc774\ub984\uacfc \ubd84\ub958 ID\ub97c\n\ub9e4\ud551\ud558\ub294 \uac83\uc774 \ud544\uc694\ud569\ub2c8\ub2e4. [\uc774\n\ud30c\uc77c](https://s3.amazonaws.com/deep-learning-models/image-models/imagenet_class_index.json)\n\uc744 \ub2e4\uc6b4\ub85c\ub4dc \ubc1b\uc544 `imagenet_class_index.json` \uc774\ub77c\ub294 \uc774\ub984\uc73c\ub85c \uc800\uc7a5 \ud6c4,\n\uc800\uc7a5\ud55c \uacf3\uc758 \uc704\uce58\ub97c \uae30\uc5b5\ud574\ub450\uc138\uc694. (\ub610\ub294, \uc774 \ud29c\ud1a0\ub9ac\uc5bc\uacfc \ub611\uac19\uc774 \uc9c4\ud589\ud558\ub294\n\uacbd\uc6b0\uc5d0\ub294 [tutorials/\\_static]{.title-ref} \uc5d0 \uc800\uc7a5\ud558\uc138\uc694.) \uc774 \ud30c\uc77c\uc740\nImageNet \ubd84\ub958 ID\uc640 ImageNet \ubd84\ub958\uba85\uc758 \uc30d\uc744 \ud3ec\ud568\ud558\uace0 \uc788\uc2b5\ub2c8\ub2e4. \uc774\uc81c \uc774\nJSON \ud30c\uc77c\uc744 \ubd88\ub7ec\uc640 \uc608\uce21 \uacb0\uacfc\uc758 \uc778\ub371\uc2a4\uc5d0 \ud574\ub2f9\ud558\ub294 \ubd84\ub958\uba85\uc744\n\uac00\uc838\uc624\uaca0\uc2b5\ub2c8\ub2e4.\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "import json\n\nimagenet_class_index = json.load(open('../_static/imagenet_class_index.json'))\n\ndef get_prediction(image_bytes):\n tensor = transform_image(image_bytes=image_bytes)\n outputs = model.forward(tensor)\n _, y_hat = outputs.max(1)\n predicted_idx = str(y_hat.item())\n return imagenet_class_index[predicted_idx]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`imagenet_class_index` \uc0ac\uc804(dictionary)\uc744 \uc0ac\uc6a9\ud558\uae30 \uc804\uc5d0,\n`imagenet_class_index` \uc758 \ud0a4 \uac12\uc774 \ubb38\uc790\uc5f4\uc774\ubbc0\ub85c Tensor\uc758 \uac12\ub3c4 \ubb38\uc790\uc5f4\ub85c\n\ubcc0\ud658\ud574\uc57c \ud569\ub2c8\ub2e4. \uc704 \uba54\uc18c\ub4dc\ub97c \ud14c\uc2a4\ud2b8\ud574\ubcf4\uaca0\uc2b5\ub2c8\ub2e4:\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "with open(\"../_static/img/sample_file.jpeg\", 'rb') as f:\n image_bytes = f.read()\n print(get_prediction(image_bytes=image_bytes))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\ub2e4\uc74c\uacfc \uac19\uc740 \uc751\ub2f5\uc744 \ubc1b\uac8c \ub420 \uac83\uc785\ub2c8\ub2e4:\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "['n02124075', 'Egyptian_cat']" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\ubc30\uc5f4\uc758 \uccab\ubc88\uc9f8 \ud56d\ubaa9\uc740 ImageNet \ubd84\ub958 ID\uc774\uace0, \ub450\ubc88\uc9f8 \ud56d\ubaa9\uc740 \uc0ac\ub78c\uc774 \uc77d\uc744 \uc218\n\uc788\ub294 \uc774\ub984\uc785\ub2c8\ub2e4.\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\ubaa8\ub378\uc744 API \uc11c\ubc84\uc5d0 \ud1b5\ud569\ud558\uae30\n\n: \n\n ------------------------------------------------------------------------\n\n \ub9c8\uc9c0\ub9c9\uc73c\ub85c \uc704\uc5d0\uc11c \ub9cc\ub4e0 Flask API \uc11c\ubc84\uc5d0 \ubaa8\ub378\uc744 \ucd94\uac00\ud558\uaca0\uc2b5\ub2c8\ub2e4. API\n \uc11c\ubc84\ub294 \uc774\ubbf8\uc9c0 \ud30c\uc77c\uc744 \ubc1b\ub294 \uac83\uc744 \uac00\uc815\ud558\uace0 \uc788\uc73c\ubbc0\ub85c, \uc694\uccad\uc73c\ub85c\ubd80\ud130\n \ud30c\uc77c\uc744 \uc77d\ub3c4\ub85d `predict` \uba54\uc18c\ub4dc\ub97c \uc218\uc815\ud574\uc57c \ud569\ub2c8\ub2e4:\n\n ``` {.sourceCode .python}\n from flask import request\n\n @app.route('/predict', methods=['POST'])\n def predict():\n if request.method == 'POST':\n # we will get the file from the request\n file = request.files['file']\n # convert that to bytes\n img_bytes = file.read()\n class_id, class_name = get_prediction(image_bytes=img_bytes)\n return jsonify({'class_id': class_id, 'class_name': class_name})\n ```\n\n\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\n\n: `app.py` \ud30c\uc77c\uc740 \uc774\uc81c \uc644\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4. \uc544\ub798\uac00 \uc804\uccb4 \ucf54\ub4dc\uc785\ub2c8\ub2e4; \uc544\ub798\n [\\]{.title-ref} \uc758 \uacbd\ub85c\ub97c json \ud30c\uc77c\uc744 \uc800\uc7a5\ud574\ub454\n \uacbd\ub85c\ub85c \ubc14\uafb8\uba74 \ub3d9\uc791\ud569\ub2c8\ub2e4:\n\n ``` {.sourceCode .python}\n import io\n import json\n\n from torchvision import models\n import torchvision.transforms as transforms\n from PIL import Image\n from flask import Flask, jsonify, request\n\n\n app = Flask(__name__)\n imagenet_class_index = json.load(open('/imagenet_class_index.json'))\n model = models.densenet121(weights='IMAGENET1K_V1')\n model.eval()\n\n\n def transform_image(image_bytes):\n my_transforms = transforms.Compose([transforms.Resize(255),\n transforms.CenterCrop(224),\n transforms.ToTensor(),\n transforms.Normalize(\n [0.485, 0.456, 0.406],\n [0.229, 0.224, 0.225])])\n image = Image.open(io.BytesIO(image_bytes))\n return my_transforms(image).unsqueeze(0)\n\n\n def get_prediction(image_bytes):\n tensor = transform_image(image_bytes=image_bytes)\n outputs = model.forward(tensor)\n _, y_hat = outputs.max(1)\n predicted_idx = str(y_hat.item())\n return imagenet_class_index[predicted_idx]\n\n\n @app.route('/predict', methods=['POST'])\n def predict():\n if request.method == 'POST':\n file = request.files['file']\n img_bytes = file.read()\n class_id, class_name = get_prediction(image_bytes=img_bytes)\n return jsonify({'class_id': class_id, 'class_name': class_name})\n\n\n if __name__ == '__main__':\n app.run()\n ```\n\n\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\n\n: \uc774\uc81c \uc6f9 \uc11c\ubc84\ub97c \ud14c\uc2a4\ud2b8\ud574\ubcf4\uaca0\uc2b5\ub2c8\ub2e4! \ub2e4\uc74c\uacfc \uac19\uc774 \uc2e4\ud589\ud574\ubcf4\uc138\uc694:\n\n ``` {.sourceCode .sh}\n FLASK_ENV=development FLASK_APP=app.py flask run\n ```\n\n\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\\#\n\n: [requests](https://pypi.org/project/requests/) \ub77c\uc774\ube0c\ub7ec\ub9ac\ub97c \uc0ac\uc6a9\ud558\uc5ec\n POST \uc694\uccad\uc744 \ub9cc\ub4e4\uc5b4\ubcf4\uaca0\uc2b5\ub2c8\ub2e4:\n\n ``` {.sourceCode .python}\n import requests\n\n resp = requests.post(\"http://localhost:5000/predict\",\n files={\"file\": open('/cat.jpg','rb')})\n ```\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "[resp.json()]{.title-ref} \uc744 \ud638\ucd9c\ud558\uba74 \ub2e4\uc74c\uacfc \uac19\uc740 \uacb0\uacfc\ub97c \ucd9c\ub825\ud569\ub2c8\ub2e4:\n\n``` {.sourceCode .sh}\n{\"class_id\": \"n02124075\", \"class_name\": \"Egyptian_cat\"}\n```\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\ub2e4\uc74c \ub2e8\uacc4\n=========\n\n\uc9c0\uae08\uae4c\uc9c0 \ub9cc\ub4e0 \uc11c\ubc84\ub294 \ub9e4\uc6b0 \uac04\ub2e8\ud558\uc5ec \uc0c1\uc6a9 \ud504\ub85c\uadf8\ub7a8(production\napplication)\uc73c\ub85c\uc368 \uac16\ucdb0\uc57c\ud560 \uac83\ub4e4\uc744 \ubaa8\ub450 \uac16\ucd94\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4. \ub530\ub77c\uc11c,\n\ub2e4\uc74c\uacfc \uac19\uc774 \uac1c\uc120\ud574\ubcfc \uc218 \uc788\uc2b5\ub2c8\ub2e4:\n\n- `/predict` \uc5d4\ub4dc\ud3ec\uc778\ud2b8\ub294 \uc694\uccad \uc2dc\uc5d0 \ubc18\ub4dc\uc2dc \uc774\ubbf8\uc9c0 \ud30c\uc77c\uc774 \uc804\ub2ec\ub418\ub294 \uac83\uc744\n \uac00\uc815\ud558\uace0 \uc788\uc2b5\ub2c8\ub2e4. \ud558\uc9c0\ub9cc \ubaa8\ub4e0 \uc694\uccad\uc774 \uadf8\ub807\uc9c0\ub294 \uc54a\uc2b5\ub2c8\ub2e4. \uc0ac\uc6a9\uc790\ub294\n \ub2e4\ub978 \ub9e4\uac1c\ubcc0\uc218\ub85c \uc774\ubbf8\uc9c0\ub97c \ubcf4\ub0b4\uac70\ub098, \uc774\ubbf8\uc9c0\ub97c \uc544\uc608 \ubcf4\ub0b4\uc9c0 \uc54a\uc744\uc218\ub3c4\n \uc788\uc2b5\ub2c8\ub2e4.\n- \uc0ac\uc6a9\uc790\uac00 \uc774\ubbf8\uc9c0\uac00 \uc544\ub2cc \uc720\ud615\uc758 \ud30c\uc77c\uc744 \ubcf4\ub0bc\uc218\ub3c4 \uc788\uc2b5\ub2c8\ub2e4. \uc5ec\uae30\uc11c\ub294\n \uc5d0\ub7ec\ub97c \ucc98\ub9ac\ud558\uc9c0 \uc54a\uace0 \uc788\uc73c\ubbc0\ub85c, \uc774\ub7ec\ud55c \uacbd\uc6b0\uc5d0 \uc11c\ubc84\ub294\n \ub2e4\uc6b4(break)\ub429\ub2c8\ub2e4. \uc608\uc678 \uc804\ub2ec(exception throe)\uc744 \uc704\ud55c \uba85\uc2dc\uc801\uc778 \uc5d0\ub7ec\n \ud578\ub4e4\ub9c1 \uacbd\ub85c\ub97c \ucd94\uac00\ud558\uba74 \uc798\ubabb\ub41c \uc785\ub825\uc744 \ub354 \uc798 \ucc98\ub9ac\ud560 \uc218 \uc788\uc2b5\ub2c8\ub2e4.\n- \ubaa8\ub378\uc740 \ub9ce\uc740 \uc885\ub958\uc758 \uc774\ubbf8\uc9c0 \ubd84\ub958\ub97c \uc778\uc2dd\ud560 \uc218 \uc788\uc9c0\ub9cc, \ubaa8\ub4e0 \uc774\ubbf8\uc9c0\ub97c\n \uc778\uc2dd\ud560 \uc218 \uc788\ub294 \uac83\uc740 \uc544\ub2d9\ub2c8\ub2e4. \ubaa8\ub378\uc774 \uc774\ubbf8\uc9c0\uc5d0\uc11c \uc544\ubb34\uac83\ub3c4 \uc778\uc2dd\ud558\uc9c0\n \ubabb\ud558\ub294 \uacbd\uc6b0\ub97c \ucc98\ub9ac\ud558\ub3c4\ub85d \uac1c\uc120\ud569\ub2c8\ub2e4.\n- \uc704\uc5d0\uc11c\ub294 Flask \uc11c\ubc84\ub97c \uac1c\ubc1c \ubaa8\ub4dc\uc5d0\uc11c \uc2e4\ud589\ud558\uc600\uc9c0\ub9cc, \uc774\ub294 \uc0c1\uc6a9\uc73c\ub85c\n \ubc30\ud3ec\ud558\uae30\uc5d0\ub294 \uc801\ub2f9\ud558\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4. Flask \uc11c\ubc84\ub97c \uc0c1\uc6a9\uc73c\ub85c \ubc30\ud3ec\ud558\ub294 \uac83\uc740\n [\uc774\n \ud29c\ud1a0\ub9ac\uc5bc](https://flask.palletsprojects.com/en/1.1.x/tutorial/deploy/)\n \uc744 \ucc38\uace0\ud574\ubcf4\uc138\uc694.\n- \ub610\ud55c \uc774\ubbf8\uc9c0\ub97c \uac00\uc838\uc624\ub294 \uc591\uc2dd(form)\uacfc \uc608\uce21 \uacb0\uacfc\ub97c \ud45c\uc2dc\ud558\ub294 \ud398\uc774\uc9c0\ub97c\n \ub9cc\ub4e4\uc5b4 UI\ub97c \ucd94\uac00\ud560 \uc218\ub3c4 \uc788\uc2b5\ub2c8\ub2e4. \ube44\uc2b7\ud55c \ud504\ub85c\uc81d\ud2b8\uc758\n [\ub370\ubaa8](https://pytorch-imagenet.herokuapp.com/) \uc640 \uc774 \ub370\ubaa8\uc758 [\uc18c\uc2a4\n \ucf54\ub4dc](https://github.com/avinassh/pytorch-flask-api-heroku) \ub97c\n \ucc38\uace0\ud574\ubcf4\uc138\uc694.\n- \uc774 \ud29c\ud1a0\ub9ac\uc5bc\uc5d0\uc11c\ub294 \ud55c \ubc88\uc5d0 \ud558\ub098\uc758 \uc774\ubbf8\uc9c0\uc5d0 \ub300\ud55c \uc608\uce21 \uacb0\uacfc\ub97c \ubc18\ud658\ud558\ub294\n \uc11c\ube44\uc2a4\ub97c \ub9cc\ub4dc\ub294 \ubc29\ubc95\ub9cc \uc0b4\ud3b4\ubcf4\uc558\ub294\ub370\uc694, \ud55c \ubc88\uc5d0 \uc5ec\ub7ec \uc774\ubbf8\uc9c0\uc5d0 \ub300\ud55c\n \uc608\uce21 \uacb0\uacfc\ub97c \ubc18\ud658\ud558\ub3c4\ub85d \uc218\uc815\ud574\ubcfc \uc218 \uc788\uc2b5\ub2c8\ub2e4. \ucd94\uac00\ub85c,\n [service-streamer](https://github.com/ShannonAI/service-streamer)\n \ub77c\uc774\ube0c\ub7ec\ub9ac\ub294 \uc790\ub3d9\uc73c\ub85c \uc694\uccad\uc744 \ud050\uc5d0 \ub123\uc740 \ub4a4 \ubaa8\ub378\uc5d0 \uacf5\uae09(feed)\ud560 \uc218\n \uc788\ub294 \ubbf8\ub2c8-\ubc30\uce58\ub85c \uc0d8\ud50c\ub9c1\ud569\ub2c8\ub2e4. [\uc774\n \ud29c\ud1a0\ub9ac\uc5bc](https://github.com/ShannonAI/service-streamer/wiki/Vision-Recognition-Service-with-Flask-and-service-streamer)\n \uc744 \ucc38\uace0\ud574\ubcf4\uc138\uc694.\n- \ub9c8\uc9c0\ub9c9\uc73c\ub85c \uc774 \ubb38\uc11c \uc0c1\ub2e8\uc5d0 \ub9c1\ud06c\ub41c, PyTorch \ubaa8\ub378\uc744 \ubc30\ud3ec\ud558\ub294 \ub2e4\ub978\n \ud29c\ud1a0\ub9ac\uc5bc\ub4e4\uc744 \uc77d\uc5b4\ubcf4\ub294 \uac83\uc744 \uad8c\uc7a5\ud569\ub2c8\ub2e4.\n" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "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.10.14" } }, "nbformat": 4, "nbformat_minor": 0 }