Created
November 7, 2022 17:53
-
-
Save hamelsmu/088381899b3308bdd7437ff5960e23f9 to your computer and use it in GitHub Desktop.
A demo of creating a gradio app with nbdev
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"cells": [ | |
{ | |
"cell_type": "markdown", | |
"id": "8c68f03e-620c-46a9-a7ec-a6cde27043cd", | |
"metadata": {}, | |
"source": [ | |
"# Hugging Face Spaces From A Notebook\n", | |
"\n", | |
"> A demo of using nbdev with Hugging Face Spaces" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"id": "96483373-4ae1-49b2-85ed-ceee8456df19", | |
"metadata": {}, | |
"source": [ | |
"## 1. Create a Gradio-enabled Space on Hugging Face\n", | |
"\n", | |
"The first step is to create a space and select the appropriate sdk (which is Gradio in this example), per [these instructions](https://huggingface.co/docs/hub/spaces-overview#creating-a-new-space):" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"id": "b34d7ec6-69b8-48c4-a68b-fad6db3c2fab", | |
"metadata": {}, | |
"source": [ | |
"" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"id": "c25e8e7a-52d9-4305-a107-ba03e3d6a5f3", | |
"metadata": {}, | |
"source": [ | |
"After you are done creating the space, **clone the repo per the instructions provided in the app.** In this example, I ran the command `git clone https://huggingface.co/spaces/hamel/hfspace_demo`." | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"id": "ff26114c-329b-4a97-98b5-c652554b0114", | |
"metadata": {}, | |
"source": [ | |
"## 2. Make an app with Gradio" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"id": "14a884fc-36e2-43ec-8e42-ca2903aaa4de", | |
"metadata": {}, | |
"source": [ | |
"Below, we will create a [gradio](https://gradio.app/) app in a notebook and show you how to deploy it to [Hugging Face Spaces](https://huggingface.co/docs/hub/spaces).\n", | |
"\n", | |
"First, lets specify the libraries we need, which in this case are `gradio` and `fastcore`:" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"id": "e5e5d597-19ad-46e5-81ad-8f646d8a1c21", | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"#|export\n", | |
"import gradio as gr\n", | |
"from fastcore.net import urljson, HTTPError" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"id": "38a4389f-ef53-4626-a6f5-a859354f854b", | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"#|export\n", | |
"def size(repo:str):\n", | |
" \"Returns the size in GB of a HuggingFace Dataset.\"\n", | |
" url = f'https://huggingface.co/api/datasets/{repo}'\n", | |
" try: resp = urljson(f'{url}/treesize/main')\n", | |
" except HTTPError: return f'Did not find repo: {url}'\n", | |
" gb = resp['size'] / 1e9\n", | |
" return f'{gb:.2f} GB'" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"id": "9ff9f84d-7744-46ad-80ed-2cf1fa6d0643", | |
"metadata": {}, | |
"source": [ | |
"`size` take as an input a [Hugging Face Dataset](https://huggingface.co/docs/datasets/index) repo and returns the total size in GB of the data.\n", | |
"\n", | |
"For example, we can check the size of [tglcourse/CelebA-faces-cropped-128](https://huggingface.co/datasets/tglcourse/CelebA-faces-cropped-128) like so:" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"id": "95bc32b8-d8ff-4761-a2d7-0880c51d0a42", | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"data": { | |
"text/plain": [ | |
"'5.49 GB'" | |
] | |
}, | |
"execution_count": null, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
], | |
"source": [ | |
"size(\"tglcourse/CelebA-faces-cropped-128\")" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"id": "cb13747b-ea48-4146-846d-deb9e855d32d", | |
"metadata": {}, | |
"source": [ | |
"You can construct a simple UI with the `gradio.interface` and then call the `launch` method of that interface to display a preview in a notebook. This is a great way to test your app to see if it works" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"id": "7b20e2a1-b622-4970-9069-0202ce10a2ce", | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": [ | |
"Running on local URL: http://127.0.0.1:7860\n", | |
"\n", | |
"To create a public link, set `share=True` in `launch()`.\n" | |
] | |
}, | |
{ | |
"data": { | |
"text/html": [ | |
"<div><iframe src=\"http://127.0.0.1:7860/\" width=\"500\" height=\"500\" allow=\"autoplay; camera; microphone; clipboard-read; clipboard-write;\" frameborder=\"0\" allowfullscreen></iframe></div>" | |
], | |
"text/plain": [ | |
"<IPython.core.display.HTML object>" | |
] | |
}, | |
"metadata": {}, | |
"output_type": "display_data" | |
}, | |
{ | |
"data": { | |
"text/plain": [ | |
"(<gradio.routes.App>, 'http://127.0.0.1:7860/', None)" | |
] | |
}, | |
"execution_count": null, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
], | |
"source": [ | |
"#|export\n", | |
"iface = gr.Interface(fn=size, inputs=gr.Text(value=\"tglcourse/CelebA-faces-cropped-128\"), outputs=\"text\")\n", | |
"iface.launch(width=500)" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"id": "59926b18-a9af-4387-9fcc-f88e588da577", | |
"metadata": {}, | |
"source": [ | |
"Note how running the `launch()` method in a notebook runs a webserver in the background. Below, we call the `close()` method to close the webserver." | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"id": "39d7be72-9389-42cf-91b1-78e8f4bbd083", | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": [ | |
"Closing server running on port: 7860\n" | |
] | |
} | |
], | |
"source": [ | |
"# this is only necessary in a notebook\n", | |
"iface.close()" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"id": "249b2cd7-3123-45bf-945f-882b8a964cf5", | |
"metadata": {}, | |
"source": [ | |
"## 3. Converting This Notebook Into A Gradio App" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"id": "5c18ca6e-8de8-49e1-b95a-304070bbc171", | |
"metadata": {}, | |
"source": [ | |
"In order to host this code on Hugging Faces spaces, you will export parts of this notebook to a script named `app.py`. That is what the special `#|export` comment that you have seen in cells above do! You can export code from this notebook like so:" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"id": "6706d92c-5785-4f09-9773-b9a944c493a5", | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"from nbdev.export import nb_export\n", | |
"nb_export('app.ipynb', lib_path='.', name='app')" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"id": "0182403f-d1d6-48c0-8e66-46aefb23a9ab", | |
"metadata": {}, | |
"source": [ | |
"<div>\n", | |
"<link rel=\"stylesheet\" href=\"https://gradio.s3-us-west-2.amazonaws.com/2.6.5/static/bundle.css\">\n", | |
"<div id=\"target\"></div>\n", | |
"<script src=\"https://gradio.s3-us-west-2.amazonaws.com/2.6.5/static/bundle.js\"></script>\n", | |
"<script>\n", | |
"launchGradioFromSpaces(\"abidlabs/question-answering\", \"#target\")\n", | |
"</script>\n", | |
"</div>" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"id": "84d5fd19-7880-459c-8382-b3574ed11141", | |
"metadata": {}, | |
"source": [ | |
"### Understanding what is generated" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"id": "9ea562e7-b67a-45df-b822-2f4528a307c2", | |
"metadata": {}, | |
"source": [ | |
"Notice how the contents of app.py only contains the exported cells from this notebook:" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"id": "4bae6a5c-58bc-4a0f-9aac-34c092150fdc", | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"data": { | |
"text/plain": [ | |
"\u001b[0;31m# AUTOGENERATED! DO NOT EDIT! File to edit: app.ipynb.\u001b[0m\u001b[0;34m\u001b[0m\n", | |
"\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n", | |
"\u001b[0;34m\u001b[0m\u001b[0;31m# %% auto 0\u001b[0m\u001b[0;34m\u001b[0m\n", | |
"\u001b[0;34m\u001b[0m\u001b[0m__all__\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0;34m'iface'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m'size'\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\n", | |
"\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n", | |
"\u001b[0;34m\u001b[0m\u001b[0;31m# %% app.ipynb 6\u001b[0m\u001b[0;34m\u001b[0m\n", | |
"\u001b[0;34m\u001b[0m\u001b[0;32mimport\u001b[0m \u001b[0mgradio\u001b[0m \u001b[0;32mas\u001b[0m \u001b[0mgr\u001b[0m\u001b[0;34m\u001b[0m\n", | |
"\u001b[0;34m\u001b[0m\u001b[0;32mfrom\u001b[0m \u001b[0mfastcore\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mnet\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0murljson\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mHTTPError\u001b[0m\u001b[0;34m\u001b[0m\n", | |
"\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n", | |
"\u001b[0;34m\u001b[0m\u001b[0;31m# %% app.ipynb 7\u001b[0m\u001b[0;34m\u001b[0m\n", | |
"\u001b[0;34m\u001b[0m\u001b[0;32mdef\u001b[0m \u001b[0msize\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mrepo\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0mstr\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\n", | |
"\u001b[0;34m\u001b[0m \u001b[0;34m\"Returns the size in GB of a HuggingFace Dataset.\"\u001b[0m\u001b[0;34m\u001b[0m\n", | |
"\u001b[0;34m\u001b[0m \u001b[0murl\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34mf'https://huggingface.co/api/datasets/{repo}'\u001b[0m\u001b[0;34m\u001b[0m\n", | |
"\u001b[0;34m\u001b[0m \u001b[0;32mtry\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mresp\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0murljson\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34mf'{url}/treesize/main'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", | |
"\u001b[0;34m\u001b[0m \u001b[0;32mexcept\u001b[0m \u001b[0mHTTPError\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0;34mf'Did not find repo: {url}'\u001b[0m\u001b[0;34m\u001b[0m\n", | |
"\u001b[0;34m\u001b[0m \u001b[0mgb\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mresp\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m'size'\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m/\u001b[0m \u001b[0;36m1e9\u001b[0m\u001b[0;34m\u001b[0m\n", | |
"\u001b[0;34m\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0;34mf'{gb:.2f} GB'\u001b[0m\u001b[0;34m\u001b[0m\n", | |
"\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\n", | |
"\u001b[0;34m\u001b[0m\u001b[0;31m# %% app.ipynb 11\u001b[0m\u001b[0;34m\u001b[0m\n", | |
"\u001b[0;34m\u001b[0m\u001b[0miface\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mgr\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mInterface\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mfn\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0msize\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0minputs\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mgr\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mText\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mvalue\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m\"tglcourse/CelebA-faces-cropped-128\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0moutputs\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m\"text\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\n", | |
"\u001b[0;34m\u001b[0m\u001b[0miface\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mlaunch\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mwidth\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m500\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n" | |
] | |
}, | |
"metadata": {}, | |
"output_type": "display_data" | |
} | |
], | |
"source": [ | |
"%pycat app.py" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"id": "a081bb0f-5cad-4b99-962b-4dd49cee61a2", | |
"metadata": {}, | |
"source": [ | |
"### Fill out `requirements.txt`\n", | |
"\n", | |
"You must supply a requirements.txt file so the gradio app knows how to build your dependencies. In this example, the only depdency other than gradio is `fastcore`. You don't need to specify gradio itself as a depdendency in `requirements.txt` so our `requirements.txt` file has only one dependency:" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"id": "0b611d9c-d262-4124-9e9e-4fe754ac4378", | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": [ | |
"fastcore" | |
] | |
} | |
], | |
"source": [ | |
"!cat requirements.txt" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"id": "f15d9c78-1f55-449e-8058-9af1832367a0", | |
"metadata": {}, | |
"source": [ | |
"## 4. Launch Your Gradio App\n", | |
"\n", | |
"To launch your gradio app, you need to commit the changes in the Hugging Face repo:\n", | |
"\n", | |
"```\n", | |
"git add -A; git commit -m \"Add application files\"; git push\n", | |
"```" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"id": "fa661f93-73b4-465a-9c22-cc38197505cb", | |
"metadata": {}, | |
"source": [ | |
"## 5. Voilà! Enjoy your Gradio App" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"id": "9b20ff94-6842-4078-9ec1-be740944e721", | |
"metadata": {}, | |
"source": [ | |
"After a couple of minutes, you will see your app published! This app is published at https://huggingface.co/spaces/hamel/hfspace_demo." | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"id": "52bacbff-63fc-492e-805c-6252c1ce2150", | |
"metadata": {}, | |
"outputs": [], | |
"source": [] | |
} | |
], | |
"metadata": { | |
"kernelspec": { | |
"display_name": "Python 3 (ipykernel)", | |
"language": "python", | |
"name": "python3" | |
} | |
}, | |
"nbformat": 4, | |
"nbformat_minor": 5 | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment