From 57e468196a8e0e20f711550df9c39a12f5f6427a Mon Sep 17 00:00:00 2001 From: Kevin Cheung Date: Thu, 16 Apr 2020 17:57:18 +0000 Subject: [PATCH 1/9] ML model management quickstart --- machine-learning/README.md | 69 +++++++++++++++ machine-learning/manage-ml.py | 156 ++++++++++++++++++++++++++++++++++ 2 files changed, 225 insertions(+) create mode 100644 machine-learning/README.md create mode 100644 machine-learning/manage-ml.py diff --git a/machine-learning/README.md b/machine-learning/README.md new file mode 100644 index 0000000..dd60209 --- /dev/null +++ b/machine-learning/README.md @@ -0,0 +1,69 @@ +# Firebase Admin Python SDK ML quickstart + +This sample script shows how you can use the Firebase Admin SDK to manage your +Firebase-hosted ML models. + +## Setup + +1. Install the Admin SDK (probably in a virtual environment): + + ``` + $ pip install -U pip setuptools + $ pip install firebase_admin + ``` + +2. Clone the quickstart repository and change to the `machine-learning` +   directory: + + ``` + $ git clone https://github.com/firebase/quickstart-python.git + $ cd quickstart-python/machine-learning + $ chmod u+x manage-ml.py # Optional + ``` + +3. If you don't already have a Firebase project, create a new project in the + [Firebase console](https://console.firebase.google.com/). Then, open your + project in the Firebase console and do the following: + + 1. On the [Settings][service-account] page, create a service account and + download the service account key file. Keep this file safe, since it + grants administrator access to your project. + 2. On the Storage page, enable Cloud Storage. Take note of your default + bucket name (or create a new bucket for ML models.) + 3. On the ML Kit page, click **Get started** if you haven't yet enabled ML + Kit. + +4. In the [Google APIs console][enable-api], open your Firebase project and + enable the Firebase ML API. + +[enable-api]: https://console.developers.google.com/apis/library/firebaseml.googleapis.com?project=_ + +5. At the top of `manage-ml.py`, set the `SERVICE_ACCOUNT_KEY` and + `STORAGE_BUCKET`: + + ``` + SERVICE_ACCOUNT_KEY = '/path/to/your/service_account_key.json' + STORAGE_BUCKET = 'your-storage-bucket' + ``` + +[service-account]: https://firebase.google.com/project/_/settings/serviceaccounts/adminsdk + +## Example session + +``` +$ ./manage-ml.py list +fish_detector 8716935 vision +barcode_scanner 8716959 vision +smart_reply 8716981 natural_language +$ ./manage-ml.py new ~/yak.tflite yak_detector --tags vision,experimental +Uploading model to Cloud Storage... +Model uploaded and published: +yak_detector 8717019 experimental, vision +$ ./manage-ml.py update 8717019 --remove_tags experimental +$ ./manage-ml.py delete 8716959 +$ ./manage-ml.py list +fish_detector 8716935 vision +smart_reply 8716981 natural_language +yak_detector 8717019 vision +$ +``` diff --git a/machine-learning/manage-ml.py b/machine-learning/manage-ml.py new file mode 100644 index 0000000..a35682b --- /dev/null +++ b/machine-learning/manage-ml.py @@ -0,0 +1,156 @@ +#!/usr/bin/env python3 +"""Firebase Admin SDK ML quickstart example.""" + +import argparse + +import firebase_admin +from firebase_admin import ml + + +# TODO(user): Configure for your project. (See README.md.) +SERVICE_ACCOUNT_KEY = '/path/to/your/service_account_key.json' +STORAGE_BUCKET = 'your-storage-bucket' + +credentials = firebase_admin.credentials.Certificate(SERVICE_ACCOUNT_KEY) +firebase_admin.initialize_app(credentials, options={ + 'storageBucket': STORAGE_BUCKET +}) + + +def upload_model(model_file, name, tags=None): + """Upload a tflite model file to the project and publish it.""" + # Load a tflite file and upload it to Cloud Storage + print('Uploading to Cloud Storage...') + model_source = ml.TFLiteGCSModelSource.from_tflite_model_file(model_file) + + # Create the model object + tflite_format = ml.TFLiteFormat(model_source=model_source) + model = ml.Model( + display_name=name, + model_format=tflite_format) + if tags is not None: + model.tags = tags + + # Add the model to your Firebase project and publish it + new_model = ml.create_model(model) + ml.publish_model(new_model.model_id) + + print('Model uploaded and published:') + tags = ', '.join(new_model.tags) if new_model.tags is not None else '' + print('{:<20}{:<10} {}'.format(new_model.display_name, new_model.model_id, + tags)) + + +def list_models(filter_exp=''): + """List the models in the project.""" + models = ml.list_models(list_filter=filter_exp).iterate_all() + for model in models: + tags = ', '.join(model.tags) if model.tags is not None else '' + print('{:<20}{:<10} {}'.format(model.display_name, model.model_id, tags)) + + +def update_model(model_id, model_file=None, name=None, + new_tags=None, remove_tags=None): + """Update one of the project's models.""" + model = ml.get_model(model_id) + + if model_file is not None: + # Load a tflite file and upload it to Cloud Storage + print('Uploading to Cloud Storage...') + model_source = ml.TFLiteGCSModelSource.from_tflite_model_file(model_file) + tflite_format = ml.TFLiteFormat(model_source=model_source) + model.model_format = tflite_format + + if name is not None: + model.display_name = name + + if new_tags is not None: + model.tags = new_tags if model.tags is None else model.tags + new_tags + + if remove_tags is not None and model.tags is not None: + model.tags = list(set(model.tags).difference(set(remove_tags))) + + updated_model = ml.update_model(model) + ml.publish_model(updated_model.model_id) + + +def delete_model(model_id): + """Delete a model from the project.""" + ml.delete_model(model_id) + + +# The rest of the file just parses the command line and dispatches one of the +# functions above. + + +def main(): + main_parser = argparse.ArgumentParser() + subparsers = main_parser.add_subparsers( + dest='command', required=True, metavar='command') + + new_parser = subparsers.add_parser( + 'new', help='upload a tflite model to your project') + new_parser.add_argument( + 'model_file', type=str, help='path to the tflite file') + new_parser.add_argument( + 'name', type=str, help='display name for the new model') + new_parser.add_argument( + '-t', '--tags', type=str, help='comma-separated list of tags') + + list_parser = subparsers.add_parser( + 'list', help='list your project\'s models') + list_parser.add_argument( + '-f', '--filter', type=str, default='', + help='''filter expression to limit results (see: + https://firebase.google.com/docs/ml-kit/manage-hosted-models#list_your_projects_models)''') + + update_parser = subparsers.add_parser( + 'update', help='update one of your project\'s models') + update_parser.add_argument( + 'model_id', type=valid_id, help='the ID of the model you want to update') + update_parser.add_argument( + '-m', '--model_file', type=str, help='path to a new tflite file') + update_parser.add_argument( + '-n', '--name', type=str, help='display name for the model') + update_parser.add_argument( + '-t', '--new_tags', type=str, + help='comma-separated list of tags to add') + update_parser.add_argument( + '-d', '--remove_tags', type=str, + help='comma-separated list of tags to remove') + + delete_parser = subparsers.add_parser( + 'delete', help='delete a model from your project') + delete_parser.add_argument( + 'model_id', type=valid_id, help='the ID of the model you want to delete') + + args = main_parser.parse_args() + try: + if args.command == 'new': + tags = args.tags.split(',') if args.tags is not None else None + upload_model(args.model_file, args.name, tags) + elif args.command == 'list': + list_models(args.filter) + elif args.command == 'update': + new_tags = args.new_tags.split(',') if args.new_tags is not None else None + remove_tags = ( + args.remove_tags.split(',') if args.remove_tags is not None else None) + update_model(args.model_id, args.model_file, args.name, + new_tags, remove_tags) + elif args.command == 'delete': + delete_model(args.model_id) + except firebase_admin.exceptions.NotFoundError: + print('ERROR: Model not found. Make sure you\'re specifying a valid' + ' numerical model ID.') + + +def valid_id(model_id): + try: + val = int(model_id) + return str(val) + except ValueError: + raise argparse.ArgumentTypeError('must be a numerical model ID.') + + +if __name__ == '__main__': + main() From 5de7d5b47d43710b195bfc7a32836850e16ae36e Mon Sep 17 00:00:00 2001 From: Kevin Cheung Date: Tue, 28 Apr 2020 17:20:21 -0700 Subject: [PATCH 2/9] ML Colab/Jupyter notebook --- .../Firebase_ML_API_Tutorial.ipynb | 979 ++++++++++++++++++ 1 file changed, 979 insertions(+) create mode 100644 machine-learning/Firebase_ML_API_Tutorial.ipynb diff --git a/machine-learning/Firebase_ML_API_Tutorial.ipynb b/machine-learning/Firebase_ML_API_Tutorial.ipynb new file mode 100644 index 0000000..c1fadcd --- /dev/null +++ b/machine-learning/Firebase_ML_API_Tutorial.ipynb @@ -0,0 +1,979 @@ +{ + "nbformat": 4, + "nbformat_minor": 0, + "metadata": { + "colab": { + "name": "Firebase ML API Tutorial", + "provenance": [], + "collapsed_sections": [], + "toc_visible": true + }, + "kernelspec": { + "name": "python3", + "display_name": "Python 3" + } + }, + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "ZRvF80bJ1imr", + "colab_type": "text" + }, + "source": [ + "```\n", + "# Copyright 2020 Google LLC\n", + "#\n", + "# 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.\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "XK1TC6JmaSo5", + "colab_type": "text" + }, + "source": [ + "This Colab demonstrates how you can use the Firebase Admin Python SDK from a Jupyter notebook to manage your Firebase-hosted ML models." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "ThfwN5km4l7X", + "colab_type": "text" + }, + "source": [ + "# **1. Install the Admin SDK and TensorFlow** " + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "x4qsJu_y4onY", + "colab_type": "text" + }, + "source": [ + "Install the Firebase Admin SDK and TensorFlow. If you're running this notebook in a Google Colab environment, you can skip this step." + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "1sTVOhJC20Ce", + "colab_type": "code", + "colab": {} + }, + "source": [ + "%pip install 'firebase_admin>=4.1.0'\n", + "%pip install 'tensorflow>=2.1.0'" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "xTklrznZYR_F", + "colab_type": "text" + }, + "source": [ + "# **2. Set up a Firebase project**" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "CHjogYcnYcgb", + "colab_type": "text" + }, + "source": [ + "Before you can continue, you need to set up a Firebase project:\n", + "\n", + "1. If you don't already have a Firebase project, create a new project in the [Firebase console](https://console.firebase.google.com/). Then, open your project and do the following:\n", + "\n", + " 1. On the [Settings](https://console.firebase.google.com/project/_/settings/serviceaccounts/adminsdk) page, create a service account and download the service account key file. Keep this file safe, since it grants administrator access to your project.\n", + "\n", + " 1. On the [Storage](https://console.firebase.google.com/project/_/storage) page, enable Cloud Storage. Take note of your bucket name.\n", + "\n", + " You need a Storage bucket to temporarily store model files while adding them to your Firebase project. If you are on the Blaze plan, you can create and use a bucket other than the default for this purpose.\n", + "\n", + " 1. On the [ML Kit](https://console.firebase.google.com/project/_/ml) page, click **Get started** if you haven't yet enabled ML Kit.\n", + "\n", + "1. In the [Google APIs console](https://console.developers.google.com/apis/library/firebaseml.googleapis.com?project=_), open your Firebase project and enable the Firebase ML API.\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "qJ07z89GsNqh", + "colab_type": "text" + }, + "source": [ + "# **3. Upload the json service account key file for your project to the runtime**" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "A9w5FJ0cXia2", + "colab_type": "text" + }, + "source": [ + "Then, upload the service account key file you got in the previous step:" + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "8OeMQU9az4gh", + "colab_type": "code", + "colab": {} + }, + "source": [ + "from google.colab import files\n", + "\n", + "uploaded = files.upload()\n", + "service_acct_file = next(iter(uploaded)) # The name of the first uploaded file" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "HEVNf2rxwgfo", + "colab_type": "text" + }, + "source": [ + "# **4. Set your Google Application Credentials location**" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "39qaiVVqeA51", + "colab_type": "text" + }, + "source": [ + "Set the `GOOGLE_APPLICATION_CREDENTIALS` environmental variable to the location of the key file:" + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "fdGKEWCH1SsT", + "colab_type": "code", + "colab": {} + }, + "source": [ + "import os\n", + "os.environ[\"GOOGLE_APPLICATION_CREDENTIALS\"]=\"/content/{0}\".format(service_acct_file)" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "ma-McTrY0cGE", + "colab_type": "text" + }, + "source": [ + "# **5. Initialize Firebase Admin**" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "X9xyubP_ekA5", + "colab_type": "text" + }, + "source": [ + "Import the `firebase_admin` module and initialize the SDK with the name of your Storage bucket. Be sure the Storage bucket is in the same Firebase project as your service account. Your project's default bucket looks like `your-project-id.appspot.com`." + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "2sLO335O7IIF", + "colab_type": "code", + "colab": {} + }, + "source": [ + "storage_bucket = input('Storage bucket (no \"gs://\"): ')" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "metadata": { + "id": "YxYkdFBs0oS1", + "colab_type": "code", + "colab": {} + }, + "source": [ + "import firebase_admin\n", + "from firebase_admin import ml\n", + "\n", + "firebase_admin.initialize_app(options={'storageBucket': storage_bucket})" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "DLOmPtM77wMI", + "colab_type": "text" + }, + "source": [ + "# **6. Train your model**" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "oNEyKOO_ivrO", + "colab_type": "text" + }, + "source": [ + "Next, train your model.\n", + "\n", + "In a real notebook, you'd use a model architecture designed for your use case and provide your own training data. For this demo, just train a trivial model:" + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "6lrYxa4Z70Xp", + "colab_type": "code", + "colab": {} + }, + "source": [ + "import tensorflow as tf\n", + "\n", + "# Create a simple Keras model.\n", + "x = [-1, 0, 1, 2, 3, 4]\n", + "y = [-3, -1, 1, 3, 5, 7]\n", + "\n", + "model_binary = tf.keras.models.Sequential(\n", + " [tf.keras.layers.Dense(units=1, input_shape=[1])])\n", + "model_binary.compile(optimizer='sgd', loss='mean_squared_error')\n", + "model_binary.fit(x, y, epochs=3)" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "1LK9d37xYDet", + "colab_type": "text" + }, + "source": [ + "# **7. Convert & upload your model**" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "mGe3os8Xkf7F", + "colab_type": "text" + }, + "source": [ + "Now that you have a trained model, you can upload it to Firebase and make it available to your iOS and Android apps.\n", + "\n", + "First, convert the model to TensorFlow Lite and upload it to Cloud Storage. With the Admin SDK, this is a single call:" + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "SLZvUStHa7T3", + "colab_type": "code", + "colab": {} + }, + "source": [ + "# This takes the Keras model, converts it to a TFLite model, and uploads it to\n", + "# your bucket as my_model.tflite\n", + "source = ml.TFLiteGCSModelSource.from_keras_model(model_binary, 'my_model.tflite')\n", + "print (source.gcs_tflite_uri)" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "RRA56_U9o2P9", + "colab_type": "text" + }, + "source": [ + "# **8. Create a Model object**" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "HzB_CV5yo9Ms", + "colab_type": "text" + }, + "source": [ + "Next, create a `Model` object, specifying the model's Cloud Storage source and the name of your model. (You will use the name you specify here to download the model in your iOS and Android apps.)" + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "rljlmVwgo1dk", + "colab_type": "code", + "colab": {} + }, + "source": [ + "model_format = ml.TFLiteFormat(model_source=source)\n", + "sdk_model_1 = ml.Model(display_name=\"my_model_1\", model_format=model_format)" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "0Hd7aZrpdOqm", + "colab_type": "text" + }, + "source": [ + "# **9. Add the model to your Firebase project**" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "ZEMJ-B2LqhPt", + "colab_type": "text" + }, + "source": [ + "Add the model to your Firebase project by calling `create_model()`. When you do so, the model gets copied from Cloud Storage.\n", + "\n", + "Note that this step will fail if your project already has a model named `my_model_1`. If this happens, [delete the model with the Firebase console](https://console.firebase.google.com/project/_/ml/custom) and try again." + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "AOjQVSU2eG8v", + "colab_type": "code", + "colab": {} + }, + "source": [ + "firebase_model_1 = ml.create_model(sdk_model_1)\n", + "if firebase_model_1.validation_error:\n", + " raise Exception(firebase_model_1.validation_error)\n", + "print(firebase_model_1.as_dict())" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "lMthadQ4qU32", + "colab_type": "text" + }, + "source": [ + "#**10. Publish the model**" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "oNTN-XEFvd7g", + "colab_type": "text" + }, + "source": [ + "Finally, publish your model:" + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "qK2S0KD7qbIB", + "colab_type": "code", + "colab": {} + }, + "source": [ + "model_id = firebase_model_1.model_id\n", + "firebase_model_1 = ml.publish_model(model_id)" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "IpxBS8HzvvWm", + "colab_type": "text" + }, + "source": [ + "Now that you've published the model, you can [use it in your apps](https://firebase.google.com/docs/ml-kit/use-custom-models)." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "ZHSN6vM7b_pH", + "colab_type": "text" + }, + "source": [ + "# **11. Create a second model (so we can update)**" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "lhuuTNtHwduW", + "colab_type": "text" + }, + "source": [ + "You can update a published model with a new model file. When you do so, client apps automatically download and use the new model.\n", + "\n", + "For demonstration purposes, first save one of Keras's prepackaged models to a saved model directory:" + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "1pLVpHi4cF6e", + "colab_type": "code", + "colab": {} + }, + "source": [ + "tf.saved_model.save(tf.keras.applications.MobileNet(), '/tmp/saved_model/1')" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "veOOV0ZxjMf-", + "colab_type": "text" + }, + "source": [ + "#**12. Create a second model source and model format from the new model**" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "XIyAhNEFGNIN", + "colab_type": "text" + }, + "source": [ + "Now, convert the saved model to TensorFlow Lite and upload it to Cloud Storage. This time, you're converting a TensorFlow saved model to TensorFlow Lite, but you could also convert a Keras model like you did earlier, or convert a Keras model saved as an HDF5 (`.h5`) file." + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "hn5IgonpjWmy", + "colab_type": "code", + "colab": {} + }, + "source": [ + "# This takes the saved model directory, converts it to TFLite and writes it to your bucket as my_model_2.tflite\n", + "source2 = ml.TFLiteGCSModelSource.from_saved_model('/tmp/saved_model/1', 'my_model_2.tflite')" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "wtXV_x7ko1Dz", + "colab_type": "text" + }, + "source": [ + "#**13. Modify the local model and then call the API Update**" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "m2WyUn4SJWEC", + "colab_type": "text" + }, + "source": [ + "Change the original `Model` object's model source and (optionally) metadata, then call `update_model()`:" + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "MiRQi2Iqo8PK", + "colab_type": "code", + "colab": {} + }, + "source": [ + "model_format2 = ml.TFLiteFormat(model_source=source2)\n", + "firebase_model_1.model_format = model_format2\n", + "firebase_model_1.tags = ['tag1', 'tag2'] # replaces any existing tags with these tags.\n", + "\n", + "firebase_model_2 = ml.update_model(firebase_model_1)\n", + "if firebase_model_2.validation_error:\n", + " raise Exception(firebase_model_2.validation_error)\n", + "print(firebase_model_2.as_dict())\n" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "uZzu3k5EXMma", + "colab_type": "text" + }, + "source": [ + "#**14. Publish the model_format2 model**" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "XaiWqq4EkWBU", + "colab_type": "text" + }, + "source": [ + "After you update the model, re-publish it:" + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "iGsNmx2oXUFS", + "colab_type": "code", + "colab": {} + }, + "source": [ + "firebase_model_2 = ml.publish_model(model_id)\n", + "print(firebase_model_2.as_dict())" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "m2YmhA4Ab1DH", + "colab_type": "text" + }, + "source": [ + "#**15. Get the model**" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "_01adxyEk0JN", + "colab_type": "text" + }, + "source": [ + "If you need to get a `Model` object from one of your project's models, use `get_model()`:" + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "9Q1jQXYJb4qx", + "colab_type": "code", + "colab": {} + }, + "source": [ + "firebase_model_get = ml.get_model(model_id)\n", + "print(firebase_model_get.as_dict())" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "OphmZAVdcqxO", + "colab_type": "text" + }, + "source": [ + "#**16. List the models**" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "kOIjFLyGlpyq", + "colab_type": "text" + }, + "source": [ + "To list your project's models, iterate over the result of `list_models()`:" + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "ebJBI9NVcwjp", + "colab_type": "code", + "colab": {} + }, + "source": [ + "firebase_models_list = ml.list_models()\n", + "iterator = firebase_models_list.iterate_all()\n", + "for m in iterator:\n", + " print(m.as_dict())" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "p_4yAHeFg7BA", + "colab_type": "text" + }, + "source": [ + "#**17. Make more models so we can show lists better**" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "9vOkbq1lmA2B", + "colab_type": "text" + }, + "source": [ + "The Admin SDK can help you manage projects with many models.\n", + "\n", + "To demonstrate this, create some more models:" + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "ce3zB3HLhCTf", + "colab_type": "code", + "colab": {} + }, + "source": [ + "list_model_1 = ml.create_model(ml.Model(display_name='my_model_2', tags=['tag2', 'tag3'], model_format=model_format))\n", + "list_model_2 = ml.create_model(ml.Model(display_name='my_model_3', tags=['tag3'], model_format=model_format))\n", + "list_model_3 = ml.create_model(ml.Model(display_name='cat_model_1', tags=['cat'], model_format=model_format))\n", + "list_model_4 = ml.create_model(ml.Model(display_name='cat_model_2', tags=['cat'], model_format=model_format))\n", + "list_model_5 = ml.create_model(ml.Model(display_name='new_cat_model_007', tags=['cat'], model_format=model_format))" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "QmxpYBsQnua4", + "colab_type": "text" + }, + "source": [ + "#**18. Publish some of them**" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "OvYY0orEmtsT", + "colab_type": "text" + }, + "source": [ + "And publish some of them:" + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "x6kzkhWjnyXr", + "colab_type": "code", + "colab": {} + }, + "source": [ + "list_model_2 = ml.publish_model(list_model_2.model_id)\n", + "list_model_4 = ml.publish_model(list_model_4.model_id)" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "aAIAexOTpkVu", + "colab_type": "text" + }, + "source": [ + "#**19. Listing with page size**" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "BZC1dbfnm43F", + "colab_type": "text" + }, + "source": [ + "You can specify how many results to return at a time:" + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "kf3rUxE0pqzJ", + "colab_type": "code", + "colab": {} + }, + "source": [ + "firebase_models_list_2 = ml.list_models(page_size=3)\n", + "for m in firebase_models_list_2.models:\n", + " print (m.as_dict())" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "V0WpL3UarTlA", + "colab_type": "text" + }, + "source": [ + "#**20. Listing the next page**" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "iPh886g2nNm9", + "colab_type": "text" + }, + "source": [ + "Get the next page of results:" + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "iodXtP_XrYGT", + "colab_type": "code", + "colab": {} + }, + "source": [ + "firebase_models_list_3 = firebase_models_list_2.get_next_page()\n", + "for m in firebase_models_list_3.models:\n", + " print (m.as_dict())" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "LJwxO53KnYHA", + "colab_type": "text" + }, + "source": [ + "When you retrieve the final page, `get_next_page()` returns `None`." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Xbv-NAm5tT_5", + "colab_type": "text" + }, + "source": [ + "#**21. Filtering lists**" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "-aNfitPHoF9P", + "colab_type": "text" + }, + "source": [ + "You can also filter the results.\n", + "\n", + "Filter by display name:" + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "9eS1kVSwtgwJ", + "colab_type": "code", + "colab": {} + }, + "source": [ + "firebase_models_list = ml.list_models(list_filter='display_name=cat_model_1')\n", + "for m in firebase_models_list.models:\n", + " print (m.as_dict())\n" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "mLNhAGtjuxqF", + "colab_type": "text" + }, + "source": [ + "Filter by display name prefix (note that only prefix matching is supported; you can't do general wildcard matching):" + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "YpihoJvnu1PU", + "colab_type": "code", + "colab": {} + }, + "source": [ + "firebase_models_list = ml.list_models(list_filter='display_name:cat_*')\n", + "for m in firebase_models_list.models:\n", + " print (m.as_dict())" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "e54EB4ngwBM6", + "colab_type": "text" + }, + "source": [ + "Filter by tag:" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "woK9u8P0wJ0a", + "colab": {} + }, + "source": [ + "firebase_models_list = ml.list_models(list_filter='tags: cat')\n", + "for m in firebase_models_list.models:\n", + " print (m.as_dict())" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "ZrSJImzSyRoI", + "colab_type": "text" + }, + "source": [ + "Filter by publish state:" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "G2xmcFuMyXRX", + "colab": {} + }, + "source": [ + "firebase_models_list = ml.list_models(list_filter='state.published = true')\n", + "for m in firebase_models_list.models:\n", + " print (m.as_dict())" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "-EagFFnwzyI5", + "colab_type": "text" + }, + "source": [ + "Combine filters:" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "3U40mUaQz9KE", + "colab": {} + }, + "source": [ + "firebase_models_list = ml.list_models(list_filter='(display_name: cat_* OR tags: tag3) AND NOT state.published = true')\n", + "for m in firebase_models_list.models:\n", + " print (m.as_dict())" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "sYur7GCQBVas", + "colab_type": "text" + }, + "source": [ + "# **22. Clean up**" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "v1BKMU30Blnr", + "colab_type": "text" + }, + "source": [ + "That's it!\n", + "\n", + "Clean up by deleting the example models:" + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "wGRXAfFiCHOF", + "colab_type": "code", + "colab": {} + }, + "source": [ + "ml.delete_model(model_id)\n", + "ml.delete_model(list_model_1.model_id)\n", + "ml.delete_model(list_model_2.model_id)\n", + "ml.delete_model(list_model_3.model_id)\n", + "ml.delete_model(list_model_4.model_id)\n", + "ml.delete_model(list_model_5.model_id)" + ], + "execution_count": 0, + "outputs": [] + } + ] +} \ No newline at end of file From 7f085d1d8cc862fbb65bdc4188ac09fc9df64110 Mon Sep 17 00:00:00 2001 From: Kevin Cheung Date: Wed, 29 Apr 2020 15:47:01 -0700 Subject: [PATCH 3/9] Update ML notebook to work with standalone Jupyter --- .../Firebase_ML_API_Tutorial.ipynb | 358 ++++++++++-------- 1 file changed, 204 insertions(+), 154 deletions(-) diff --git a/machine-learning/Firebase_ML_API_Tutorial.ipynb b/machine-learning/Firebase_ML_API_Tutorial.ipynb index c1fadcd..f49057f 100644 --- a/machine-learning/Firebase_ML_API_Tutorial.ipynb +++ b/machine-learning/Firebase_ML_API_Tutorial.ipynb @@ -9,16 +9,29 @@ "toc_visible": true }, "kernelspec": { - "name": "python3", - "display_name": "Python 3" + "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.7.7" } }, "cells": [ { "cell_type": "markdown", "metadata": { - "id": "ZRvF80bJ1imr", - "colab_type": "text" + "colab_type": "text", + "id": "ZRvF80bJ1imr" }, "source": [ "```\n", @@ -41,8 +54,8 @@ { "cell_type": "markdown", "metadata": { - "id": "XK1TC6JmaSo5", - "colab_type": "text" + "colab_type": "text", + "id": "XK1TC6JmaSo5" }, "source": [ "This Colab demonstrates how you can use the Firebase Admin Python SDK from a Jupyter notebook to manage your Firebase-hosted ML models." @@ -51,8 +64,8 @@ { "cell_type": "markdown", "metadata": { - "id": "ThfwN5km4l7X", - "colab_type": "text" + "colab_type": "text", + "id": "ThfwN5km4l7X" }, "source": [ "# **1. Install the Admin SDK and TensorFlow** " @@ -61,8 +74,8 @@ { "cell_type": "markdown", "metadata": { - "id": "x4qsJu_y4onY", - "colab_type": "text" + "colab_type": "text", + "id": "x4qsJu_y4onY" }, "source": [ "Install the Firebase Admin SDK and TensorFlow. If you're running this notebook in a Google Colab environment, you can skip this step." @@ -71,8 +84,8 @@ { "cell_type": "code", "metadata": { - "id": "1sTVOhJC20Ce", "colab_type": "code", + "id": "1sTVOhJC20Ce", "colab": {} }, "source": [ @@ -85,8 +98,8 @@ { "cell_type": "markdown", "metadata": { - "id": "xTklrznZYR_F", - "colab_type": "text" + "colab_type": "text", + "id": "xTklrznZYR_F" }, "source": [ "# **2. Set up a Firebase project**" @@ -95,8 +108,8 @@ { "cell_type": "markdown", "metadata": { - "id": "CHjogYcnYcgb", - "colab_type": "text" + "colab_type": "text", + "id": "CHjogYcnYcgb" }, "source": [ "Before you can continue, you need to set up a Firebase project:\n", @@ -118,8 +131,8 @@ { "cell_type": "markdown", "metadata": { - "id": "qJ07z89GsNqh", - "colab_type": "text" + "colab_type": "text", + "id": "qJ07z89GsNqh" }, "source": [ "# **3. Upload the json service account key file for your project to the runtime**" @@ -128,8 +141,8 @@ { "cell_type": "markdown", "metadata": { - "id": "A9w5FJ0cXia2", - "colab_type": "text" + "colab_type": "text", + "id": "A9w5FJ0cXia2" }, "source": [ "Then, upload the service account key file you got in the previous step:" @@ -138,15 +151,26 @@ { "cell_type": "code", "metadata": { - "id": "8OeMQU9az4gh", "colab_type": "code", + "id": "8OeMQU9az4gh", "colab": {} }, "source": [ - "from google.colab import files\n", + "import ipywidgets\n", "\n", - "uploaded = files.upload()\n", - "service_acct_file = next(iter(uploaded)) # The name of the first uploaded file" + "uploader = ipywidgets.FileUpload(\n", + " accept='.json',\n", + " multiple=False\n", + ")\n", + "service_acct_file = {}\n", + "def handle_upload(change):\n", + " service_acct_file['name'] = next(iter(change['owner'].value))\n", + " service_acct_file['data'] = change['owner'].value[service_acct_file['name']]['content']\n", + " with open(service_acct_file['name'], 'wb') as f:\n", + " f.write(service_acct_file['data'])\n", + " print('Uploaded {}'.format(service_acct_file['name']))\n", + "uploader.observe(handle_upload, names='data')\n", + "display(uploader)\n" ], "execution_count": 0, "outputs": [] @@ -154,8 +178,8 @@ { "cell_type": "markdown", "metadata": { - "id": "HEVNf2rxwgfo", - "colab_type": "text" + "colab_type": "text", + "id": "HEVNf2rxwgfo" }, "source": [ "# **4. Set your Google Application Credentials location**" @@ -164,8 +188,8 @@ { "cell_type": "markdown", "metadata": { - "id": "39qaiVVqeA51", - "colab_type": "text" + "colab_type": "text", + "id": "39qaiVVqeA51" }, "source": [ "Set the `GOOGLE_APPLICATION_CREDENTIALS` environmental variable to the location of the key file:" @@ -174,13 +198,13 @@ { "cell_type": "code", "metadata": { - "id": "fdGKEWCH1SsT", "colab_type": "code", + "id": "fdGKEWCH1SsT", "colab": {} }, "source": [ "import os\n", - "os.environ[\"GOOGLE_APPLICATION_CREDENTIALS\"]=\"/content/{0}\".format(service_acct_file)" + "os.environ[\"GOOGLE_APPLICATION_CREDENTIALS\"] = os.path.realpath(service_acct_file['name'])" ], "execution_count": 0, "outputs": [] @@ -188,8 +212,8 @@ { "cell_type": "markdown", "metadata": { - "id": "ma-McTrY0cGE", - "colab_type": "text" + "colab_type": "text", + "id": "ma-McTrY0cGE" }, "source": [ "# **5. Initialize Firebase Admin**" @@ -198,8 +222,8 @@ { "cell_type": "markdown", "metadata": { - "id": "X9xyubP_ekA5", - "colab_type": "text" + "colab_type": "text", + "id": "X9xyubP_ekA5" }, "source": [ "Import the `firebase_admin` module and initialize the SDK with the name of your Storage bucket. Be sure the Storage bucket is in the same Firebase project as your service account. Your project's default bucket looks like `your-project-id.appspot.com`." @@ -208,8 +232,8 @@ { "cell_type": "code", "metadata": { - "id": "2sLO335O7IIF", "colab_type": "code", + "id": "2sLO335O7IIF", "colab": {} }, "source": [ @@ -221,8 +245,8 @@ { "cell_type": "code", "metadata": { - "id": "YxYkdFBs0oS1", "colab_type": "code", + "id": "YxYkdFBs0oS1", "colab": {} }, "source": [ @@ -237,8 +261,8 @@ { "cell_type": "markdown", "metadata": { - "id": "DLOmPtM77wMI", - "colab_type": "text" + "colab_type": "text", + "id": "DLOmPtM77wMI" }, "source": [ "# **6. Train your model**" @@ -247,8 +271,8 @@ { "cell_type": "markdown", "metadata": { - "id": "oNEyKOO_ivrO", - "colab_type": "text" + "colab_type": "text", + "id": "oNEyKOO_ivrO" }, "source": [ "Next, train your model.\n", @@ -259,8 +283,8 @@ { "cell_type": "code", "metadata": { - "id": "6lrYxa4Z70Xp", "colab_type": "code", + "id": "6lrYxa4Z70Xp", "colab": {} }, "source": [ @@ -281,8 +305,8 @@ { "cell_type": "markdown", "metadata": { - "id": "1LK9d37xYDet", - "colab_type": "text" + "colab_type": "text", + "id": "1LK9d37xYDet" }, "source": [ "# **7. Convert & upload your model**" @@ -291,8 +315,8 @@ { "cell_type": "markdown", "metadata": { - "id": "mGe3os8Xkf7F", - "colab_type": "text" + "colab_type": "text", + "id": "mGe3os8Xkf7F" }, "source": [ "Now that you have a trained model, you can upload it to Firebase and make it available to your iOS and Android apps.\n", @@ -303,15 +327,15 @@ { "cell_type": "code", "metadata": { - "id": "SLZvUStHa7T3", "colab_type": "code", + "id": "SLZvUStHa7T3", "colab": {} }, "source": [ "# This takes the Keras model, converts it to a TFLite model, and uploads it to\n", "# your bucket as my_model.tflite\n", "source = ml.TFLiteGCSModelSource.from_keras_model(model_binary, 'my_model.tflite')\n", - "print (source.gcs_tflite_uri)" + "print(source.gcs_tflite_uri)" ], "execution_count": 0, "outputs": [] @@ -319,9 +343,35 @@ { "cell_type": "markdown", "metadata": { - "id": "RRA56_U9o2P9", + "id": "NpZMyCUzGTbU", "colab_type": "text" }, + "source": [ + "If you get a `toco_from_protos: command not found` error, make sure the Python binary directory is in your `PATH`, then try again." + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "qR3s7s5BGTbU", + "colab_type": "code", + "colab": {} + }, + "source": [ + "import os\n", + "import sys\n", + "py_bin_dir = os.path.dirname(sys.executable)\n", + "os.environ['PATH'] = '{}:{}'.format(os.environ['PATH'], py_bin_dir)" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "RRA56_U9o2P9" + }, "source": [ "# **8. Create a Model object**" ] @@ -329,8 +379,8 @@ { "cell_type": "markdown", "metadata": { - "id": "HzB_CV5yo9Ms", - "colab_type": "text" + "colab_type": "text", + "id": "HzB_CV5yo9Ms" }, "source": [ "Next, create a `Model` object, specifying the model's Cloud Storage source and the name of your model. (You will use the name you specify here to download the model in your iOS and Android apps.)" @@ -339,8 +389,8 @@ { "cell_type": "code", "metadata": { - "id": "rljlmVwgo1dk", "colab_type": "code", + "id": "rljlmVwgo1dk", "colab": {} }, "source": [ @@ -353,8 +403,8 @@ { "cell_type": "markdown", "metadata": { - "id": "0Hd7aZrpdOqm", - "colab_type": "text" + "colab_type": "text", + "id": "0Hd7aZrpdOqm" }, "source": [ "# **9. Add the model to your Firebase project**" @@ -363,8 +413,8 @@ { "cell_type": "markdown", "metadata": { - "id": "ZEMJ-B2LqhPt", - "colab_type": "text" + "colab_type": "text", + "id": "ZEMJ-B2LqhPt" }, "source": [ "Add the model to your Firebase project by calling `create_model()`. When you do so, the model gets copied from Cloud Storage.\n", @@ -375,14 +425,14 @@ { "cell_type": "code", "metadata": { - "id": "AOjQVSU2eG8v", "colab_type": "code", + "id": "AOjQVSU2eG8v", "colab": {} }, "source": [ "firebase_model_1 = ml.create_model(sdk_model_1)\n", "if firebase_model_1.validation_error:\n", - " raise Exception(firebase_model_1.validation_error)\n", + " raise Exception(firebase_model_1.validation_error)\n", "print(firebase_model_1.as_dict())" ], "execution_count": 0, @@ -391,18 +441,18 @@ { "cell_type": "markdown", "metadata": { - "id": "lMthadQ4qU32", - "colab_type": "text" + "colab_type": "text", + "id": "lMthadQ4qU32" }, "source": [ - "#**10. Publish the model**" + "# **10. Publish the model**" ] }, { "cell_type": "markdown", "metadata": { - "id": "oNTN-XEFvd7g", - "colab_type": "text" + "colab_type": "text", + "id": "oNTN-XEFvd7g" }, "source": [ "Finally, publish your model:" @@ -411,8 +461,8 @@ { "cell_type": "code", "metadata": { - "id": "qK2S0KD7qbIB", "colab_type": "code", + "id": "qK2S0KD7qbIB", "colab": {} }, "source": [ @@ -425,8 +475,8 @@ { "cell_type": "markdown", "metadata": { - "id": "IpxBS8HzvvWm", - "colab_type": "text" + "colab_type": "text", + "id": "IpxBS8HzvvWm" }, "source": [ "Now that you've published the model, you can [use it in your apps](https://firebase.google.com/docs/ml-kit/use-custom-models)." @@ -435,8 +485,8 @@ { "cell_type": "markdown", "metadata": { - "id": "ZHSN6vM7b_pH", - "colab_type": "text" + "colab_type": "text", + "id": "ZHSN6vM7b_pH" }, "source": [ "# **11. Create a second model (so we can update)**" @@ -445,8 +495,8 @@ { "cell_type": "markdown", "metadata": { - "id": "lhuuTNtHwduW", - "colab_type": "text" + "colab_type": "text", + "id": "lhuuTNtHwduW" }, "source": [ "You can update a published model with a new model file. When you do so, client apps automatically download and use the new model.\n", @@ -457,8 +507,8 @@ { "cell_type": "code", "metadata": { - "id": "1pLVpHi4cF6e", "colab_type": "code", + "id": "1pLVpHi4cF6e", "colab": {} }, "source": [ @@ -470,18 +520,18 @@ { "cell_type": "markdown", "metadata": { - "id": "veOOV0ZxjMf-", - "colab_type": "text" + "colab_type": "text", + "id": "veOOV0ZxjMf-" }, "source": [ - "#**12. Create a second model source and model format from the new model**" + "# **12. Create a second model source and model format from the new model**" ] }, { "cell_type": "markdown", "metadata": { - "id": "XIyAhNEFGNIN", - "colab_type": "text" + "colab_type": "text", + "id": "XIyAhNEFGNIN" }, "source": [ "Now, convert the saved model to TensorFlow Lite and upload it to Cloud Storage. This time, you're converting a TensorFlow saved model to TensorFlow Lite, but you could also convert a Keras model like you did earlier, or convert a Keras model saved as an HDF5 (`.h5`) file." @@ -490,8 +540,8 @@ { "cell_type": "code", "metadata": { - "id": "hn5IgonpjWmy", "colab_type": "code", + "id": "hn5IgonpjWmy", "colab": {} }, "source": [ @@ -504,18 +554,18 @@ { "cell_type": "markdown", "metadata": { - "id": "wtXV_x7ko1Dz", - "colab_type": "text" + "colab_type": "text", + "id": "wtXV_x7ko1Dz" }, "source": [ - "#**13. Modify the local model and then call the API Update**" + "# **13. Modify the local model and then call the API Update**" ] }, { "cell_type": "markdown", "metadata": { - "id": "m2WyUn4SJWEC", - "colab_type": "text" + "colab_type": "text", + "id": "m2WyUn4SJWEC" }, "source": [ "Change the original `Model` object's model source and (optionally) metadata, then call `update_model()`:" @@ -524,8 +574,8 @@ { "cell_type": "code", "metadata": { - "id": "MiRQi2Iqo8PK", "colab_type": "code", + "id": "MiRQi2Iqo8PK", "colab": {} }, "source": [ @@ -535,7 +585,7 @@ "\n", "firebase_model_2 = ml.update_model(firebase_model_1)\n", "if firebase_model_2.validation_error:\n", - " raise Exception(firebase_model_2.validation_error)\n", + " raise Exception(firebase_model_2.validation_error)\n", "print(firebase_model_2.as_dict())\n" ], "execution_count": 0, @@ -544,18 +594,18 @@ { "cell_type": "markdown", "metadata": { - "id": "uZzu3k5EXMma", - "colab_type": "text" + "colab_type": "text", + "id": "uZzu3k5EXMma" }, "source": [ - "#**14. Publish the model_format2 model**" + "# **14. Publish the model_format2 model**" ] }, { "cell_type": "markdown", "metadata": { - "id": "XaiWqq4EkWBU", - "colab_type": "text" + "colab_type": "text", + "id": "XaiWqq4EkWBU" }, "source": [ "After you update the model, re-publish it:" @@ -564,8 +614,8 @@ { "cell_type": "code", "metadata": { - "id": "iGsNmx2oXUFS", "colab_type": "code", + "id": "iGsNmx2oXUFS", "colab": {} }, "source": [ @@ -578,18 +628,18 @@ { "cell_type": "markdown", "metadata": { - "id": "m2YmhA4Ab1DH", - "colab_type": "text" + "colab_type": "text", + "id": "m2YmhA4Ab1DH" }, "source": [ - "#**15. Get the model**" + "# **15. Get the model**" ] }, { "cell_type": "markdown", "metadata": { - "id": "_01adxyEk0JN", - "colab_type": "text" + "colab_type": "text", + "id": "_01adxyEk0JN" }, "source": [ "If you need to get a `Model` object from one of your project's models, use `get_model()`:" @@ -598,8 +648,8 @@ { "cell_type": "code", "metadata": { - "id": "9Q1jQXYJb4qx", "colab_type": "code", + "id": "9Q1jQXYJb4qx", "colab": {} }, "source": [ @@ -612,18 +662,18 @@ { "cell_type": "markdown", "metadata": { - "id": "OphmZAVdcqxO", - "colab_type": "text" + "colab_type": "text", + "id": "OphmZAVdcqxO" }, "source": [ - "#**16. List the models**" + "# **16. List the models**" ] }, { "cell_type": "markdown", "metadata": { - "id": "kOIjFLyGlpyq", - "colab_type": "text" + "colab_type": "text", + "id": "kOIjFLyGlpyq" }, "source": [ "To list your project's models, iterate over the result of `list_models()`:" @@ -632,15 +682,15 @@ { "cell_type": "code", "metadata": { - "id": "ebJBI9NVcwjp", "colab_type": "code", + "id": "ebJBI9NVcwjp", "colab": {} }, "source": [ "firebase_models_list = ml.list_models()\n", "iterator = firebase_models_list.iterate_all()\n", "for m in iterator:\n", - " print(m.as_dict())" + " print(m.as_dict())" ], "execution_count": 0, "outputs": [] @@ -648,18 +698,18 @@ { "cell_type": "markdown", "metadata": { - "id": "p_4yAHeFg7BA", - "colab_type": "text" + "colab_type": "text", + "id": "p_4yAHeFg7BA" }, "source": [ - "#**17. Make more models so we can show lists better**" + "# **17. Make more models so we can show lists better**" ] }, { "cell_type": "markdown", "metadata": { - "id": "9vOkbq1lmA2B", - "colab_type": "text" + "colab_type": "text", + "id": "9vOkbq1lmA2B" }, "source": [ "The Admin SDK can help you manage projects with many models.\n", @@ -670,8 +720,8 @@ { "cell_type": "code", "metadata": { - "id": "ce3zB3HLhCTf", "colab_type": "code", + "id": "ce3zB3HLhCTf", "colab": {} }, "source": [ @@ -687,18 +737,18 @@ { "cell_type": "markdown", "metadata": { - "id": "QmxpYBsQnua4", - "colab_type": "text" + "colab_type": "text", + "id": "QmxpYBsQnua4" }, "source": [ - "#**18. Publish some of them**" + "# **18. Publish some of them**" ] }, { "cell_type": "markdown", "metadata": { - "id": "OvYY0orEmtsT", - "colab_type": "text" + "colab_type": "text", + "id": "OvYY0orEmtsT" }, "source": [ "And publish some of them:" @@ -707,8 +757,8 @@ { "cell_type": "code", "metadata": { - "id": "x6kzkhWjnyXr", "colab_type": "code", + "id": "x6kzkhWjnyXr", "colab": {} }, "source": [ @@ -721,18 +771,18 @@ { "cell_type": "markdown", "metadata": { - "id": "aAIAexOTpkVu", - "colab_type": "text" + "colab_type": "text", + "id": "aAIAexOTpkVu" }, "source": [ - "#**19. Listing with page size**" + "# **19. Listing with page size**" ] }, { "cell_type": "markdown", "metadata": { - "id": "BZC1dbfnm43F", - "colab_type": "text" + "colab_type": "text", + "id": "BZC1dbfnm43F" }, "source": [ "You can specify how many results to return at a time:" @@ -741,14 +791,14 @@ { "cell_type": "code", "metadata": { - "id": "kf3rUxE0pqzJ", "colab_type": "code", + "id": "kf3rUxE0pqzJ", "colab": {} }, "source": [ "firebase_models_list_2 = ml.list_models(page_size=3)\n", "for m in firebase_models_list_2.models:\n", - " print (m.as_dict())" + " print (m.as_dict())" ], "execution_count": 0, "outputs": [] @@ -756,18 +806,18 @@ { "cell_type": "markdown", "metadata": { - "id": "V0WpL3UarTlA", - "colab_type": "text" + "colab_type": "text", + "id": "V0WpL3UarTlA" }, "source": [ - "#**20. Listing the next page**" + "# **20. Listing the next page**" ] }, { "cell_type": "markdown", "metadata": { - "id": "iPh886g2nNm9", - "colab_type": "text" + "colab_type": "text", + "id": "iPh886g2nNm9" }, "source": [ "Get the next page of results:" @@ -776,14 +826,14 @@ { "cell_type": "code", "metadata": { - "id": "iodXtP_XrYGT", "colab_type": "code", + "id": "iodXtP_XrYGT", "colab": {} }, "source": [ "firebase_models_list_3 = firebase_models_list_2.get_next_page()\n", "for m in firebase_models_list_3.models:\n", - " print (m.as_dict())" + " print (m.as_dict())" ], "execution_count": 0, "outputs": [] @@ -791,8 +841,8 @@ { "cell_type": "markdown", "metadata": { - "id": "LJwxO53KnYHA", - "colab_type": "text" + "colab_type": "text", + "id": "LJwxO53KnYHA" }, "source": [ "When you retrieve the final page, `get_next_page()` returns `None`." @@ -801,18 +851,18 @@ { "cell_type": "markdown", "metadata": { - "id": "Xbv-NAm5tT_5", - "colab_type": "text" + "colab_type": "text", + "id": "Xbv-NAm5tT_5" }, "source": [ - "#**21. Filtering lists**" + "# **21. Filtering lists**" ] }, { "cell_type": "markdown", "metadata": { - "id": "-aNfitPHoF9P", - "colab_type": "text" + "colab_type": "text", + "id": "-aNfitPHoF9P" }, "source": [ "You can also filter the results.\n", @@ -823,14 +873,14 @@ { "cell_type": "code", "metadata": { - "id": "9eS1kVSwtgwJ", "colab_type": "code", + "id": "9eS1kVSwtgwJ", "colab": {} }, "source": [ "firebase_models_list = ml.list_models(list_filter='display_name=cat_model_1')\n", "for m in firebase_models_list.models:\n", - " print (m.as_dict())\n" + " print (m.as_dict())\n" ], "execution_count": 0, "outputs": [] @@ -838,8 +888,8 @@ { "cell_type": "markdown", "metadata": { - "id": "mLNhAGtjuxqF", - "colab_type": "text" + "colab_type": "text", + "id": "mLNhAGtjuxqF" }, "source": [ "Filter by display name prefix (note that only prefix matching is supported; you can't do general wildcard matching):" @@ -848,14 +898,14 @@ { "cell_type": "code", "metadata": { - "id": "YpihoJvnu1PU", "colab_type": "code", + "id": "YpihoJvnu1PU", "colab": {} }, "source": [ "firebase_models_list = ml.list_models(list_filter='display_name:cat_*')\n", "for m in firebase_models_list.models:\n", - " print (m.as_dict())" + " print (m.as_dict())" ], "execution_count": 0, "outputs": [] @@ -863,8 +913,8 @@ { "cell_type": "markdown", "metadata": { - "id": "e54EB4ngwBM6", - "colab_type": "text" + "colab_type": "text", + "id": "e54EB4ngwBM6" }, "source": [ "Filter by tag:" @@ -880,7 +930,7 @@ "source": [ "firebase_models_list = ml.list_models(list_filter='tags: cat')\n", "for m in firebase_models_list.models:\n", - " print (m.as_dict())" + " print (m.as_dict())" ], "execution_count": 0, "outputs": [] @@ -888,8 +938,8 @@ { "cell_type": "markdown", "metadata": { - "id": "ZrSJImzSyRoI", - "colab_type": "text" + "colab_type": "text", + "id": "ZrSJImzSyRoI" }, "source": [ "Filter by publish state:" @@ -905,7 +955,7 @@ "source": [ "firebase_models_list = ml.list_models(list_filter='state.published = true')\n", "for m in firebase_models_list.models:\n", - " print (m.as_dict())" + " print (m.as_dict())" ], "execution_count": 0, "outputs": [] @@ -913,8 +963,8 @@ { "cell_type": "markdown", "metadata": { - "id": "-EagFFnwzyI5", - "colab_type": "text" + "colab_type": "text", + "id": "-EagFFnwzyI5" }, "source": [ "Combine filters:" @@ -930,7 +980,7 @@ "source": [ "firebase_models_list = ml.list_models(list_filter='(display_name: cat_* OR tags: tag3) AND NOT state.published = true')\n", "for m in firebase_models_list.models:\n", - " print (m.as_dict())" + " print (m.as_dict())" ], "execution_count": 0, "outputs": [] @@ -938,8 +988,8 @@ { "cell_type": "markdown", "metadata": { - "id": "sYur7GCQBVas", - "colab_type": "text" + "colab_type": "text", + "id": "sYur7GCQBVas" }, "source": [ "# **22. Clean up**" @@ -948,8 +998,8 @@ { "cell_type": "markdown", "metadata": { - "id": "v1BKMU30Blnr", - "colab_type": "text" + "colab_type": "text", + "id": "v1BKMU30Blnr" }, "source": [ "That's it!\n", @@ -960,8 +1010,8 @@ { "cell_type": "code", "metadata": { - "id": "wGRXAfFiCHOF", "colab_type": "code", + "id": "wGRXAfFiCHOF", "colab": {} }, "source": [ From 909f39e77395cb0682108184ba565150caa68a31 Mon Sep 17 00:00:00 2001 From: Kevin Cheung Date: Wed, 29 Apr 2020 16:06:11 -0700 Subject: [PATCH 4/9] Link to notebook from README --- machine-learning/README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/machine-learning/README.md b/machine-learning/README.md index dd60209..74c5b32 100644 --- a/machine-learning/README.md +++ b/machine-learning/README.md @@ -3,6 +3,10 @@ This sample script shows how you can use the Firebase Admin SDK to manage your Firebase-hosted ML models. +Also see the [Firebase ML API Tutorial Colab/Jupyter notebook][colab]. + +[colab]: https://colab.research.google.com/github/firebase/quickstart-python/blob/master/machine-learning/Firebase_ML_API_Tutorial.ipynb + ## Setup 1. Install the Admin SDK (probably in a virtual environment): From d13fba5c28d1fd92f66714a61e79d919d19dd510 Mon Sep 17 00:00:00 2001 From: Kevin Cheung Date: Fri, 21 Aug 2020 12:27:44 -0700 Subject: [PATCH 5/9] Print with BeautifulTable --- machine-learning/README.md | 5 +++-- machine-learning/manage-ml.py | 21 +++++++++++++++++---- machine-learning/requirements.txt | 2 ++ 3 files changed, 22 insertions(+), 6 deletions(-) mode change 100644 => 100755 machine-learning/manage-ml.py create mode 100644 machine-learning/requirements.txt diff --git a/machine-learning/README.md b/machine-learning/README.md index 74c5b32..c0fcea9 100644 --- a/machine-learning/README.md +++ b/machine-learning/README.md @@ -9,11 +9,12 @@ Also see the [Firebase ML API Tutorial Colab/Jupyter notebook][colab]. ## Setup -1. Install the Admin SDK (probably in a virtual environment): +1. Install the Admin SDK and other dependencies (probably in a virtual + environment): ``` $ pip install -U pip setuptools - $ pip install firebase_admin + $ pip install -r requirements.txt ``` 2. Clone the quickstart repository and change to the `machine-learning` diff --git a/machine-learning/manage-ml.py b/machine-learning/manage-ml.py old mode 100644 new mode 100755 index a35682b..dfc9a0d --- a/machine-learning/manage-ml.py +++ b/machine-learning/manage-ml.py @@ -2,7 +2,10 @@ """Firebase Admin SDK ML quickstart example.""" import argparse +from datetime import datetime +from datetime import timezone +from beautifultable import BeautifulTable import firebase_admin from firebase_admin import ml @@ -36,17 +39,27 @@ def upload_model(model_file, name, tags=None): ml.publish_model(new_model.model_id) print('Model uploaded and published:') - tags = ', '.join(new_model.tags) if new_model.tags is not None else '' - print('{:<20}{:<10} {}'.format(new_model.display_name, new_model.model_id, - tags)) + print_models([new_model], headers=False) def list_models(filter_exp=''): """List the models in the project.""" models = ml.list_models(list_filter=filter_exp).iterate_all() + print_models(models) + + +def print_models(models, headers=True): + """Prettyprint a list of models.""" + table = BeautifulTable() + if headers: + table.columns.header = ['Name', 'ID', 'Tags'] for model in models: tags = ', '.join(model.tags) if model.tags is not None else '' - print('{:<20}{:<10} {}'.format(model.display_name, model.model_id, tags)) + table.rows.append([model.display_name, model.model_id, tags]) + table.set_style(BeautifulTable.STYLE_COMPACT) + table.columns.header.alignment = BeautifulTable.ALIGN_CENTER + table.columns.alignment = BeautifulTable.ALIGN_LEFT + print(table) def update_model(model_id, model_file=None, name=None, diff --git a/machine-learning/requirements.txt b/machine-learning/requirements.txt new file mode 100644 index 0000000..9f5c766 --- /dev/null +++ b/machine-learning/requirements.txt @@ -0,0 +1,2 @@ +beautifultable~=1.0.0 +firebase-admin~=4.3.0 From 26654454f16aed7b0986efa7959d14d6369aece2 Mon Sep 17 00:00:00 2001 From: Kevin Cheung Date: Fri, 21 Aug 2020 12:30:09 -0700 Subject: [PATCH 6/9] Add info command --- machine-learning/manage-ml.py | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/machine-learning/manage-ml.py b/machine-learning/manage-ml.py index dfc9a0d..ecbb0bf 100755 --- a/machine-learning/manage-ml.py +++ b/machine-learning/manage-ml.py @@ -62,6 +62,27 @@ def print_models(models, headers=True): print(table) +def get_model_info(model_id): + """Get model details.""" + model = ml.get_model(model_id) + created = datetime.fromtimestamp(model.create_time / 1000, timezone.utc) + updated = datetime.fromtimestamp(model.update_time / 1000, timezone.utc) + table = BeautifulTable() + table.columns.append(['Name:', 'ID:', 'Tags:', 'Published:', 'ETag:', + 'SHA256:', 'Created:', 'Updated:']) + table.columns.append([model.display_name, + model.model_id, + ', '.join(model.tags) if model.tags else '', + 'Yes' if model.published else 'No', + model.etag, + model.model_hash, + created.isoformat(' ', timespec='seconds'), + updated.isoformat(' ', timespec='seconds')]) + table.set_style(BeautifulTable.STYLE_COMPACT) + table.columns.alignment = BeautifulTable.ALIGN_LEFT + print(table) + + def update_model(model_id, model_file=None, name=None, new_tags=None, remove_tags=None): """Update one of the project's models.""" @@ -117,6 +138,11 @@ def main(): help='''filter expression to limit results (see: https://firebase.google.com/docs/ml-kit/manage-hosted-models#list_your_projects_models)''') + info_parser = subparsers.add_parser( + 'info', help='') + info_parser.add_argument( + 'model_id', type=valid_id, help='the ID of the model you want to view') + update_parser = subparsers.add_parser( 'update', help='update one of your project\'s models') update_parser.add_argument( @@ -144,6 +170,8 @@ def main(): upload_model(args.model_file, args.name, tags) elif args.command == 'list': list_models(args.filter) + elif args.command == 'info': + get_model_info(args.model_id) elif args.command == 'update': new_tags = args.new_tags.split(',') if args.new_tags is not None else None remove_tags = ( From 5efb8656ef39346b8448ea8e15632d0a89f57764 Mon Sep 17 00:00:00 2001 From: Kevin Cheung Date: Tue, 15 Sep 2020 14:10:21 -0700 Subject: [PATCH 7/9] Add AutoML model example (#19) * Add AutoML model example * Remove NL from example * Update version --- machine-learning/README.md | 35 +++++++++++++++++++--------- machine-learning/manage-ml.py | 38 +++++++++++++++++++++++++++---- machine-learning/requirements.txt | 2 +- 3 files changed, 58 insertions(+), 17 deletions(-) diff --git a/machine-learning/README.md b/machine-learning/README.md index c0fcea9..a89284b 100644 --- a/machine-learning/README.md +++ b/machine-learning/README.md @@ -57,18 +57,31 @@ Also see the [Firebase ML API Tutorial Colab/Jupyter notebook][colab]. ``` $ ./manage-ml.py list -fish_detector 8716935 vision -barcode_scanner 8716959 vision -smart_reply 8716981 natural_language -$ ./manage-ml.py new ~/yak.tflite yak_detector --tags vision,experimental -Uploading model to Cloud Storage... + Name ID Tags +---------------------- ---------- ------------------ + fish_recognizer 12990533 vision + barcode_scanner 12990544 vision +$ ./manage-ml.py new yak_detector -f model.tflite -t vision,experimental +Uploading to Cloud Storage... Model uploaded and published: -yak_detector 8717019 experimental, vision -$ ./manage-ml.py update 8717019 --remove_tags experimental -$ ./manage-ml.py delete 8716959 + yak_detector 12990577 experimental, vision +$ ./manage-ml.py new flower_classifier -a projects/12345/locations/us-central1/models/ICN12345 +Model uploaded and published: + flower_classifier 12990597 +$ ./manage-ml.py list + Name ID Tags +---------------------- ---------- ---------------------- + fish_recognizer 12990533 vision + barcode_scanner 12990544 vision + yak_detector 12990577 experimental, vision + flower_classifier 12990597 +$ ./manage-ml.py update 12990577 --remove_tags experimental +$ ./manage-ml.py delete 12990544 $ ./manage-ml.py list -fish_detector 8716935 vision -smart_reply 8716981 natural_language -yak_detector 8717019 vision + Name ID Tags +---------------------- ---------- ------------------ + fish_recognizer 12990533 vision + yak_detector 12990577 vision + flower_classifier 12990597 $ ``` diff --git a/machine-learning/manage-ml.py b/machine-learning/manage-ml.py index ecbb0bf..e18ea26 100755 --- a/machine-learning/manage-ml.py +++ b/machine-learning/manage-ml.py @@ -42,6 +42,25 @@ def upload_model(model_file, name, tags=None): print_models([new_model], headers=False) +def add_automl_model(model_ref, name, tags=None): + """Add an AutoML tflite model file to the project and publish it.""" + # Create the model object + model_source = ml.TFLiteAutoMlSource(model_ref) + model = ml.Model( + display_name=name, + model_format=ml.TFLiteFormat(model_source=model_source)) + if tags is not None: + model.tags = tags + + # Add the model to your Firebase project and publish it + new_model = ml.create_model(model) + new_model.wait_for_unlocked() + ml.publish_model(new_model.model_id) + + print('Model uploaded and published:') + print_models([new_model], headers=False) + + def list_models(filter_exp=''): """List the models in the project.""" models = ml.list_models(list_filter=filter_exp).iterate_all() @@ -123,11 +142,17 @@ def main(): dest='command', required=True, metavar='command') new_parser = subparsers.add_parser( - 'new', help='upload a tflite model to your project') - new_parser.add_argument( - 'model_file', type=str, help='path to the tflite file') + 'new', help='upload a tflite model file or AutoML model reference to' + ' your project') new_parser.add_argument( 'name', type=str, help='display name for the new model') + new_source_group = new_parser.add_mutually_exclusive_group(required=True) + new_source_group.add_argument( + '-f', '--file', type=str, help='path to the tflite file') + new_source_group.add_argument( + '-a', '--automl', type=str, help='AutoML model reference (e.g. projects/' + '12345678/locations/us-central1/models/' + 'ICN1234567890)') new_parser.add_argument( '-t', '--tags', type=str, help='comma-separated list of tags') @@ -165,9 +190,12 @@ def main(): args = main_parser.parse_args() try: - if args.command == 'new': + if args.command == 'new' and args.file is not None: + tags = args.tags.split(',') if args.tags is not None else None + upload_model(args.file.strip(), args.name.strip(), tags) + if args.command == 'new' and args.automl is not None: tags = args.tags.split(',') if args.tags is not None else None - upload_model(args.model_file, args.name, tags) + add_automl_model(args.automl.strip(), args.name.strip(), tags) elif args.command == 'list': list_models(args.filter) elif args.command == 'info': diff --git a/machine-learning/requirements.txt b/machine-learning/requirements.txt index 9f5c766..9a1b22c 100644 --- a/machine-learning/requirements.txt +++ b/machine-learning/requirements.txt @@ -1,2 +1,2 @@ beautifultable~=1.0.0 -firebase-admin~=4.3.0 +firebase-admin~=4.4.0 From 27f8f0246dacf76aab9433caa6a823efdbe0f0c5 Mon Sep 17 00:00:00 2001 From: chong-shao <31256040+chong-shao@users.noreply.github.com> Date: Tue, 6 Dec 2022 22:29:57 +0000 Subject: [PATCH 8/9] Migrate messaging's oauth token generation to use google auth --- messaging/messaging.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/messaging/messaging.py b/messaging/messaging.py index 3f1c039..d2cbd5a 100644 --- a/messaging/messaging.py +++ b/messaging/messaging.py @@ -12,8 +12,9 @@ import argparse import json import requests +import google.auth.transport.requests -from oauth2client.service_account import ServiceAccountCredentials +from google.oauth2 import service_account PROJECT_ID = '' BASE_URL = 'https://fcm.googleapis.com' @@ -27,10 +28,12 @@ def _get_access_token(): :return: Access token. """ - credentials = ServiceAccountCredentials.from_json_keyfile_name( - 'service-account.json', SCOPES) + credentials = service_account.Credentials.from_service_account_file( + 'service-account.json', scopes=SCOPES) + request = google.auth.transport.requests.Request() access_token_info = credentials.get_access_token() - return access_token_info.access_token + credentials.refresh(request) + return credentials.token # [END retrieve_access_token] def _send_fcm_message(fcm_message): From 2c68e7c5020f4dbb072cca4da03dba389fbbe4ec Mon Sep 17 00:00:00 2001 From: chong-shao <31256040+chong-shao@users.noreply.github.com> Date: Tue, 6 Dec 2022 16:11:15 -0800 Subject: [PATCH 9/9] Remove a line of code that's not needed Remove a line of code that's not needed --- messaging/messaging.py | 1 - 1 file changed, 1 deletion(-) diff --git a/messaging/messaging.py b/messaging/messaging.py index d2cbd5a..7e9b235 100644 --- a/messaging/messaging.py +++ b/messaging/messaging.py @@ -31,7 +31,6 @@ def _get_access_token(): credentials = service_account.Credentials.from_service_account_file( 'service-account.json', scopes=SCOPES) request = google.auth.transport.requests.Request() - access_token_info = credentials.get_access_token() credentials.refresh(request) return credentials.token # [END retrieve_access_token]