Amazon Lookout for Visionで異常検出 その２（Python3.6）

# はじめに
[Amazon AI by ナレコム アドベントカレンダー 2020](https://qiita.com/advent-calendar/2020/knowcom-amazonai)の20日目の記事です。

前回の[Amazon Lookout for Visionで異常検出 その１](https://qiita.com/SatoshiGachiFujimoto/items/d16da5fba4a1c85fb1dc)では、AWSコンソール上でAmazon Lookout for Visionの異常検出プロジェクトを作成し、学習とトライアル検出を行いました。
今回は、boto3を用いて作成したモデルをホスティングし、推論を行ってみましょう。

# 開発環境
- AWS CLI 2.1.13
- boto3 1.16.40
- Python 3.6
- Windows PC
<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">プライムデーで安かったので、予備PC<br>【基本スペック】<br>OS：Windows 10 Home 64bit、CPU：インテル Core i7-9750H、GPU：NVIDIA GeForce GTX 1650 Ti Max-Q デザイン 4GB GDDR5、メモリ：16GB（8GB ×2）、SSD：512GB、液晶パネル：15.6インチ、フルHD（1,920×1,080）、リフレッシュレート120Hz <a href="https://t.co/S8RIsayFPY">pic.twitter.com/S8RIsayFPY</a></p>&mdash; 藤本賢志(ガチ本)@XRKaigi (@sotongshi) <a href="https://twitter.com/sotongshi/status/1317758401946947591?ref_src=twsrc%5Etfw">October 18, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

# 導入

IAMユーザーの作成、[AWS CLI & SDKs の設定](https://docs.aws.amazon.com/lookout-for-vision/latest/developer-guide/su-awscli-sdk.html)、AmazonLookoutVisionFullAccessのアクセス権限を与えます。AmazonLookoutVisionFullAccessポリシーが見つからなかったので、作成しました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/63863/cdb3ccb0-a52b-6e28-62a0-edafeb765dd4.png)

AWS CLIとboto3を更新しておきましょう。

[Windows での AWS CLI バージョン 2 のインストール、更新、アンインストール](https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/install-cliv2-windows.html#cliv2-windows-install)

~~~console
pip install -U boto3
~~~

## [Start](https://docs.aws.amazon.com/lookout-for-vision/latest/developer-guide/run-start-model.html)

作成したモデルを開始します。プロジェクト名（test）とモデルのバージョン（1）を指定します。

~~~start_model.py
#Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.

import boto3

def start_model(project_name, model_version, min_inference_units):

    client=boto3.client('lookoutvision')

    try:
        # Start the model
        print('Starting model version ' + model_version  + ' for project ' + project_name )
        response=client.start_model(ProjectName=project_name,
            ModelVersion=model_version,
            MinInferenceUnits=min_inference_units)
        print('Status: ' + response['Status'])


    except Exception as e:
        print(e)
        
    print('Done...')
    
def main():
    project='test'
    model_version='1'
    min_inference_units=1 
    start_model(project, model_version, min_inference_units)

if __name__ == "__main__":
    main()    
~~~

状態が「STARTING_HOSTING」となります。

~~~console
$ python start_model.py
Starting model version 1 for project test
Status: STARTING_HOSTING
Done...
~~~

## [Describe]((https://docs.aws.amazon.com/lookout-for-vision/latest/developer-guide/developer-guide.pdf))

モデルを開始したら、状態を確認しましょう。

~~~describe_model.py
#Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.

import boto3
import json

def describe_models(project_name):

    client=boto3.client('lookoutvision')

    try:
        #list models
        response=client.list_models(ProjectName=project_name)
        print('Project: ' + project_name)
        for model in response['Models']:
            print('Version: ' + model['ModelVersion'])
            print('ARN: ' + model['ModelArn'])
            if 'Description' in model:
                print('Description: ' + model['Description'])
            print('Status: ' + model['Status'])
            print('Status Message: ' + model['StatusMessage'])

            # get model description
            model_description=client.describe_model(ProjectName=project_name,ModelVersion=model['ModelVersion'])
            print('Status: ' + model_description['ModelDescription']['Status'])
            print('Message: ' + model_description['ModelDescription']['StatusMessage'])

            if 'Performance' in model_description['ModelDescription']:
                print('Recall: ' + str(model_description['ModelDescription']['Performance']['Recall']))
                print('Precision: ' + str(model_description['ModelDescription']['Performance']['Precision']))

            if 'OutputConfig' in model_description['ModelDescription']:
                print('Output config: ' + str(model_description['ModelDescription']['OutputConfig']))
            print()

        print('Done...')

    except Exception as e:
        print(e)        

def main():
    project_name='test'
    describe_models(project_name)

if __name__ == "__main__":
    main()
~~~

状態が「STARTING_HOSTING」となっています。

~~~console
$ python describe_model.py
Project: test
Version: 1
ARN: arn:aws:lookoutvision:us-east-1:xxxxxxxxxxxx:model/test/1
Status: STARTING_HOSTING
Status Message: Hosting starting.
Status: STARTING_HOSTING
Message: Hosting starting.
Recall: 0.9090909361839294
Precision: 0.9090909361839294
Output config: {'S3Location': {'Bucket': 'lookoutvision-us-east-1-xxxxxxxxxx', 'Prefix': 'project/test/model/'}}

Done...
~~~

「HOSTED」になれば使用可能です。

~~~console
Project: test
Version: 1
ARN: arn:aws:lookoutvision:us-east-1:xxxxxxxxxxxx:model/test/1
Status: HOSTED
Status Message: The model is running
Status: HOSTED
Message: The model is running
Recall: 0.9090909361839294
Precision: 0.9090909361839294
Output config: {'S3Location': {'Bucket': 'lookoutvision-us-east-1-xxxxxxxxxx', 'Prefix': 'project/test/model/'}}

Done...
~~~

もしモデルが停止していた場合は、「TRAINED」となっています。

~~~console
$ python describe_model.py
Project: test
Version: 1
ARN: arn:aws:lookoutvision:us-east-1:xxxxxxxxxxxx:model/test/1
Status: TRAINED
Status Message: The model is ready for hosting
Status: TRAINED
Message: The model is ready for hosting
Recall: 0.9090909361839294
Precision: 0.9090909361839294
Output config: {'S3Location': {'Bucket': 'lookoutvision-us-east-1-xxxxxxxxxx', 'Prefix': 'project/test/model/'}}

Done...
~~~

## [Detect](https://docs.aws.amazon.com/lookout-for-vision/latest/developer-guide/inference-detect-anomalies.html)

モデルの状態が「HOSTED」になったら、推論してみましょう。これらの画像をテストしました。

|異常（capsule/test/squeeze/001.png） |正常（capsule/test/good/022.png） | 
|:--|:--|
|![000.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/63863/a93fb65f-01d5-75f0-2303-4a7568544f77.png) |![022.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/63863/53547c4f-18d0-786a-a504-887b8c3eee17.png) |
 

~~~detect.py
#Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.

import boto3

def detect_anomalies(project_name,model_version,photo):
     

    client=boto3.client('lookoutvision')

    #Call DetectAnomalies 

    with open(photo, 'rb') as image:
        response = client.detect_anomalies(ProjectName=project_name, 
        # ContentType='image/jpeg',
        ContentType='image/png',
        Body=image.read(),
        ModelVersion=model_version)
    print ('Anomalous?: ' + str(response['DetectAnomalyResult']['IsAnomalous']))
    print ('Confidence: ' + str(response['DetectAnomalyResult']['Confidence']))


def main():
    project_name='test'
    photo="capsule/test/squeeze/000.png"
    model_version='1'
  
    anomalous=detect_anomalies(project_name,model_version,photo)
    

if __name__ == "__main__":
    main()
~~~

正しい予測結果が出ています。

~~~console
$ python detect.py
Anomalous?: True
Confidence: 0.9995505213737488
~~~

~~~console
$ python detect.py
Anomalous?: False
Confidence: 0.9181729555130005
~~~

モデルが「STARTING_HOSTING」の状態のときに推論した場合、以下のようなエラーが出ます。

~~~console
botocore.errorfactory.ConflictException: An error occurred (ConflictException) when calling the DetectAnomalies operation: Detect cannot be performed when the resource is in STARTING_HOSTING. The resource must be in HOSTED to perform this action
~~~

また、AWSコンソールのLookout for Visionのダッシュボードで検出結果を見ることができます。

最初に異常画像を推論した結果です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/63863/908d146d-0922-d1b0-1f52-a03d8f0a7b7d.png)

次に正常画像を推論した結果です。ダッシュボードはすぐに更新されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/63863/a66c89e7-8e9b-c6d2-7eca-0ec0205b2668.png)

## [Stop](https://docs.aws.amazon.com/lookout-for-vision/latest/developer-guide/run-stop-model.html)

モデルを停止します。停止するまで料金かかるので注意しましょう。

~~~stop_model.py
#Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.

import boto3

def stop_model(project_name, model_version):

    client=boto3.client('lookoutvision')


    try:
        # Stop the model
        print('Stopping model version ' + model_version  + ' for project ' + project_name )
        response=client.stop_model(ProjectName=project_name,
            ModelVersion=model_version)
        print('Status: ' + response['Status'])


    except Exception as e:
        print(e)
        
    print('Done...')
    
def main():
    project='test'
    model_version='1'
    stop_model(project, model_version)

if __name__ == "__main__":
    main()
~~~

~~~console
$ python stop_model.py
Stopping model version 1 for project test
Status: STOPPING_HOSTING
Done...
~~~

# まとめ
前回作ったモデルをboto3を用いてホスティングし、推論してみました。これで運用できそうです。

# 参考
- [Amazon Lookout for Vision Developer Guide](https://docs.aws.amazon.com/lookout-for-vision/latest/developer-guide/developer-guide.pdf)