原文はこちら。
https://blogs.oracle.com/cloud-infrastructure/serverless-image-classification-with-oracle-functions-and-tensorflow
画像分類は、機械学習技術の実証でよく使われます。このエントリでは、最近発表されたクラウドサービスのOracle FunctionsでTensorFlowベースの画像分類アプリケーションを実行する方法をご紹介します。
TensorFlow
https://www.tensorflow.org/
Announcing Oracle Functions
https://blogs.oracle.com/cloud-infrastructure/announcing-oracle-functions
https://orablogs-jp.blogspot.com/2018/12/announcing-oracle-functions.html
Oracle Functions
Oracle Functionsはフルマネージドで高い拡張性を備える、オンデマンドのFunction as a Serviceプラットフォームで、エンタープライズクラスのOracle Cloud Infrastructure上に構築されています。
Oracle Cloud Infrastructure
https://cloud.oracle.com/ja_JP/cloud-infrastructure
基盤となるインフラストラクチャを気にせずにビジネスニーズを満たすコードを書くことに集中でき、実行中に消費されるリソースに対してのみ課金される、サーバーレス製品です。コードをデプロイして直接またはトリガーに応じて呼び出すことができ、アプリケーションの高可用性、スケーラビリティ、安全かつ監視対象とするために必要な、背後のすべての作業はOracle Functionsが担います。
Oracle Functionsはオープンソース、コンテナネイティブ、任意の環境、つまりクラウドまたはオンプレミスで実行できるサーバーレスのプラットフォームであるFn Projectをベースにしています。
Fn Project
http://fnproject.io/
Fn Projectのオープンソースディストリビューションをダウンロード、インストールし、functionをローカルで開発およびテストしてから、同じツールを使用してそのfunctionをOracle Functionsにデプロイできます。
What to Expect
詳細に入る前に、サーバーレス機械学習関数から期待できるものを見ていきましょう。セットアップして動作させると、アプリケーションを画像にポイントし、画像がどのようなものかを想定し、そしてその想定の確からしさをあわせて返します。
例えば、分類関数に渡した場合、以下の画像では次のように返します。
This is a ‘pizza’ Accuracy—100%
Photo by Alan Hardman on Unsplash
The Code
イメージ分類関数は既存のTensorFlowのサンプルをベースにしています。
Image Classification Example
https://github.com/tensorflow/models/tree/master/samples/languages/java/label_image
この関数はTensorFlow Java SDKを使っていて、この中ではJNI(Java Native Interface)を介してネイティブC++実装を利用しています。
Install TensorFlow for Java
https://www.tensorflow.org/install/lang_java
Function Image Input
画像分類関数はFn Java FDKを利用しており、このおかげでJavaでのfunctionの開発および実行プロセスがシンプルになります。
Java JDKを使う利点の1つは、functionに送られた入力をJavaのオブジェクトと型にシームレスに変換できることです。これには以下のものが含まれます。
Java API and runtime for fn
https://github.com/fnproject/fdk-java/
- 文字列入力処理のようなシンプルなデータバインディング
- JSONデータ型をPOJOにバインド。Jacksonを使って内部で実装しているため、カスタマイズ可能。
Main Portal page for the Jackson project
https://github.com/FasterXML/jackson
- functionが受信もしくは返す生のFn Java FDKイベントの抽象化によって、生の入力を操作可能。
入力データと出力データのマーシャリング方法をカスタマイズしたい場合は、バインディングをさらに拡張できます。
Extending the Data Binding functionality
https://github.com/fnproject/fdk-java/blob/master/docs/ExtendingDataBinding.md
既存のTensorFlowのサンプルでは、入力として画像名のリスト(コード実行マシンに画像が存在する必要があります)を想定しています。この関数も同様に動作しますが、Fn Java FDKがもたらす柔軟なバインディング機能を使用している点が重要な違いです。classifyメソッドはfunctionのエントリポイントとして機能し、functionに渡されるイメージのraw byteを表すJavaバイト配列(byte[])を受け入れます。このバイト配列は、staticなTensor.create(byte[])メソッドを使ってTensorオブジェクトを作成するために利用されます。
Tensor
https://www.tensorflow.org/api_docs/java/reference/org/tensorflow/Tensor
public static Tensor<String> create (byte[])
https://www.tensorflow.org/api_docs/java/reference/org/tensorflow/Tensors#create(byte%5B%5D)
public class LabelImageFunction {
public String classify(byte[] image) {
...
Tensor<String> input = Tensors.create(image);
...
}
}
完全なソースコードはGitHubからご覧いただけます。
Serverless image classification with functions & TensorFlow
https://github.com/abhirockzz/fn-hello-tensorflow
Machine Learning Model
通常、機械学習ベースのシステムは以下のフェーズで構成されています。
- Training(学習):アルゴリズムに過去のデータ(履歴データ)を食わせて学習(パターンを導出)させ、モデルを構築する。多くの場合このプロセスは継続して実行される。
- Predicting(予測):生成されたモデルを使い、学習フェーズで学んだ事実に基づき、新たな入力に対応する予測や出力を生成する。
このアプリケーションでは事前学習済みのモデルを使います。さらに便利なことに、分類ロジックが必要とするこのモデル(およびラベル)はfunctionそのもの(Dockerイメージの一部)と共にパッケージされています。これらはソースコードのresourcesフォルダにあります。
このことは、TensorFlow Servingのような専用のモデル提供コンポーネントを設定する必要がない、ということです。
TensorFlow Serving for model deployment in production
https://www.tensorflow.org/serving/
Function Metadata
func.yaml ファイルには、メモリ割り当てやタイムアウトのような属性値(このfunctionではそれぞれ1024MB、120秒が設定されています)を含む、functionのメタデータが記述されています。このメタデータは、(より単純な計算とは対照的に)画像分類アルゴリズムの(かなり)厳しい性質のために必要な値になっています。
schema_version: 20180708
name: classify
version: 0.0.1
runtime: java
memory: 1024
timeout: 120
triggers:
- name: classify
type: http
source: /classify
以下は設定している属性のサマリです。
- schema_version:このファイルが従う仕様のバージョン
- name:このfunctionをプッシュする際の名前とタグ
- version:functionの現在のバージョン。デプロイ時にタグとしてイメージに追加する。
- runtime:プログラミング言語ランタイム。この例ではJava。
- memory (optional) :このfunctionの最大メモリ閾値。このfunctionが実行時にこの上限を超えると、functionは停止し、エラーメッセージをログ出力する。
- timeout (optional) :function実行の最大許容時間
- triggers (optional) :functionのトリガーを指定するためのトリガー・エンティティの配列。この例では、HTTPトリガーを使用。
Function Dockerfile
Oracle Functionsは、ビルドおよび実行時フェーズで一連の事前ビルド済みかつ言語固有のDockerイメージを使います。例えばJava functionの場合、fn-java-fdk-buildをビルドフェーズで利用し、実行時にはfn-java-fdkを使います。
fn-java-fdk-build
https://hub.docker.com/r/fnproject/fn-java-fdk-build/
fn-java-fdk
https://hub.docker.com/r/fnproject/fn-java-fdk/
以下はfunction用のDockerイメージ作成で使うデフォルトのDockerfileです。
FROM fnproject/fn-java-fdk-build:jdk9-1.0.75 as build-stage
WORKDIR /function
ENV MAVEN_OPTS -Dhttp.proxyHost= -Dhttp.proxyPort= -Dhttps.proxyHost= -Dhttps.proxyPort= -Dhttp.nonProxyHosts= -Dmaven.repo.local=/usr/share/maven/ref/repository
ADD pom.xml /function/pom.xml
RUN ["mvn", "package", "dependency:copy-dependencies", "-DincludeScope=runtime", "-DskipTests=true", "-Dmdep.prependGroupId=true", "-DoutputDirectory=target", "--fail-never"]
ADD src /function/src
RUN ["mvn", "package"]
FROM fnproject/fn-java-fdk:jdk9-1.0.75
WORKDIR /function
COPY --from=build-stage /function/target/*.jar /function/app/
CMD ["com.example.fn.HelloFunction::handleRequest"]
このDockerfileはマルチステージDockerビルドを使って(標準で)以下のアクションを実行します。
- maven package およびmaven build
- (COPYコマンドを使って)functionのJARファイルと依存関係をランタイムイメージにコピー
- functionのコンテナが生成されたら、(CMDコマンドを使って)実行コマンドを設定
しかし、例えば3rdパーティのネイティブライブラリを組み込むために、Dockerイメージの作成をさらに制御する必要がある場合があるでしょう。そのような場合は、カスタムDockerfileを使いたいと思うでしょう。あなたのfunctionのレシピを定義する自由を手に入れるため、非常に強力です。やるべきことは、基本のDockerイメージ拡張する、これだけです。
Docker base images for various programming languages
https://github.com/fnproject/dockers
以下はこのfunctionで利用しているDockerfileです。
FROM fnproject/fn-java-fdk-build:jdk9-1.0.75 as build-stage
WORKDIR /function
ENV MAVEN_OPTS -Dhttp.proxyHost= -Dhttp.proxyPort= -Dhttps.proxyHost= -Dhttps.proxyPort= -Dhttp.nonProxyHosts= -Dmaven.repo.local=/usr/share/maven/ref/repository
ADD pom.xml /function/pom.xml
RUN ["mvn", "package", "dependency:copy-dependencies", "-DincludeScope=runtime", "-DskipTests=true", "-Dmdep.prependGroupId=true", "-DoutputDirectory=target", "--fail-never"]'
ARG TENSORFLOW_VERSION=1.12.0
RUN echo "using tensorflow version " $TENSORFLOW_VERSION
RUN curl -LJO https://storage.googleapis.com/tensorflow/libtensorflow/libtensorflow-$TENSORFLOW_VERSION.jar
RUN curl -LJO https://storage.googleapis.com/tensorflow/libtensorflow/libtensorflow_jni-cpu-linux-x86_64-$TENSORFLOW_VERSION.tar.gz
RUN tar -xvzf libtensorflow_jni-cpu-linux-x86_64-$TENSORFLOW_VERSION.tar.gz
ADD src /function/src
RUN ["mvn", "package"]
FROM fnproject/fn-java-fdk:jdk9-1.0.75
ARG TENSORFLOW_VERSION=1.12.0
WORKDIR /function
COPY --from=build-stage /function/libtensorflow_jni.so /function/runtime/lib
COPY --from=build-stage /function/libtensorflow_framework.so /function/runtime/lib
COPY --from=build-stage /function/libtensorflow-$TENSORFLOW_VERSION.jar /function/app/
COPY --from=build-stage /function/target/*.jar /function/app/
CMD ["com.example.fn.LabelImageFunction::classify"]
maven buildのようなデフォルトのステップに加え、組み込んでいる追加のカスタマイズに着目してください。
- (指示に従って)TensorFlowのセットアップを自動化し、TensorFlow Java SDKとネイティブJNI(.so)ライブラリを展開
Install TensorFlow for Java
https://www.tensorflow.org/install/lang_java
- (Docker buildの第2段階の一部として)JNIライブラリーを/function/runtime/libにコピーし、SDK JARを/function/appにコピーして、実行時にこのfunctionで使用できるようにする
Deploying to Oracle Functions
先述の通り、オープンソースのFn CLIを使ってOracle Functionsをデプロイできます。最新バージョンであることを確認してください。
CLI tool for fnproject
https://github.com/fnproject/cli/
curl -LSs https://raw.githubusercontent.com/fnproject/cli/master/install | sh
以下のURLから直接ダウンロードすることもできます。
fnproject/cli - Releases
https://github.com/fnproject/cli/releases
Oracle Functions Context
Oracle Functionsを使用する前に、Oracle Cloud Infrastructureテナントに接続するようにFn Project CLIを構成する必要があります。
Fn Project CLIを最初にインストールすると、ローカル開発コンテキスト用に設定されます。そうではなく、Fn Project CLIをOracle Cloud Infrastructureテナントに接続するように設定するには、新しいコンテキストを作成する必要があります。コンテキスト情報は~/.fn/contextディレクトリ内の.yamlファイルに格納されます。このファイル内で、Oracle Functionsのエンドポイント、デプロイされた機能が属するコンパートメントのOCID、Oracle Cloud Infrastructureの構成ファイル、およびイメージのプッシュ先およびプル元のDockerレジストリのアドレスを指定します。
以下はコンテキストファイルの例です。
api-url: https://functions.us-phoenix-1.oraclecloud.com
oracle.compartment-id: <OCI_compartment_OCID>
oracle.profile: <profile_name_in_OCI_config>
provider: oracle
registry: <OCI_docker_registry>
Oracle Cloud Infrastructure Configuration
Oracle Cloud Infrastructureの構成ファイルには、ユーザーの資格証明とテナントのOCIDが含まれています。
SDK and CLI Configuration File
https://docs.cloud.oracle.com/iaas/Content/API/Concepts/sdkconfig.htm
これらのエントリに対して異なる値を持つ複数のプロファイルを作成できます。そのため、oracle.profile属性を使ってCLIが利用するプロファイルを定義できます。
以下は構成ファイルの例です。
[DEFAULT]
user=ocid1.user.oc1..exampleuniqueID
fingerprint=20:3b:97:13:55:1c:5b:0d:d3:37:d8:50:4e:c5:3a:34
key_file=~/.oci/oci_api_key.pem
tenancy=ocid1.tenancy.oc1..exampleuniqueID
pass_phrase=tops3cr3t
region=us-ashburn-1
[ORACLE_FUNCTIONS_USER]
user=ocid1.user.oc1..exampleuniqueID
fingerprint=72:00:22:7f:d3:8b:47:a4:58:05:b8:95:84:31:dd:0e
key_file=/.oci/admin_key.pem
tenancy=ocid1.tenancy.oc1..exampleuniqueID
pass_phrase=s3cr3t
region=us-phoenix-1
複数のコンテキストを定義でき、それぞれは異なるコンテキストファイルに保存されます。Functions開発環境に従って、正しいコンテキストに切り替えてください。
fn use context <context_name>
Create the Application
GitHubリポジトリのコンテンツをクローンするところから始めます。
git clone https://github.com/abhirockzz/fn-hello-tensorflow
以下はアプリケーションデプロイ時に必要なコマンドです。
fn create app <app_name> --annotation oracle.com/oci/subnetIds='["<subnet_ocid>"]'
- <app_name>:新たなアプリケーションの名前
- <subnet_ocid> :functionを実行するサブネットのOCID
以下は例です。
fn create app fn-tensorflow-app --annotation oracle.com/oci/subnetIds='["ocid1.subnet.oc1.phx.exampleuniqueID","ocid1.subnet.oc1.phx.exampleuniqueID","ocid1.subnet.oc1.phx.exampleuniqueID"]'
Deploy the Function
アプリケーション作成後、以下のコマンドでfunctionをデプロイできます。
fn deploy --app <app_name>
<app_name>はfunctionを追加するOracle Functionsのアプリケーションの名前です。
(Java SDKと対応するネイティブライブラリで)TensorFlow v 1.12.0を使いたい場合、以下のコマンドを使います。
fn -v deploy --app fn-tensorflow-app
特定のバージョンを選択することもできます。その場合は、functionのビルド前にpom.xmlファイルで指定することをお忘れなく。例えば、v 1.11.0を使いたい場合、以下のように設定してください。
<dependency>
<groupId>org.tensorflow</groupId>
<artifactId>tensorflow</artifactId>
<version>1.11.0</version>
<scope>provided</scope>
</dependency>
Tofunctionのデプロイ時にバージョンを指定したい場合、以下のように--build-arg(ビルド引数)を使います。
fn -v deploy --app fn-tensorflow-app --build-arg TENSORFLOW_VERSION=<version>
例えばv1.11.0を使う場合は以下のようです。
fn -v deploy --app fn-tensorflow-app --build-arg TENSORFLOW_VERSION=1.11.0
デプロイが無事に成功すると、functionは利用可能になっています。fn ls appsコマンドを使って現在デプロイされているアプリケーションをリスト表示すると、n-tensorflow-appが出てくるはずです。
Time to Classify Images!
前述の通り、このfunctionは入力として画像を受け取り、それが何であるかを正確さのパーセンテージと共に伝えます。
推奨された画像のいくつかをダウンロードして試すか、もしくはすでにコンピュータにある画像を使うことができます。呼び出し時にfunctionにイメージを渡すだけでOKです。
download_sample_images.sh
https://github.com/tensorflow/models/blob/master/samples/languages/java/label_image/download_sample_images.sh
cat <path to image> | fn invoke fn-tensorflow-app classify
はい、では試してみましょう。この画像でソンブレロを検出できますか?
cat /Users/abhishek/manwithhat.jpg | fn invoke fn-tensorflow-app classify
“366 • 9 • Gringo” (CC BY-NC-ND 2.0) by Pragmagraphr
結果
This is a ‘sombrero’ Accuracy — 92%
テリアはどうでしょうか。
cat /Users/abhishek/terrier.jpg | fn invoke fn-tensorflow-app classify
“Terrier” (CC BY-NC 2.0) by No_Water
結果
This is a 'West Highland white terrier' Accuracy - 88%
次は何を分類しましょうか?
Summary
シンプルでありながらも完全に機能する機械学習アプリケーションをクラウドにデプロイしました。試してみたいですよね?
Oracle Functionsは2019年に一般提供の予定ですが、現在Cloud Native Limited Availability Programを通じて選ばれたお客様に対してのみご利用いただけるようにしております。Oracle Functionsの詳細ならびにOracle Functionsへのアクセスのリクエストは、
こちらから登録をお願いします。Oracle Functionsで使われている、基礎のオープンソーステクノロジーの詳細は、FnProject.ioをご覧ください。
FnProject
http://fnproject.io/