15
11

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【90分】AzureのAI を活用して野生のシロクマを追跡する

Posted at

#【Azure】AI を活用して野生のシロクマを追跡する

DA-100という資格を取る過程で勉強したラーニングパスでとても面白いものがあったので紹介します。
以下のラーニングパスを実勢した内容をこの記事で紹介していきます。

出典:

 

 

このラーニングパスでのハンズオンの成果物

仮想カメラで撮影した写真をAzure Custom Visionで解析し
写真に写っている動物を分類します。 写真のメタデータから撮影場所の情報を取得し、Power BIで白くま生息地を分析するためのレポートを作成します。

image.png

 
 
##必要なAzureサービスの構成と説明

色々なAzureサービスを駆使して、ラーニングパスで成果物を作成していくのですが
各サービスの構成と利用目的については以下のように理解しています。

image.png

 

 
##このラーニングパスを実勢するご利益

Azure FunctionやAzure Custom VisionやAzure Storare/SQL Database等の主要なAzureサービスに一通り触れて理解することができる。
Power BIの基本機能から外部データソースとの連携/視覚化の装飾等を学ぶことができると思います。

Azure Functionではフローの自動化を学ぶことができました。
Azure Custom Visionでは写真の分類に必要な学習モデルの作成方法から分類まで、他サービスとの連携方法を広く学ぶことができました。
Azure Storare/SQL Databaseでは基本な機能から、他サービスとの連携方法を学ぶことができました。
Power BIについても基本機能のから外部データソースとの連携/視覚化の装飾等を学ぶことができると思います。

 

#実践していく

基本的にラーニングパスに沿ってやったことを覚書としてかいています。このハンズオンに興味がある方は是非、本家のラーニングパスを実勢してみてください。

本家はこちら:

 

###アカウントを作成し、仮想カメラをデプロイしていきます。

Azure Portalに接続して、Cloud Shell を使っていきます。

image.png

####polar-bear-rgという名前のリソースグループを作成します。

az group create --name polar-bear-rg --location southcentralus

####sirokumasanというアカウント名の変数を作成します。

ACCOUNT_NAME=sirokumasan

####先ほど作成したリソースグループにsirokumasanというアカウント名のストレージを作成します。

az storage account create --name $ACCOUNT_NAME --resource-group polar-bear-rg --location southcentralus --sku Standard_LRS

####ストレージにphotosというコンテナを作成します。

az storage container create --name photos --account-name $ACCOUNT_NAME

 
 
この時点で以下のようになっていれば問題なしです。
image.png

 
####アカウントキーを取得して、メモに控えておきます

az storage account keys list --account-name $ACCOUNT_NAME

 

これをメモ帳に控えます。
image.png

###VSCodeでnode環境を作成する

####ターミナルを開いて、作業用フォルダを作成し、フォルダに移動します。

mkdir C:\sirokumasan
cd C:\sirokumasan

image.png

 

####プロジェクトを作成し、先ほどPortalで作成したストレージへアクセスするためのライブラリを取得します。

npm init -y
npm install azure-storage --save

 

####プロジェクトにphotosというフォルダを作成します。

image.png

 
####作成したフォルダに以下から取得した画像を格納していきます。

画像:
https://github.com/MicrosoftDocs/mslearn-build-ml-model-with-azure-stream-analytics/raw/master/camera-images.zip

画像は30個あるので置いたあとはこのようになると思います!
image.png

 
 
####プロジェクトにcameras.json を作成します。

image.png

[
    {
        "deviceId" : "polar_cam_0001",
        "latitude" : 75.401451,
        "longitude" : -95.722518
    },
    {
        "deviceId" : "polar_cam_0002",
        "latitude" : 75.027715,
        "longitude" : -96.041859
    },
    {
        "deviceId" : "polar_cam_0003",
        "latitude" : 74.996653,
        "longitude" : -96.601780
    },
    {
        "deviceId" : "polar_cam_0004",
        "latitude" : 75.247701,
        "longitude" : -96.074436
    },
    {
        "deviceId" : "polar_cam_0005",
        "latitude" : 75.044926,
        "longitude" : -93.651951
    },
    {
        "deviceId" : "polar_cam_0006",
        "latitude" : 75.601571,
        "longitude" : -95.294407
    },
    {
        "deviceId" : "polar_cam_0007",
        "latitude" : 74.763102,
        "longitude" : -95.091160
    },
    {
        "deviceId" : "polar_cam_0008",
        "latitude" : 75.473988,
        "longitude" : -94.069432
    },
    {
        "deviceId" : "polar_cam_0009",
        "latitude" : 75.232307,
        "longitude" : -96.277683
    },
    {
        "deviceId" : "polar_cam_0010",
        "latitude" : 74.658811,
        "longitude" : -93.783787
    }
]

 
 

####プロジェクトにrun.jsを作成します。

image.png

'use strict';

// Connect to the storage account
var storage = require('azure-storage');

var blobService = storage.createBlobService(
    process.env.ACCOUNT_NAME,
    process.env.ACCOUNT_KEY
);

// Load image file names and create an array of cameras
var fs = require('fs');

fs.readdir('photos', (err, files) => {
    var cameras = JSON.parse(fs.readFileSync('cameras.json', 'utf8')).map(
        camera => new Camera(
            camera.deviceId,
            camera.latitude,
            camera.longitude,
            blobService,
            files
        )
    );

    // Start the cameras
    cameras.forEach(camera => {
        camera.start();
    });
});

class Camera {
    constructor(id, latitude, longitude, blobService, files) {
        this._id = id;
        this._latitude = latitude;
        this._longitude = longitude;
        this._blobService = blobService;
        this._files = files.slice(0);
        this._interval = 300000;
    }

    start() {
        // Register the first callback
        setTimeout(this.timer, Math.random() * this._interval, this);
        console.log('Started ' + this._id);
    }

    timer(self) {
        // Randomly select a photo
        var index = Math.floor(Math.random() * self._files.length);
        var filename = self._files[index]

        // Define the metadata to be written to the blob
        var metadata = {
            'latitude': self._latitude,
            'longitude': self._longitude,
            'id': self._id
        };

        // Upload the blob
        self._blobService.createBlockBlobFromLocalFile('photos', filename, 'photos/' + filename, { 'metadata': metadata }, (err, result) => {
            if (!err) {
                console.log(self._id + ': Uploaded ' + filename);
            }
            else {
                console.log(self._id + ': Error uploading ' + filename);
            }
        });

        // Register the next callback
        setTimeout(self.timer, Math.random() * self._interval, self);
    }
}

 
 

####VSCodeのターミナルでストレージアカウントへの接続情報を設定します。

set ACCOUNT_NAME=sirokumasan
set ACCOUNT_KEY=<事前にメモしたアカウントキー>

image.png

 
####VSCodeのターミナルでストレージアカウントへの接続情報を設定します。

set ACCOUNT_NAME=sirokumasan
set ACCOUNT_KEY=<事前にメモしたアカウントキー>

 
####run.jsを実行します。

node run.js

成功すると以下のようになります。
image.png

※うまくいかない場合はrun.jsにアクセス情報を直書きしてみると良いかもしれません。
image.png

###シロクマを認識するように機械学習モデルをトレーニングしてみる

####Custom Visionポータルにアクセスしてサインインします。

 
####新しいプロジェクトを作成し、設定していきます。

#####新しいプロジェクトを作成します

image.png

#####プロジェクトを設定していきます。

リソース: polar-bear-vision
プロジェクトの種類: "分類"
分類の種類: Multiclass (Single tab per image) (マルチクラス (画像ごとに 1 つのタブ))
ドメイン: General (一般)

image.png

####プロジェクトで使用するホッキョクギツネの画像を取得して設定していきます。

画像取得先:
https://github.com/MicrosoftDocs/mslearn-build-ml-model-with-azure-stream-analytics/raw/master/training-images/arctic-fox.zip

 
#####画像をアップロードしていきます。

image.png

ホッキョクギツネとして『arctic-fox』とタグづけます。

image.png

 
 

####プロジェクトで使用する白くまの画像を取得して設定していきます。

画像取得先:
https://github.com/MicrosoftDocs/mslearn-build-ml-model-with-azure-stream-analytics/raw/master/training-images/polar-bear.zip

 
#####画像をアップロードして白くまとして『polar-bear』とタグづけます。

image.png

 
 

####プロジェクトで使用するセイウチの画像を取得して設定していきます。

画像取得先:
https://github.com/MicrosoftDocs/mslearn-build-ml-model-with-azure-stream-analytics/raw/master/training-images/walrus.zip

 
#####画像をアップロードしてセイウチとして『walrus』とタグづけます。

image.png

####モデルをトレーニングしていきます。

image.png

トレーニングタイプはクイックを指定します

image.png

 
 

トレーニングが終わると以下のような画面が表示されます。 完了までに少し時間がかかります。

image.png

####クイックテストを行っていきます。

#####クイックテストの実行
image.png

テストで使用する画像は以下から取得します。
https://github.com/MicrosoftDocs/mslearn-build-ml-model-with-azure-stream-analytics/raw/master/testing-images/testing-images.zip

 
 

#####クイックテストの実行結果

画像にシロクマが含まれるかどうかのモデルによる予測が十分満足できるレベルに熟達するまで
これらの画像を活用して繰り返しテストを行います。

image.png

  
 

####モデルを発行します

#####Publishを選択します
image.png

 
#####Prediction resourceを選択してモデルを発行します

image.png

#####Prediction URLから接続情報を取得します

image.png

 
 

赤枠の部分を控えます

image.png

 
 

###Azure Portalでファンクションを作成します。

####リソースの作成から関数アプリを選択します

image.png

####関数アプリを作成します

image.png

####関数を作成します

image.png

BlobTriggerのindex.jsを編集していきます。 
ストレージコネクションストリングはストレージアカウントの接続文字列を設定します。

※Pathの部分は以下のようにphotos/{name}として監視するコンテナ名を指定する必要があります

image.png

image.png

module.exports = function (context, myBlob) {
    var predictionUrl = process.env.PREDICTION_URL;
    var predictionKey = process.env.PREDICTION_KEY;
    var storageConnectionString = process.env.<CONNECTION_STRING_NAME>;

    var storage = require('azure-storage');
    var blobService = storage.createBlobService(storageConnectionString);
    var blobName = context.bindingData.name;
    var blobUri = context.bindingData.uri;

    // Read the blob's metadata
    blobService.getBlobMetadata('photos', blobName, (err, result, response) => {
        if (!err) {
            var latitude = result.metadata.latitude;
            var longitude = result.metadata.longitude;
            var id = result.metadata.id;

            // Generate a shared access signature for the Custom Vision service
            var now = new Date();
            var expiry = new Date(now).setMinutes(now.getMinutes() + 3);

            var policy = {
                AccessPolicy: {
                    Permissions: storage.BlobUtilities.SharedAccessPermissions.READ,
                    Start: now,
                    Expiry: expiry
                },
            };

            var sas = blobService.generateSharedAccessSignature('photos', blobName, policy);

            // Pass the blob URL to the Custom Vision service
            var request = require('request');

            var options = {
                url: predictionUrl,
                method: 'POST',
                headers: {
                    'Prediction-Key': predictionKey
                },
                body: {
                    'Url': blobUri + '?' + sas
                },
                json: true
            };

            request(options, (err, result, body) => {
                if (!err) {
                    var probability =  body.predictions.find(p => p.tagName.toLowerCase() === 'polar-bear').probability;
                    var isPolarBear = probability > 0.8; // 80% threshold
                     if (isPolarBear) {
                        context.log('POLAR BEAR detected by ' + id + ' at ' + latitude + ', ' + longitude);
                    }
                    else {
                        context.log('Other wildlife detected by ' + id + ' at ' + latitude + ', ' + longitude);
                    }

                    context.done();
                }
                else {
                    context.log(err);
                    context.done();
                }
            });
        }
        else {
            context.log(err);
            context.done();
        }
    });
};

 
 
####関数アプリのコンソールで以下のコマンドを実行します。

npm install request
npm install azure-storage

警告は無視していいらしい。

image.png

 

####関数アプリのに前工程で取得したPrediction-URLとPrediction-Key を設定していきます。

image.png

 
 

####設定後にブロブに新たに画像を追加すると、トリガーが作動して画像を自動で認識して分類することを確認することができます。

image.png

画像は以下の画像をアップロードしました。
https://docs.microsoft.com/ja-jp/learn/modules/build-ml-model-with-azure-stream-analytics/media/image-12.jpg

 
 

####ログを確認し、ログ上でカスタムビジョンと連携し、画像が正しく認識され分類されたことを確認します。

※何回か試したので、ファイル名はimage-16.jpgになってます。
(インクリメントしながら確認しました)

image.png

 
 

####VScodeでもう一度run.jsを実行して、ストレージに写真を送信してみます。

image.png

 
 
 
####カスタムビジョンが実行され、VScodeから送信した画像から白くまを検出していることがわかります!
超おもしろい!!
image.png

 

###Azure SQL Databaseをデプロイする

####Azure Portalからクラウドシェルを開きます
image.png

####作成するデータベース情報を変数セットしておきます

SERVER_NAME="<server-name>"
ADMIN_USERNAME="<admin-username>"
ADMIN_PASSWORD="<admin-password>"
DATABASE_NAME="<database-name>"

※パスワードはパスワードバリデーションチェックにひっかからないようにやや複雑目なパスワードを設定することを推奨します。

 
####データベースサーバーを作成します

az sql server create --name $SERVER_NAME --resource-group polar-bear-rg --location westus --admin-user $ADMIN_USERNAME --admin-password $ADMIN_PASSWORD

※デフォルトのロケーションはnorthcentralusになっていますが対象ロケーションだと作成エラーになるのでロケーションはwestusに変更しています。
 
 

####データベースを作成します

az sql db create --resource-group polar-bear-rg --server $SERVER_NAME --name $DATABASE_NAME --service-objective S0

 
 

####リソースグループにSQLデータベースができていることを確認します

image.png

 
 
 
####SQLサーバーのファイアウォールの設定を変更し、あとでPower BIデスクトップから接続できるようにしておきます

image.png

 
 

####SQLデータベースのクエリエディタでSQLを実行して、テーブルを作成します

image.png

SQL

CREATE TABLE [dbo].[PolarBears]
(
    [Id] [uniqueidentifier] NOT NULL,
    [CameraId] [nvarchar](16) NULL,
    [Latitude] [real] NULL,
    [Longitude] [real] NULL,
    [Url] [varchar](max) NULL,
    [Timestamp] [datetime] NULL,
    [IsPolarBear] [bit] NULL,
    PRIMARY KEY CLUSTERED ([Id] ASC)
    WITH (STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF) ON [PRIMARY]
)
ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO

ALTER TABLE [dbo].[PolarBears] ADD DEFAULT (newid()) FOR [Id]
GO

ALTER TABLE [dbo].[PolarBears] ADD DEFAULT (getdate()) FOR [Timestamp]
GO

ALTER TABLE [dbo].[PolarBears] ADD DEFAULT ((0)) FOR [IsPolarBear]
GO

 
 

####テーブルが作成できていることを確認します。

image.png

 
 

####関数を変更します。
 

関数アプリのコンソールでnpm installを実行します
npm install tedious

image.png

tedious パッケージにより、Node.js アプリから SQL Server や Azure SQL Database と接続が可能になります。

 
 

#####最初のほうに作ったBlob Triggerのコードとテストを開きます。

image.png

 
#####コードにデータベースへの接続情報を追記します。 ダブルクォーテーション内は自身が設定した接続情報で置き換えます。

var databaseUserName = process.env.DATABASE_USER_NAME;
var databasePassword = process.env.DATABASE_PASSWORD;
var databaseServer = 'DATABASE_SERVER_NAME.database.windows.net';
var databaseName = 'DATABASE_NAME';

#####関数を一部差し換えます

◆差し換え前

if (isPolarBear) {
    context.log('POLAR BEAR detected by ' + id + ' at ' + latitude + ', ' + longitude);
}
else {
    context.log('Other wildlife detected by ' + id + ' at ' + latitude + ', ' + longitude);
}

context.done();

 
◆差し換え後

以下、本家のコードに足りてない部分があり追加しました。
dbConnection.connect();
上記のコードが抜けているとSQLDatabaseへの接続エラーが出てしまいます。
2021/9/13時点では本家のコードだけではエラーになります。

// Update the database
var Connection = require('tedious').Connection;
var Request = require('tedious').Request;

var config =
{
    authentication:
    {
        type: 'default',
        options:
        {
            userName: databaseUserName,
            password: databasePassword
        }
    },
    server: databaseServer,
    options:
    {
        database: databaseName,
        encrypt: true
    }
};

var dbConnection = new Connection(config);

dbConnection.on('connect', err => {
    if (!err) {
        var query = "INSERT INTO dbo.PolarBears (CameraId, Latitude, Longitude, URL, Timestamp, IsPolarBear) " +
            "VALUES ('" + id + "', " + latitude + ", " + longitude + ", '" + blobUri + "', GETDATE(), " + (isPolarBear ? "1" : "0") + ")";

        var dbRequest = new Request(query, err => {
            // Called when request completes, with or without error
            if (err) {
                context.log(err);
            }

            dbConnection.close();
            context.done();
        });

        dbConnection.execSql(dbRequest);
    }
    // 以下、本家のコードに足りてない部分があり追加しました。
    dbConnection.connect();
    else {
        context.log(err);
        context.done();
    }
});

 
 

####VSCodeで再度run.jsを起動します

image.png

 
 

####クエリエディタでデータが蓄積できていることを確認します

image.png

 
 
 

###Power BI レポートを作製する

 

Power BIデスクトップを起動してSQLデータベースに接続します

image.png

 

image.png

 
 

Power BIデスクトップを起動してSQLデータベースに接続します

image.png

SQLステートメント

SELECT TOP 20 Id, CameraId, Latitude, Longitude, Url, Timestamp, FORMAT(Timestamp,'MM/dd/yyyy h:mm:ss tt') AS TimestampLabel, IsPolarBear FROM dbo.PolarBears ORDER BY Timestamp DESC

 

接続の方法をデータベースに切り替えて、接続していきます。

image.png

 
 

いよいよデータベースの情報を視覚化していきます!!

 
#####視覚化で白くま判定と経度情報を設定していきます。

image.png

 

各経度の情報を集計しないように設定していきます。

image.png

 
 

少しずつ形になっています

image.png

 

空きスペースにCameraId、IsPolarBear、TimestampLabelの表を作成します

image.png

 

空きスペースにIsPolarBear と Latitudeの円グラフを作成します

image.png

 

視覚化の表示の値をカウントに切り替えます

image.png

 

IsPolarBearのスライサーを作成します

image.png

 
 

装飾していきます

image.png

動作確認していく

SQLデータベースの情報を削除します

DELETE FROM dbo.PolarBears

 
 

VSCodeでデータを再投入します

node run.js

 
 

Power BIのデータを更新します

image.png

データが更新され、白くまの生息地を追うことができるようになります

image.png

非常に面白いラーニングパスでした。
これからAzureを学ばれる方に是非おすすめしたいラーニングパスだと思います。

15
11
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
15
11

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?