Azure Custom VisionでトレーニングしたモデルをAPIで使いたい

#やりたいこと

Azureでトレーニングしたモデルを手元のPC上で扱いたい。

#実行コード

```python
!pip install azure-cognitiveservices-vision-customvision
import glob
from azure.cognitiveservices.vision.customvision.prediction import CustomVisionPredictionClient
from azure.cognitiveservices.vision.customvision.training.models import ImageFileCreateBatch, ImageFileCreateEntry, Region
from msrest.authentication import ApiKeyCredentials
import os, time, uuid

ENDPOINT = "https://****************.cognitiveservices.azure.com/"
prediction_key = '***************************'
prediction_resource_id = "/subscriptions/*****************************************/resourceGroups/Fish/providers/Microsoft.CognitiveServices/accounts/****************"
projectID = '*******************************'
publish_iteration_name = 'Iteration1'

def testModel(testfiles, fishname):
    data_count = len(testfiles)
    true_count = 0

    # 予測用インスタンスの作成　…①
    prediction_credentials = ApiKeyCredentials(in_headers={"Prediction-key": prediction_key})
    predictor = CustomVisionPredictionClient(ENDPOINT, prediction_credentials)
    for testfile in testfiles:
        predicts = {}
        with open(testfile, mode='rb') as f:
            # 予測実行　…②
            results = predictor.classify_image(projectID, publish_iteration_name, f.read())

        # 予測結果のタグの数だけループ　…③
        for prediction in results.predictions:
            # 予測した魚とその確率を紐づけて格納　…④
            predicts[prediction.tag_name] = prediction.probability

        # 一番確率の高い魚を予測結果として選択　…⑤
        prediction_result = max(predicts, key=predicts.get)

        # 予測結果が合っていれば正解数を増やす
        if fishname == prediction_result:
            true_count += 1

    # 正解率の算出
    accuracy = (true_count / data_count) * 100
    print('正解率:' + str(accuracy) + '%')

#検証用画像を保存したルートディレクトリパス
root_dir = 'fish_images/'
#検証対象の魚一覧
fishnames = ['アイゴ', 'オニカサゴ']

for fishname in fishnames:
    print('*****' + fishname + '*****')
    #testデータのリストを取得
    testfiles = glob.glob(root_dir + fishname + '/test/*')
    testModel(testfiles, fishname)
```

```python:エラーメッセージ
CustomVisionErrorException: Operation returned an invalid status code 'Unauthorized'
```

エラーメッセージの内容は'CustomVisionErrorException：操作が無効なステータスコード「Unauthorized」を返しました'となっている。該当コードをみてみると

```python:該当コード
---------------------------------------------------------------------------
CustomVisionErrorException                Traceback (most recent call last)
<ipython-input-8-4906765a0299> in <module>
      8     #testデータのリストを取得
      9     testfiles = glob.glob(root_dir + fishname + '/test/*')
---> 10     testModel(testfiles, fishname)

<ipython-input-7-145134263771> in testModel(testfiles, fishname)
     10         with open(testfile, mode='rb') as f:
     11             # 予測実行　…②
---> 12             results = predictor.classify_image(projectID, publish_iteration_name, f.read())
```
12行目のprojectID, publish_iteration_nameに問題があると考えられる。このエラーメッセージを検索してみると、[こちら](https://stackoverflow.com/questions/67399647/operation-returned-an-invalid-status-code-unauthorized-on-azure-cognitive)の記事がヒットした。

API typeでALL Cognitive Servicesとすると良いとのこと。私はCustom Vision Trainingとしているためこうなっているのではとのこと。

![Screen Shot 2021-10-30 at 13.00.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/551489/3f11fc05-23dc-a570-083f-6d9f4955d02d.png)

#対処

ただ、API typeを変える以外に方法はあるはずなので確認してみると[こちら](https://qiita.com/mine820/items/257da3d0f38f4c4e2625)に参考になるページがあった。サイトのとおりに一度やってみる。以下のようなコードに変更した。

```python
import requests
import json

url = 'https://*********************.cognitiveservices.azure.com/customvision/v3.0/Prediction/********************************/classify/iterations/Iteration1/image'
headers={'content-type':'application/octet-stream','Prediction-Key':'**********************'}

def testModel(testfiles, fishname):
    data_count = len(testfiles)
    true_count = 0

    for testfile in testfiles:
        predicts = {}
        #予測実行
        response =requests.post(url,data=open(testfile,"rb"), headers=headers)
        response.raise_for_status()
        analysis = response.json()
        
        # 予測結果のタグの数だけループ　…③
        for prediction in range(len(analysis["predictions"])):
            # 予測した魚とその確率を紐づけて格納　…④
            predicts[analysis["predictions"][prediction]["tagName"]] = analysis["predictions"][prediction]["probability"]

        # 一番確率の高い魚を予測結果として選択　…⑤
        prediction_result = max(predicts, key=predicts.get)

        # 予測結果が合っていれば正解数を増やす
        if fishname == prediction_result:
            true_count += 1

    # 正解率の算出
    accuracy = (true_count / data_count) * 100
    print('正解率:' + str(accuracy) + '%')

#検証用画像を保存したルートディレクトリパス
root_dir = 'fish_images/'
#検証対象の魚一覧
fishnames = ['アイゴ', 'オニカサゴ']

for fishname in fishnames:
    print('*****' + fishname + '*****')
    #testデータのリストを取得
    testfiles = glob.glob(root_dir + fishname + '/test/*')
    testModel(testfiles, fishname)
```

すると以下のように結果がエラー無く返ってきたため成功したことがわかる。

```python:result
*****アイゴ*****
正解率:81.25%
*****オニカサゴ*****
正解率:92.85714285714286%
```

#課題

実行することはできてひとまず良かったが、大きく２つ課題が生じた。

+ 動かなかったコードについて原因がわかっていない。Unautherized
+ 毎回WebAPIを叩いているため予測をするのに時間がかかること