ABEJA Platformの事前推論を使って、高速で機械学習用のデータを作成する

## はじめに

本記事は、以下の手順をまとめた記事です。
- ABEJA Platformの事前推論の機能を使用して、高速で機械学習用の教師データを作成する

※事前推論は、2019年7月3日現在、β版の機能です。今後、手順は変更になる可能性があります。

## 事前推論機能とは？
機械学習用の教師データの作成（アノテーション）は、とても労力がかかる作業です。自分で大量のアノテーションをしたことのある人はみな「この作業、AIで自動化できないの？」と感じていることでしょう。事前推論は、その想いの一部を実現する機能です。

## 事前準備
### 機械学習モデルの用意
AI（機械学習）でアノテーションを自動化するためには、まずは機械学習モデルが必要です。
今回は、こちらの記事を参考にして、機械学習モデルもノンプログラミングでデプロイしてしまいます。

（参考）ABEJA Platformのテンプレートを使用して、ノンプログラミングで機械学習モデルをデプロイする
https://qiita.com/yushin_n/items/fb338ca9bd3c685ad691

デプロイされたエンドポイントをクリックして、表示されるURLを控えておきます。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/376869/62985877-9854-6e67-b236-28a7f6881d7c.png" width=80%>

### データの用意
事前推論を行うデータを用意して、ABEJA Platformのデータレイクに保存します。
今回使ったデータはWebクローリングで集めました。

（参考）ABEJA Platform × icrawlerで機械学習用のデータを用意する
https://qiita.com/yushin_n/items/afedf46e14fae984ce4c

### アノテーションのプロジェクトの作成
ABEJA Platformのアノテーションツールにログインして、プロジェクトを新規作成します。
タスクは画像分類を選びます。
カテゴリーとラベルは、モデルを学習した時の教師データの構成と揃えます。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/376869/29244896-6cbf-9ab0-3b8b-25410440ae37.png" width=80%>

先ほどデータを保存したDataLakeのチャンネル名を指定して、データの同期をしておきます。
これで準備完了です。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/376869/91738c4f-4129-1e4d-bae1-b2bf781040cf.png" width=80%>


## 事前推論を使用して、高速で機械学習用のデータを作成する
### 事前推論

準備が完了したら、以下のコードを実行します。

```python:preinference.py

import io
import requests
from urllib.parse import urljoin
from tqdm import tqdm

from abeja.datalake import Client as DatalakeClient

# set deployment url
deployment_url = 'https://abeja-internal.api.abeja.io/deployments/XXXXXXXXXXXXX/services/ser-XXXXXXXXXXXXXXXX'

# set credential
organization_id = 'XXXXXXXXXXXXX'
user_id = 'user-XXXXXXXXXXXXX'
personal_access_token = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'

credential = {
    'user_id': user_id,
    'personal_access_token': personal_access_token
}

ANNOTATION_API = 'https://annotation-tool.abeja.io'

# set id for preinference
ANNOTATION_PROJECT_ID = 'XXXX'
ANNOTATION_ORGANIZATION_ID = 'XXX'

# set credential for preinference
headers = {
    'api-access-user-id': 'XXXX',
    'api-access-token': 'XXXXXXXXXXXX'
}

# get DataLake channel id
organization_url = urljoin(ANNOTATION_API, "/api/v1/organizations/{}/projects/{}".format(ANNOTATION_ORGANIZATION_ID,
                                                                                         ANNOTATION_PROJECT_ID))
res = requests.get(organization_url, headers=headers)
res.raise_for_status()
channel_id = res.json()['data_lake_channels'][0]['channel_id']
client = DatalakeClient(organization_id=organization_id, credential=credential)
datalake_channel = client.get_channel(channel_id)

# get annotation task url
task_url = urljoin(ANNOTATION_API, "/api/v1/organizations/{}/projects/{}/tasks/".format(ANNOTATION_ORGANIZATION_ID,
                                                                                        ANNOTATION_PROJECT_ID))
page = 1

while True:
    res = requests.get(task_url, headers=headers, params={'page': page})
    res.raise_for_status()
    res_body = res.json()

    # check if project has data
    if len(res_body) == 0:
        break

    for task in tqdm(res_body):
        metadata = task['metadata'][0]
        # load file from DataLake channel
        file = datalake_channel.get_file(metadata['file_id'])
        content_type = file.get_file_info()['content_type']
        img_io = io.BytesIO(file.get_content())

        # post image to deployment api for prediction
        res = requests.post(deployment_url, data=img_io, headers={'Content-Type': content_type},
                            auth=(user_id, personal_access_token))
        result = res.json()

        try:
            information = {'flower': result['result'][0]['label']}
            # post predicted result to annotation tool
            preinference_url = urljoin(task_url, "{}/preinferences".format(str(task['id'])))
            res = requests.post(preinference_url, json={'information': information}, headers=headers)
        except KeyError:
            print(result['error'])
            print('Fail to read {}'.format(file.uri))

    page = page + 1
```

※事前推論は現在β版のため、ABEJA Platformとは別にアノテーションツール専用の認証情報が必要です。認証情報の詳細については、担当者にお問合せ下さい。

### アノテーション

処理が完了した後、アノテーションツールのプロジェクトの作業画面を確認します。
モデルによる推論結果のラベルが付与された状態から、作業を開始することができるようになっています！

こちらはマーガレットです。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/376869/f512dc99-33ee-6e4f-9b89-d971d3dc70c8.png" width=80%>

こちらはヒマワリ。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/376869/b5d08605-7d9b-0450-4bf2-fb066ccf1ef0.png" width=80%>

こちらは、珍しい白いタンポポ。白いタンポポは学習データには含まれていなかったため、モデルはマーガレットと予測してます。手動でラベルを付け直して送信します。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/376869/492547ef-a213-136f-ea95-e88b13eb4590.png" width=80%>

クローリングの精度が低かったのか、アサガオが混じっていました。手動でスキップします。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/376869/0adf8f15-fffe-1854-a38d-60943a817007.png" width=80%>

誤認識やイレギュラーケースがあるため、完全な自動化は難しいですが、ゼロからアノテーションするより作業が大分早くなりました！

## まとめ
本記事では、ABEJA Platformの事前推論の機能を使用して、高速で機械学習用の教師データを作成する手順をまとめました。今回は画像分類を試しましたが、ABEJA Platformが対応しているアノテーションのテンプレート（画像物体検出、セグメンテーション、動画のシーンラベリング等）は全て事前推論機能を使うことができます。アノテーションツールのテンプレートの種類については、[公式ドキュメント](https://developers.abeja.io/function/annotation/templates/)を参照ください :dog:

## 参考
ABEJA Platformは、トライアルも提供しています。気になられた方は、是非、お気軽にお問い合わせください。また、フォーラムもありますので、是非、ご活用ください。

ABEJA Platformに関するお問い合わせ
https://abejainc.com/platform/ja/contact/

ABEJA Platform Forum
https://forums.abeja.io/