{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Build ML web app - assignment 2" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "## Final product\n", "\n", "- https://deepdream.streamlit.app/" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "## Code" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "`requirements.txt`" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "tensorflow-cpu==2.7.0\n", "streamlit==1.11.0\n", "click==8" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "`streamlit_app.py`" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from io import BytesIO\n", "import PIL.Image\n", "import numpy as np\n", "import os\n", "import requests\n", "import streamlit as st\n", "import tensorflow as tf\n", "import urllib\n", "import zipfile" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "'# Deeeeeeep dreeeeeeeeeam ~_~'\n", "\n", "# Basic setup\n", "__file__ = \"build-ml-web-app-1.ipynb\"\n", "THIS_FILE_DIR = os.path.abspath(os.path.dirname(__file__))\n", "MODEL_DIR = os.path.join(THIS_FILE_DIR, 'models')\n", "MODEL_FILENAME = os.path.join(MODEL_DIR, 'tensorflow_inception_graph.pb')" ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "2024-01-19 21:23:59.409 No runtime found, using MemoryCacheStorageManager\n", "2024-01-19 21:23:59.413 No runtime found, using MemoryCacheStorageManager\n" ] } ], "source": [ "@st.cache_data\n", "def download_model_from_web():\n", " if os.path.isfile(MODEL_FILENAME):\n", " return\n", "\n", " try:\n", " os.mkdir(MODEL_DIR)\n", " except FileExistsError:\n", " pass\n", "\n", " MODEL_ZIP_URL = (\n", " 'https://storage.googleapis.com/download.tensorflow.org/models/inception5h.zip')\n", " ZIP_FILE_NAME = 'inception5h.zip'\n", " ZIP_FILE_PATH = os.path.join(MODEL_DIR, ZIP_FILE_NAME)\n", " resp = requests.get(MODEL_ZIP_URL, stream=True)\n", "\n", " with open(ZIP_FILE_PATH, 'wb') as file_desc:\n", " for chunk in resp.iter_content(chunk_size=5000000):\n", " file_desc.write(chunk)\n", "\n", " zip_file = zipfile.ZipFile(ZIP_FILE_PATH)\n", " zip_file.extractall(path=MODEL_DIR)\n", "\n", " os.remove(ZIP_FILE_PATH)\n", "\n", "@st.cache_data\n", "def init_model():\n", " with tf.compat.v1.gfile.FastGFile(MODEL_FILENAME, 'rb') as f:\n", " graph_def = tf.compat.v1.GraphDef()\n", " graph_def.ParseFromString(f.read())\n", " return graph_def" ] }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [], "source": [ "download_model_from_web()\n", "graph_def = init_model()\n", "graph = tf.Graph()\n", "sess = tf.compat.v1.InteractiveSession(graph=graph)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Below is the actual logic for this app. It's a bit messy because it's almost a\n", "# straight copy/paste from the original DeepDream example repo, which is also\n", "# messy:\n", "# https://github.com/tensorflow/tensorflow/tree/master/tensorflow/examples/tutorials/deepdream\n", "\n", "t_input = tf.compat.v1.placeholder(np.float32, name='input')\n", "imagenet_mean = 117.0\n", "t_preprocessed = tf.expand_dims(t_input - imagenet_mean, 0)\n", "tf.import_graph_def(graph_def, {'input': t_preprocessed})\n", "\n", "\n", "def get_tensor(layer):\n", " '''Helper for getting layer output tensor'''\n", " return graph.get_tensor_by_name('%s:0' % layer)\n", "\n", "\n", "# Start with a gray image with a little noise.\n", "img_noise = np.random.uniform(size=(224, 224, 3)) + 100.0\n", "\n", "\n", "def write_image(dg, arr):\n", " arr = np.uint8(np.clip(arr/255.0, 0, 1)*255)\n", " dg.image(arr, use_column_width=True)\n", " return dg\n", "\n", "\n", "def tffunc(*argtypes):\n", " '''Helper that transforms TF-graph generating function into a regular one.\n", "\n", " See \"resize\" function below.\n", " '''\n", " placeholders = list(map(tf.compat.v1.placeholder, argtypes))\n", "\n", " def wrap(f):\n", " out = f(*placeholders)\n", "\n", " def wrapper(*args, **kw):\n", " return out.eval(\n", " dict(zip(placeholders, args)), session=kw.get('session'))\n", " return wrapper\n", " return wrap\n", "\n", "\n", "# Helper function that uses TF to resize an image\n", "def resize(img, size):\n", " img = tf.expand_dims(img, 0)\n", " return tf.compat.v1.image.resize_bilinear(img, size)[0, :, :, :]\n", "\n", "\n", "resize = tffunc(np.float32, np.int32)(resize)\n", "\n", "\n", "def calc_grad_tiled(img, t_grad, tile_size=512):\n", " '''Compute the value of tensor t_grad over the image in a tiled way.\n", "\n", " Random shifts are applied to the image to blur tile boundaries over\n", " multiple iterations.\n", " '''\n", " sz = tile_size\n", " h, w = img.shape[:2]\n", " sx, sy = np.random.randint(sz, size=2)\n", " img_shift = np.roll(np.roll(img, sx, 1), sy, 0)\n", " grad = np.zeros_like(img)\n", " for y in range(0, max(h-sz//2, sz), sz):\n", " for x in range(0, max(w-sz//2, sz), sz):\n", " sub = img_shift[y:y+sz, x:x+sz]\n", " g = sess.run(t_grad, {t_input: sub})\n", " grad[y:y+sz, x:x+sz] = g\n", " return np.roll(np.roll(grad, -sx, 1), -sy, 0)\n", "\n", "\n", "def do_deepdream(\n", " t_obj, img_in=img_noise, iter_n=10, step=1.5, octave_n=4,\n", " octave_scale=1.4):\n", " t_score = tf.reduce_mean(t_obj)\n", " t_grad = tf.gradients(t_score, t_input)[0]\n", "\n", " # split the image into a number of octaves\n", " octaves = []\n", " for i in range(octave_n-1):\n", " hw = img_in.shape[:2]\n", " lo = resize(img_in, np.int32(np.float32(hw)/octave_scale))\n", " hi = img_in-resize(lo, hw)\n", " img_in = lo\n", " octaves.append(hi)\n", "\n", " image_widget = st.empty()\n", " text_template = 'Octave: %s\\nIteration: %s'\n", " text_widget = st.sidebar.text(text_template % (0, 0))\n", " progress_widget = st.sidebar.progress(0)\n", " p = 0.0\n", "\n", " # generate details octave by octave\n", " for octave in range(octave_n):\n", " if octave > 0:\n", " hi = octaves[-octave]\n", " img_in = resize(img_in, hi.shape[:2])+hi\n", " for i in range(iter_n):\n", " g = calc_grad_tiled(img_in, t_grad)\n", " img_in += g*(step / (np.abs(g).mean()+1e-7))\n", " p += 1\n", " progress_widget.progress(p / (octave_n * iter_n))\n", "\n", " write_image(image_widget, img_in)\n", " text_widget.text(text_template % (octave, i))" ] }, { "cell_type": "code", "execution_count": 26, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "2024-01-19 21:32:23.032 No runtime found, using MemoryCacheStorageManager\n", "2024-01-19 21:32:23.035 No runtime found, using MemoryCacheStorageManager\n" ] } ], "source": [ "layers = [\n", " op.name for op in graph.get_operations()\n", " if op.type == 'Conv2D' and 'import/' in op.name\n", " ]\n", "\n", "\n", "@st.cache_data\n", "def read_file_from_url(url):\n", " return urllib.request.urlopen(url).read()\n", "\n", "\n", "# Sidebar controls:\n", "\n", "# Temporary config option to remove deprecation warning.\n", "st.set_option('deprecation.showfileUploaderEncoding', False)\n", "\n", "MAX_IMG_WIDTH = 600\n", "MAX_IMG_HEIGHT = 400\n", "DEFAULT_IMAGE_URL = 'https://i.imgur.com/dOPMzXl.jpg'\n", "\n", "\n", "file_obj = st.sidebar.file_uploader('Choose an image:', ('jpg', 'jpeg'))\n", "\n", "if not file_obj:\n", " file_obj = BytesIO(read_file_from_url(DEFAULT_IMAGE_URL))\n", "\n", "img_in = PIL.Image.open(file_obj)\n", "\n", "# img_in.thumbnail((MAX_IMG_WIDTH, MAX_IMG_HEIGHT), PIL.Image.ANTIALIAS)\n", "img_in.thumbnail((MAX_IMG_WIDTH, MAX_IMG_HEIGHT))\n", "img_in = np.float32(img_in)\n", "\n", "\n", "# Picking some internal layer. Note that we use outputs before applying the\n", "# ReLU nonlinearity to have non-zero gradients for features with negative\n", "# initial activations.\n", "\n", "max_value = len(layers) - 1\n", "layer_num = st.sidebar.slider('Layer to visualize', 0, max_value, min(58, max_value))\n", "layer = layers[layer_num]\n", "\n", "channels = int(get_tensor(layer).get_shape()[-1])\n", "max_value = channels - 1\n", "channel = st.sidebar.slider('Channel to visualize', 0, max_value, min(62, max_value))\n", "\n", "octaves = st.sidebar.slider('Octaves', 1, 30, 4)\n", "\n", "iterations = st.sidebar.slider('Iterations per octave', 1, 30, 10)\n", "\n", "\n", "# Show original image and final image, computing DeepDream on it iteratively.\n", "\n", "'## Original image'\n", "\n", "write_image(st, img_in)\n", "\n", "\n", "'## Output'\n", "\n", "out = do_deepdream(\n", " get_tensor(layer)[:, :, :, channel], img_in, octave_n=octaves,\n", " iter_n=iterations)\n" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "## Deployment on streamlit cloud\n", "\n", "- https://docs.streamlit.io/streamlit-community-cloud/get-started/deploy-an-app" ] } ], "metadata": { "kernelspec": { "display_name": "py39", "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.9.18" }, "orig_nbformat": 4 }, "nbformat": 4, "nbformat_minor": 2 }