CCCMKホールディングス TECH LABの Tech Blog

TECH LABのエンジニアが技術情報を発信しています

ブログタイトル

Power AppsとPower Automateを使って機械学習モデルのデモアプリを作ってみました!

こんにちは、技術開発の三浦です。

スマートスピーカーが我が家にやってきました。一緒にリモコンをコントロールできるデバイスも購入し、音声でテレビや電気を付けたり消したり出来るようになりました。実は来る前はそんなに期待していなくて、すぐに飽きるだろうって思っていたのですが、使い始めるとだんだん便利だなと思うようになりました。たとえばちょっとした計算をしたい時、これまでは電卓やアプリで計算していたのですが、この前「1,000÷60は?」っ聞いたらちゃんと答えてくれてちょっとうれしかったです。

実は今週11/24(木)に、Microsoft様の「Azure Analytics Day 2022 Autumn」というイベントで少しお話させて頂く機会を頂戴しました!

msevents.microsoft.com

内容はこのブログでも何回か触れた、Azure Databricksを中心とした機械学習モデル開発環境を構築している話です。よろしければぜひご覧いただければと思います!

さて、この前の記事で、Azure Databricksで開発したモデルをAzure Machine Learningに登録し、推論機能を提供するAPIを構築する方法をご紹介しました。

techblog.cccmk.co.jp

この記事の中ではAPIを別のPythonプログラムから利用して、好きな画像に対してモデルの推論結果を得ることが出来るところまで試してみました。今回はこのAPIを利用する別の手段として、Microsoftが提供している"Power Apps"と"Power Automate"というサービスを使って機械学習モデルを試すことが出来るデモアプリを作ってみたので、その話をご紹介したいと思います。

作ったもの

今回作ったのは、下のような画面で構成された、簡単なアプリケーションです。

今回作ったアプリケーションです。

画面の配置や動きはPower Appsで作成しています。まず左の画像を表示している部分をクリックすると、ファイル選択ダイアログが開き、ローカル環境にある画像ファイルを選択すると画面上に表示することが出来ます。

画像を表示した後、[推論]ボタンをクリックすると、表示されている画像データをbase64形式に加工してPower Automateで動く処理フローに渡されます。Power Automateでは受け取ったbase64のデータを推論APIに渡し、その結果を取得します。その後取得したJSONデータを解析し、Power Apps側に結果を戻します。

最後にPower Appsにその結果をテーブル形式で表示します。

Power AppsではアプリのデザインはPower Pointのような感覚で行うことが出来ました。一方でアプリの動作はスクリプトを書いて指示する必要があり、こちらは慣れるのに若干苦戦をしました。ただ、一度アプリを作ってしまえば画面のちょっとした変更ならPower Pointを編集する感覚で出来るので、ユーザーが自身で使いやすいように改善出来る点がとても魅力的だと思います。

以降ではこのアプリの作り方について、簡単にご紹介していきます。

準備

推論APIの修正

以前作った推論APIは入力として画像データを3x32x32のfloat型の配列で受け付ける仕様になっていたのですが、それだと画像データを配列に変換する処理をAPI利用者側で実装する必要があり、利用するまでのハードルが高いことが分かりました。実際、今回Power Apps, Power Automateでどうやって変換処理を実装したら良いか分かりませんでした・・・。

なので、利用者は推論APIに対して画像データを加工せずにそのまま渡せるようにし、推論APIの中で必要な変換処理を行うように修正を加えました。

API接続情報

推論APIに関するエンドポイントURL、アクセスキー、そして推論クラス('airplane', 'dog'など、何を推論出来るのか)といった情報は、One Drive上のExcelファイルに記入し、Power Appsから参照するようにしました。Power AppsからOne Drive上のExcelファイルを参照するために、対象のExcelファイルをPower Appsアプリにデータとして追加しました。

Excelの中はこのようにテーブル形式にしておく必要があります。

コントロールの配置

最初にアプリに必要になる画面部品、コントロールを追加していきます。配置したコントロールは以下の通りです。

配置したコントロールです。

"画像を追加"コントロール

このコントロールは特に特別な設定は必要なく、配置するだけでクリックするとファイル選択ダイアログが開き、画像ファイルを選択するとその画像がコントロール上に表示される、という動作をしてくれます。ツリービューでこのコントロールを確認すると、さらに2つの部品で構成されていることが分かります。

"画像を追加"コントロール

読み込んだ画像データを保持しているのは上の図の下の"UploadImage1"の方で、他の場所から画像データを参照する場合はUploadImage1.Imageのような形で参照することが出来ます。

"ドロップダウン"コントロール

ドロップダウンで推論に使用するモデルを選べるようにしたくて設置したコントロールです。とはいっても現状モデルは1つだけなので、今は必要のないコントロールです。"ドロップダウン"コントロールのプロパティから、参照するデータを指定することが出来ます。ドロップダウンに表示したいのはモデル名ですが、こちらはOne Driveに保存したExcelファイルに"MODEL"というカラムに記載しているので参照対象のデータとしてそちらを指定します。

"ボタン"コントール(推論実行)

ここが今回の一番のキモであり、最も苦労したところです・・・。"ボタン"コントロールには"OnSelect"というプロパティがあり、そこにボタンをクリックしたときに実行する処理を書くことが出来ます。以下のような処理を書きました。

//ドロップダウンで選択中のモデルの推論APIのURLを"Classifier"データから参照します。
UpdateContext(
    {
        ModelPath: First(Search(Classifier,Dropdown1.SelectedText.MODEL,"MODEL")).URL
    }
);

//ドロップダウンで選択中のモデルの推論APIのKEYを"Classifier"データから参照します。
UpdateContext(
    {
        ModelKey: First(Search(Classifier,Dropdown1.SelectedText.MODEL,"MODEL")).KEY
    }
);

//Power Automateを呼び出して推論APIを実行します。
UpdateContext(
    {
        //実行結果をPredという変数で受けます。
        Pred:('PowerApp->HTTP'.Run(
            /**
            Power Automateに渡す1つ目のパラメータです。
            JSON関数を使うと、base64データをText型に変換できます。
            しかしそのままだと先頭に余分な文字列"data:image/jpeg;base64,"が
            含まれるのでSubstitute関数で置換して除外しています。
            **/
            Substitute(JSON(UploadedImage1.Image, JSONFormat.IncludeBinaryData),
            "data:image/jpeg;base64,",""),
            //2つ目のパラメータ、推論APIのURL
            ModelPath, 
            //3つ目のパラメータ、推論APIのKEY
            ModelKey
        ).predictions)});

/**
Excelに記載された推論クラス情報を取得します。
最初にLabel変数で文字列"['airplane','automobile',...]"を取得し、その後文字列から"[","]"を除外、
","で分割してTable型の変数Classに格納します。
**/
UpdateContext(
    {
        Label: First(Search(Classifier,Dropdown1.SelectedText.MODEL,"MODEL")).Class
    }
);
UpdateContext(
    {
        Class: Split(Substitute(Substitute(Label,"[",""),"]",""),",")
    }
);

/**
Power Automateで返ってきた結果が"[[1.4,1.1,...]]"のような文字列になっているので、まず不要な文字を
削除し、次に","で分割してText要素を持つTable型の変数Scoreに格納します。
**/
UpdateContext(
    {
        Score: Split(Substitute(Substitute(Pred, "[[", ""),"]]",""),",")
    }
);

/**
"テーブル"コントロールに表示するTable型のデータPredTableに値を格納する部分です。
CountRows(Score)でテーブルの行数を取って、その行番号を"ForAll(Sequence(CountRows(Score)),"
で順々に取得します。ForAll関数ではValueループ対象の変数の現在の値が格納されるので、
ClassとScoreからIndex関数を使って対象の行の値を取って、PredTableのカラム名に対応付けをしています。
**/
ClearCollect(
    PredTable,
    ForAll(Sequence(CountRows(Score)),{class:Index(Class,Value).Result,score:Value(Index(Score,Value).Result)})
)

詳細についてはコメント部分を参照頂ければ・・・と思います。Power Appsには2種類の変数があります。1つはグローバル変数で、もう1つはコンテキスト変数です。グローバル変数はアプリ内の全ての画面上で共有出来、コンテキスト変数はアプリ内のその画面の中だけで共有が出来る変数です。グローバル変数はSetを、コンテキスト変数はUpdateContextという関数で値をセットすることが出来ます。この処理の結果はPredTableというTable型の変数に格納されます。

"テーブル"コントロール

表形式のデータを表示するコントロールです。[推論]ボタンクリック時の処理で値が格納されるPredTableを参照先のデータに指定します。

テーブルが参照するデータ

"アイコン"(上矢印、下矢印)

"アイコン"もボタンと同様に"OnSelect"プロパティを持っていて、クリックした時の動作をスクリプトで書くことが出来ます。実現したいのはPredTableの値をScoreカラムで昇順、降順で並び変えた値に変更したい、ということなので、たとえば"↑"アイコンをクリックしたときの動作は

ClearCollect(PredTable,SortByColumns(PredTable,"score",Ascending))

のようになります。ClearCollectはTable型のような複数の要素で成り立つコレクションに値をセットするための関数です。

Power Automateで作ったフロー

最後に推論ボタンを押した時に呼ばれるPower Automateのフローをご紹介します。

Power Automateで作ったフローの全体図

最初の"変数を初期化する","変数を初期化する2","変数を初期化する3"の部分はPower Appsがこのフローを呼び出すときに渡す3つのパラメータ(base64の画像データ、APIのURL、APIのKEY)を、Power Automate側の変数(image_data, path, keyにセットする処理です。そして"HTTP"の部分で推論APIにリクエストを送信します。設定は以下の様にしました。

"HTTP"の設定

あとはレスポンスで受け取ったJSONの解析、解析結果の変数への格納、そしてPower Appsに結果を戻す処理が続きます。

まとめ

ということで、今回はMicrosoft Power PlatformのPower AppsとPower Automateを使って画像推論モデルのデモアプリを作った話をご紹介しました。アプリの画面がPower Point感覚で直感的に構築出来る点がとても魅力的です。今後も上手く活用していきたいと思いました。