BigQuery ML による画像データの分類

1. はじめに

この Codelab では、BigQuery でヨガのポーズの画像を保存および分析し、BigQuery ML で分類モデルを実装して SQL コンストラクトのみを使用し、他の形態のコードを使わないでポーズにラベルを付けるユースケースについて解説します。

BigQuery と BQML

BigQuery はサーバーレスのマルチクラウド データ ウェアハウスで、運用上のオーバーヘッドを発生させずにバイトからペタバイトまでスケーリングできます。そのため、ML トレーニング データの保存に最適です。さらに、組み込まれた BigQuery の機械学習(BQML)と分析機能によって、SQL クエリだけを使ってコード不要の予測を作成できます。また、連携クエリを使用して外部ソースからデータにアクセスできるため、複雑な ETL パイプラインが不要になります。BigQuery が提供するすべての機能については、BigQuery ページで詳しくご紹介しています。

今のところ BigQuery は、ユーザーが構造化データと半構造化データを分析するのを支援する、フルマネージド クラウド データ ウェアハウスです。ただし、

  • BigQuery は、非構造化データに対してもあらゆる分析と ML を実行できるように拡張されてきました。
  • 特別なコードを書かないでも、SQL クエリを使用して画像、動画、オーディオなどに対して、インサイトを得るための分析および ML を大規模に実行することができます。
  • 構造化データと非構造化データを、まるで 1 つのテーブルにすべてが一緒に置かれているかのように組み合わせることができます。

次のセクションでは、ヨガのポーズの分類のユースケースでこれらについて説明します。

BigQuery ML による画像データの分類

構造化クエリを使用して、画像を構造化データであるかのように処理および分析できるというのは、今までになかったことです。BigQuery ML によって、機械学習分類モデルを使用して結果を予測することさえ可能になっています。分かりやすいように、関連するステージを 5 つのステップに分類しました。

fe97945bce996e1.jpeg

上記のステップは、項目だけを見るとわかりづらいかもしれません。BigQuery データセット、BigLake 接続、Cloud Storage バケット(コンテナ)、オブジェクト テーブル、BQML などのような関連コンポーネントのそれぞれの詳細はすべて、実装セクションで定義されています。ですので、これらの用語についてよく知らなくても落胆しないでください。

作成するアプリの概要

これから、以下のものをカバーする画像データ分類モデルを BQML で作成します。

  • テーブルとモデル コンポーネントが格納された BigQuery データセット
  • モデルのためにヨガの画像を保存する Google Cloud Storage(GCS)
  • Cloud Storage の画像にアクセスするための外部テーブル
  • 外部テーブルが GCS 内の画像にアクセスするための BigLake 接続
  • BigQuery ML 内の ResNet モデル
  • 作成されたモデルを使用した推論
  • 画像データを分析するための BigQuery SQL
  • 構造化データと非構造化データを一緒にクエリするための BigQuery SQL

学習内容

  • Cloud Storage バケットを作成して画像を保存する方法
  • BigQuery データセット、テーブル、接続を作成する方法
  • BQML を使用して画像データ分類モデルを作成する方法
  • BigQuery ML を使用して、作成されたモデルによって予測を行う方法
  • BigQuery SQL を使用して画像をクエリし、構造化データと組み合わせる方法

2. 必要なもの

  • ブラウザ(ChromeFirefox など)
  • BigQuery、Cloud Storage、BigLake 接続サービスを含む、課金が有効化された Google Cloud プロジェクト
  • 次のセクションでは、画像データ分類アプリケーションを作成するステップの一覧を示します。

3. データセットと BigLake 接続の作成

5 種類のヨガのポーズの画像を検出するユースケースのために、一般公開されているデータセットを使用しました。そのデータセットには、このレポジトリからアクセスできます。私たちがこれから見つけるヨガのポーズは、ダウンドッグ、ゴッデス、プランク、ツリー、ウォリアー 2 に限られます。BigQuery データセットの作成を開始する前に必ず、Google Cloud プロジェクトを選択または作成して、そのプロジェクトで課金が有効になっているかどうかを確認してください。BigQuery API と BigQuery 接続 API を有効にします。この実装で使用されているすべてのサービスは、同じ地域にある必要があることに注意してください。

a. 以下に示すステップに従ってデータセット「yoga_set」を作成します。

BigQuery エディタに移動して次のコマンドを入力します。

CREATE SCHEMA `<<project_id>>.yoga_set`;

b. BigLake 接続によって、きめ細かい BigQuery アクセス制御とセキュリティを維持しながら、外部データソース(このユースケースでは、画像データのための Cloud Storage)に接続できます。この接続を使用して Cloud Storage からオブジェクトを読み込みます。以下のステップに従って、BigLake 接続を作成します。

BigQuery ページの [エクスプローラ] ペインで [データを追加する] をクリックします。

4cb42b1245bb0ba6.pngBigQuery [追加] 画面

[外部データソースへの接続] をクリックして、[BigLake とリモート関数] オプションを選択します。

9ffec2b2bfcc3cd5.png外部データソース接続の構成

接続 ID を入力して、接続を作成します。サービス アカウント ID をメモしておくことを忘れないでください。この ID は、接続が作成されると画面に表示されます <<SERVICE_ACCOUNT>>。この例では、接続 ID は「yoga-pose-conn」です。地域をメモしておくことを忘れないでください。

4. Google Cloud Storage バケットの作成と権限の付与

これから Google Cloud Storage バケットを使用して、モデルを作りたいヨガのポーズの画像ファイルを格納します。バケットは、私たちがこれから分析する画像を格納するための Cloud Storage コンテナです。

a. コンソールで検索することによって Google Cloud Storage に移動してから、バケットのホームページで [バケット] をクリックし、[作成] をクリックします。

a6f6b26cffb53ae0.pngGoogle Cloud Storage [バケット] ページ

b. [バケットの作成] ページで、バケットの情報(一意の名前)を入力して継続し、その情報が上記のステップで説明したデータセットと接続と同じ地域にあることを確認し、[作成] をクリックします。

1280366a42b7bdf6.pngGoogle Cloud Storage [バケットの作成] ページ

次のステップに進む前に、サービス アカウント、バケット名、パスをメモしたことを確認してください。

c. バケットが作成されたら、(コンソール、Cloud Shell コマンド、またはプログラムによって)画像を保存し、(前に保存した)接続のサービス アカウントに対して画像にアクセスする上で必要な権限を付与します。

> export sa=<<"SERVICE_ACCOUNT">>
> gsutil iam ch serviceAccount:$sa:objectViewer "gs://<<bucket>>"

5. オブジェクト テーブルの作成

BigQuery から、作成した接続を使用してバケット内の非構造化データにアクセスするための外部オブジェクト テーブルを作成します。BigQuery エディタから以下の [CREATE SQL] を実行します。

CREATE OR REPLACE EXTERNAL TABLE `<<dataset>>.<<table_name>>`
WITH CONNECTION `us.<<connection-name>>`
OPTIONS(
object_metadata="SIMPLE", uris=["gs://<<bucket>>/<<folder_if_exists>>/*.jpg"]);

以下に示すように外部テーブルが作成されます。

bda48f566e0c292f.png

すぐに新しく作成した外部テーブルからポーズをクエリしてみましょう。

SELECT data , uri
FROM `yoga_set.yoga_poses`
WHERE REGEXP_CONTAINS(uri, 'gs://yoga_images/Downdog')
Limit 1;

以下のスクリーンショットからわかるように、非構造化データをそれが構造化データであるかのように作成し、操作することができます。

7d1784122b5013f.png

上記のクエリ結果を小さな Python スニペットにエクスポートして、結果を可視化してみましょう。

[結果を保存する] をクリックして [CSV(ローカル ファイル)] を選択して、結果をエクスポートします。次に Colab ノートブックを開いて(または作成して)、以下のコードを入力します。

from IPython.display import display
from PIL import Image
import io
import pandas as pd
import base64
df = pd.read_csv('/content/sample_data/<<your_csv>>')
imgdata = base64.b64decode(str(df.data[0]))
image = Image.open(io.BytesIO(imgdata))
display(image)

実行すると、以下のように結果が表示されます。

b8edd68cb281786a.png

これで、SQL クエリだけを使用して、Cloud Storage から外部テーブルを作成し、画像にアクセスすることができました。それでは次のセクションに進みましょう。次のセクションでは分類モデルを作成します。

6. モデルの作成と Google Cloud Storage へのアップロード

この実装では、トレーニング済みの ResNet 50 モデルを使用して、私たちが作成したばかりのオブジェクト テーブルに関する推論を実行します。ResNet 50 モデルは、画像ファイルを分析し、ある画像が対応するクラス(ロジット)に属している可能性を表す一連のベクターを出力します。

このステップに移る前に、必要な権限がすべてあることを確認してください。以下の手順に従います。

  1. この場所からモデルをダウンロードして、ローカルで保存します。
  2. 解凍して saved_model.pb と変数フォルダにする必要があります。
  3. これら 2 つ(ファイルとフォルダ)を、前のセクションで作成したバケットにアップロードします。

2629ff3eda214946.pngGoogle Cloud Storage バケット「yoga_images」と、アップロードされた ResNet モデル ファイル

このステップが完了すると、モデルに関連したファイルが、上の画像に表示されているものと同じバケット内に存在しているはずです。

7. モデルの BQML へのロードと推測

このステップでは、先に作成した外部テーブルと同じ BigQuery データセットにモデルをロードして、Cloud Storage に保存した画像に適用します。

a. BigQuery エディタから、次の SQL ステートメントを実行します。

CREATE MODEL `<<Dataset>>.<<Model_Name>>`
OPTIONS(
model_type = 'TENSORFLOW',
model_path = 'gs://<<Bucket>>/*');

実行が完了すると(データセットによってはしばらく時間がかかる場合があります)、BigQuery 内のデータセット セクションにモデルが表示されます。

435fa0919aeb57a6.png作成したモデルが表示されている BigQuery データセット

b. モデルを調べて、その入力フィールドと出力フィールドを確認します。

データセットを開いて、作成したモデルの「yoga_poses_resnet」をクリックします。[スキーマ] タブをクリックします。

e88928764f10f6ff.png[BigQuery モデル定義スキーマ] タブ

[ラベル] セクションには、出力フィールドを表す「activation_49」フィールドが表示されています。[機能] セクションには、モデルに入力されるフィールドを表す「input_1」が表示されています。推論クエリ(または予測クエリ)で、「テスト」データ用に渡すフィールドとして「input_1」を参照します。

c. ヨガのポーズを推測します。

作成したモデルを使用して、テスト画像データを分類してみましょう。作成した時に外部テーブルに変換された Cloud Storage バケットにテスト画像(ヨガのポーズ)があることを確認してください。これから、BigQuery 内のテスト画像から選択してクエリを行い、私たちが作成した BQML モデルを使用して推論を実行します。以下のクエリを使用して、テストをトリガーしてください。

SELECT *
FROM ML.PREDICT(
MODEL yoga_set.yoga_poses_resnet,
(SELECT uri, ML.DECODE_IMAGE(data) AS input_1
FROM yoga_set.yoga_poses where REGEXP_CONTAINS(uri,
'gs://yoga_images/Downdog/00000097.jpg')));

上記のクエリでは、特定されたテスト画像を 1 つ選択して、特定の URI 値(00000097.jpg)を外部テーブルに格納しています。また SELECT 部は、ML.PREDICT 関数が動作するように、ML.DECODE_IMAGE コンストラクトをフィールド「input_1」として使用しています。

実行が完了すると、以下に示すように結果が表示されます。

867018993845e943.png

ResNet モデルについて詳しく知っている方にとっては、これは分類の理解の助けとなるはずです。詳しくない方は、小さなスニペットをコーディングして、分類を視覚的に把握してみましょう。

d. 結果のフラット化

上記の出力を可視化する方法の 1 つとして、BigQuery SQL の UNNEST コンストラクトを使用して activation_49 フィールド値をフラット化する方法があります。先行ステップの結果のフラット化について、以下のクエリを参照してください。結果のクラスをさらにテキストでラベルを付けたい場合、クエリ内のプレースホルダ <<LABEL_LOGIC>> をロジックに置き換えることができます(使用時にコメント化解除)。

with predictions as (
SELECT
Uri, data, SPLIT(uri, "/")[OFFSET(ARRAY_LENGTH(SPLIT(uri, "/")) - 1)] as img,
i as label_i,
<<LABEL_LOGIC>> label,
Score
FROM ML.PREDICT(
MODEL yoga_set.yoga_poses_resnet,
(SELECT data, uri, ML.DECODE_IMAGE(data) AS input_1
FROM yoga_set.yoga_poses
WHERE
REGEXP_CONTAINS(uri,'gs://yoga_images/Goddess/00000007.jpg'))),
UNNEST(activation_49) as score WITH OFFSET i)
SELECT * FROM predictions
ORDER BY score DESC
LIMIT  5;

クラスのラベル付けロジックがない場合、クエリの出力は以下のようになります。

71f580f41f0811f3.png

しかし私のケースでは、サンプル ロジックを適用しました。結果は以下のようになります。

1c6df6ecd14fba1.png

モデルについてさらに読み、ご自分のデータとモデルの出力に最も合ったロジックを適用してください。

e. 推論の可視化

最後に、簡単な Python スニペットで分類の結果を可視化しましょう。上記のクエリ結果を CSV ファイルにエクスポートして、Python コードの中で参照します。

68756e7e4b8d7a29.png

上記の画像出力は、ヨガのポーズである「ダウンワード ドッグ」を参照しています。これは、BQML を使用した分類のために、私たちが ML.PREDICT クエリに渡したものとまったく同じテスト入力です。

8. 構造化データと非構造化データの統合

この実装で私が気に入っているのは、構造化リレーショナル テーブルのフィールドと、非構造化画像データを統合するところです。私は、ポーズとその健康関連データを保持するために、外部テーブルと同じデータセット内に構造化 BigQuery テーブルを作成しました。

125bdf848c86fbe.pngBigQuery 構造化テーブル「yoga_health」スキーマ

上記の画像は、「yoga_health」と名付けられた構造化データ テーブルのスキーマです。各フィールドは、pose、focus、health_benefit、breath となっています。以下のクエリは、構造化データと非構造化データを結合するものです。

SELECT SPLIT(uri, "/")[OFFSET(ARRAY_LENGTH(SPLIT(uri, "/")) - 2)] as pose,
a.health_benefit, breath, focus, data
FROM `abis-345004.yoga_set.yoga_health` a, yoga_set.yoga_poses b
WHERE a.pose = SPLIT(uri, "/")[OFFSET(ARRAY_LENGTH(SPLIT(uri, "/")) - 2)];

以下の結果を示します。

469bdfcffa9e19fd.png

メモ: このブログで扱ったクエリはすべて、BigQuery Magic コマンドを使用して、Python ノートブックから直接実行できます。

9. クリーンアップ

この投稿で使用したリソースについて、Google Cloud アカウントに課金されないようにするには、次の手順に従います。

  1. Google Cloud コンソールで、[リソースの管理] ページに移動します。
  2. プロジェクト リストで、削除するプロジェクトを選択し、[削除] をクリックします。
  3. ダイアログでプロジェクト ID を入力し、[シャットダウン] をクリックしてプロジェクトを削除します。

10. 完了

これで完了です。BigQuery で非構造化データを保存し、クエリを実行し、BQML を使用して分類モデルを作成し、そのモデルを使ってテスト用のヨガのポーズを予測することができました。これを実装したい場合、ご自身の Google Cloud プロジェクトで開始してください。また、Google Cloud におけるデータベースまたはその他のエンド ツー エンド アプリケーションの実装について詳しくは、私のブログにアクセスしてみてください。