1
0

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 5 years have passed since last update.

PyTorchのDL ModelをWatson MLで動かす

1
Last updated at Posted at 2021-01-12

はじめに

以下の3つの記事の応用問題として「Google Colabで学習したPyTorchのモデルをWatson ML上で動かす(予測する)」ということを試したところ、比較的簡単にできたので、その手順メモを残します。

  1. Watson Studioでscikit-learn機械学習モデルをWebサービス化する
  2. PyTorch 学習メモ (Karasと同じモデルを作ってみた)
  3. (手順メモ) Watson Studio Jupyterでデータアセットに登録したデータをローカルにコピーする

PyTorchのモデルをWatson MLで動かす場合の前提

PyTorchのモデルをWatson MLで動かす場合は、モデルをONNX形式でexportし、更にgzipで圧縮をかけます。
今回は、このgzファイルをいったんローカルPCにダウンロードし、更にWatson Studioのデータアセットとして登録する方法をとりました。
具体的な手順としては、2. PyTorch 学習メモ (Karasと同じモデルを作ってみた)
のNotebookをGoogle Colab上でそのまま動かし、更にその下に下記のセルを追加して、実行しました。

# ダミーデータの作成
dummy_input = torch.randn((1, 3, 32, 32)).to(device)

# onyx形式でexport
# keep_initializers_as_inputsのオプションが重要でこれがないとエラーになる
torch.onnx.export(net, dummy_input, "cifar10-pytorch-sample.onnx", 
                  keep_initializers_as_inputs=True, verbose=True)

# gz形式に圧縮
!tar czvf cifar10-pytorch-sample.gz cifar10-pytorch-sample.onnx

# 結果をPCにダウンロード
fn = 'cifar10-pytorch-sample.gz'
from google.colab import files
files.download(fn) 

ポイントとして、torch.onnx.export関数呼び出しのときに、keep_initializers_as_inputsオプションを付ける必要がありました。この指定をしないと、モデル呼び出し時にランタイムエラーになってしまいます。

Watson Studio側用Notebook

ここから先の作業はWatson Studio側になります。
デプロイ作業と、予測作業の両方を、以下のNotebookを使って行いました。

URL指定で新規Watson Studio Notebookを作る場合は、下記リンクをコピペして下さい。

https://raw.githubusercontent.com/makaishi2/sample-data/master/notebooks/pytorch%20deploy%20sample.ipynb
読み込んだ直後の状態は下記のようになります。

スクリーンショット 2021-01-12 15.56.58.png

Notbeookを動かす前の準備

以下のことが必要になります。

  1. Watson Studioのインスタンス作成
  2. Watson Studioのプロジェクト作成
  3. アクセストークンが作成済み
  4. デプロイメントスペースが作成済み
  5. PCにダウンロードしたファイル cifar10-pytorch-sample.gzをデータアセットとしてアップロード済み

それぞれの手順については、以下の記事を参考にして下さい。

1.と2.: 無料でなんでも試せる! Watson Studioセットアップガイド
3. : (手順メモ) Watson Studio Jupyterでデータアセットに登録したデータをローカルにコピーする
4. : デプロイメントスペースの作成

5に関しては 「プロジェクトに追加」->「データ」から画面の指示に従い、ファイルをdrag and dropすればいいです。

Notebook解説

あとは、ほとんど上で紹介した3つの記事の復習です。概要のみの説明にとどめ、PyTorch固有の話だけ、詳しめに解説します。

一番上のセル

下記の一番上のセルは、ツールが自動生成したものです。データアセットをローカルにコピーするときに必要になります。
詳しい手順は、(手順メモ) Watson Studio Jupyterでデータアセットに登録したデータをローカルにコピーする を参照して下さい。

# @hidden_cell
# The project token is an authorization token that is used to access project resources like data sources, connections, and used by platform APIs.
from project_lib import Project
project = Project(project_id='xxxx', project_access_token='xxxx')
pc = project.project_context

ローカルのモデルファイルのコピー

以下のコードがデータアセットにあるモデルファイルをローカルにコピーするものです。
解説は、(手順メモ) Watson Studio Jupyterでデータアセットに登録したデータをローカルにコピーする
にありますので、こちらを参照して下さい。

# project-libを使ってデータアセットのファイルをローカルにコピーする

fn = 'cifar10-pytorch-sample.gz'
infile = project.get_file(fn)
with open(fn, 'wb') as local_file:
    local_file.write(infile.read())

1. モデルの保存

この手順は、上で紹介した記事Watson Studioでscikit-learn機械学習モデルをWebサービス化するとほとんど同じ手順です。

差分として考慮が必要な箇所として「1.3 Software Specification ID の取得」がありますが、結論として上の記事と同じ"default_py3.7"を選べばよかったです。

1.4 モデルの保存」のコードは次のようになります。(見やすいようにnotebookから一部修正しています)

model_path = 'cifar10-pytorch-sample.gz'

metadata = {
            client.repository.ModelMetaNames.NAME: 'External pytorch model',
            client.repository.ModelMetaNames.TYPE: 'pytorch-onnx_1.3',
            client.repository.ModelMetaNames.SOFTWARE_SPEC_UID: sofware_spec_uid
}

published_model = client.repository.store_model(
    model=model_path,
    meta_props=metadata)

scikit-learnの時は、store_model関数の引数として、学習済みモデルのインスタンスそのものを引数にした箇所が、今回は、外部ファイル名(実体はONNX形式のモデルを圧縮したもの)が引数になっています。

正常にモデルの登録ができると、下記のようなコマンドに対して、モデルが表示されます。

models_details = client.repository.list_models()
------------------------------------  -------------------------  ------------------------  -----------------
ID                                    NAME                       CREATED                   TYPE
f25e3664-b2d3-476b-94ad-0f17868f82b9  External pytorch model     2021-01-12T06:08:18.002Z  pytorch-onnx_1.3
dfc072c0-3dad-42b0-9951-4827271fd868  Scikit IRIS random forest  2021-01-11T06:32:59.002Z  scikit-learn_0.23
------------------------------------  -------------------------  ------------------------  -----------------

2. モデルのデプロイ

モデルのデプロイもscikit-learnの時とまったく同じです。解説は省略し、コードと実行結果のみ記載します。

コード

metadata = {
    client.deployments.ConfigurationMetaNames.NAME: "Deployment of external pytorch model",
    client.deployments.ConfigurationMetaNames.ONLINE: {}
}

created_deployment = client.deployments.create(published_model_uid, meta_props=metadata)

実行結果

#######################################################################################

Synchronous deployment creation for uid: 'f25e3664-b2d3-476b-94ad-0f17868f82b9' started

#######################################################################################


initializing
ready


------------------------------------------------------------------------------------------------
Successfully finished deployment creation, deployment_uid='d2748d63-7b21-49a1-8533-956b421498ca'
------------------------------------------------------------------------------------------------

コード

# デプロイメントの一覧表示

client.deployments.list()

実行結果

-----------------------------------  ------------------------------------  -----  ------------------------
GUID                                  NAME                                  STATE  CREATED
d2748d63-7b21-49a1-8533-956b421498ca  Deployment of external pytorch model  ready  2021-01-12T06:08:41.907Z
864aec72-15c1-4008-b96a-6ce1c1776af4  Scikit-Learn Iris Model online        ready  2021-01-11T06:46:10.762Z
------------------------------------  ------------------------------------  -----  ------------------------

次のコードは、deployment_uid変数を設定するためのものです。この変数は予測時に必要です。
今は、デプロイをした直後なので、created_deployment変数から取得可能ですが、予測のみを行うコードの場合、この変数が使えません。その場合、上の一覧表示の結果をコピーして、手動で設定することになります。

コード

# depolyment_uid 取得

deployment_uid = client.deployments.get_uid(created_deployment)

# すでにデプロイ済みの場合、下記コメントをはずして手動でdeployment_uidを設定する

# deployment_uid = "d2748d63-7b21-49a1-8533-956b421498ca"

# deployment_uidの確認
print(deployment_uid)

結果

d2748d63-7b21-49a1-8533-956b421498ca

3. 予測

最後のステップは予測です。今は、たまたま同じnotebookにありますが、普通は別の場所から呼ばれることになります。
その場合は、上で説明したdeployment_uidの設定に注意するようにして下さい。

3.1 検証用データの取得

検証用データは、学習時同様、pytorchのライブラリを使って取得することにします。Watson StudioのJupyterでは、pytorchvisionが含まれていないので、追加導入する必要があります。ただ、この時にバージョンに注意しないと、import時にエラーが起きるようです。
下記のコードが動作確認されたものとなります。

!pip install torchvision==0.8.1 | tail -n 1
import torch
import torchvision
import torchvision.transforms as transforms

念のため、導入済みのバージョンを確認してみます。

print(torch.__version__)
print(torchvision.__version__)
1.7.0
0.8.1

それでは、実際にデータを読み込んでみましょう。以下のコードはPyTorch 学習メモ (Karasと同じモデルを作ってみた)とまったく同じものなので、細かい解説は省略します。

# 分類先クラス名
classes = ['plane', 'car', 'bird', 'cat',
           'deer', 'dog', 'frog', 'horse', 'ship', 'truck']

# 分類クラス数
num_classes = len(classes)

# 1回の学習で何枚の画像を使うか
batch_size = 128

transform = transforms.Compose(
    [transforms.ToTensor(),
     transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])

trainset = torchvision.datasets.CIFAR10(root='./data', train=True,
                                        download=True, transform=transform)
train_loader = torch.utils.data.DataLoader(trainset, batch_size=batch_size, shuffle=True, num_workers=2)

testset = torchvision.datasets.CIFAR10(root='./data', train=False,download=True, transform=transform)
test_loader = torch.utils.data.DataLoader(testset, batch_size=batch_size, shuffle=False, num_workers=2)

3.2 検証用データの組み立てと、イメージ表示

次の読み込んだデータから、検証用の入力データ100件分を組み立てます。
ポイントは、元のデータ形式である、LataLoaderクラスのインスタンスから、ループを回しながら、「入力データ」と「正解ラベル」のセットを取り出し、その結果をリストでつないで、2つの変数valueslabelsを作っている点です。
入力データに関しては、元々はTensor形式だったものを、numpy -> list形式に変換します。
また、この処理をしている最中にせっかくなので、読み込んだ入力データをイメージ表示もしています。
具体的なコードは以下のようになります。

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from IPython.display import display

N = 100

values = []
labels = []

plt.figure(figsize=(15, 15))

for i in range(N):
    # テスト用データの組み立て
    image, label = testset[i]
    labels.append(label)
    xdata = image.numpy().tolist()
    values .append(xdata)
    
    # データの画面表示 (予測とは無関係。ついでにやっただけ)
    ax = plt.subplot(10, 10, i + 1)
    img = np.transpose(image.numpy(), (1, 2, 0))
    img2 = (img + 1)/2 
    plt.imshow(img2)
    ax.set_title(classes[label], fontsize=10)
    ax.get_xaxis().set_visible(False)
    ax.get_yaxis().set_visible(False)

plt.show()

イメージはこんな形で出力されるはずです。(下記は一部)

スクリーンショット 2021-01-12 16.43.51.png

最後に、今、組み立てた変数valuesを使って、最終的にAPI呼び出しの引数となる変数scoring_payloadを組み立てます。この書式も、scikit-learnの時とまったく同じです。

# 入力用パラメータの組み立て
scoring_payload = {"input_data": [{"values": values}]}

3.3 予測用APIの呼び出し

これで、予測のための準備はすべて終わりました。あとは、ライブラリを使ってAPI呼び出しをします。
具体的なコードは下記になります。

predictions = client.deployments.score(deployment_uid, scoring_payload)

予測結果はPyTorchの場合、下のように、各モデルの確信度の配列になります。

print(predictions)
{'predictions': [{'values': [[-4.940670013427734, -4.821472644805908, -3.138746500015259, 4.834586143493652, -8.361305236816406, 3.5139641761779785, 0.46214526891708374, -5.3571882247924805, -2.8474788665771484, -5.455841541290283], [2.7644705772399902, 2.6016273498535156, -15.107336044311523, -17.771255493164062, -18.063720703125, -23.072168350219727, -12.652151107788086, -24.18853759765625, 16.923925399780273, -3.93799090385437], [3.2324273586273193, 0.6664918661117554, -9.696178436279297, -7.741365432739258, -7.4524688720703125, -11.031821250915527, -8.739614486694336, -9.531129837036133, 7.229529857635498, -0.4289969205856323], (以下略)

np.argmax関数を使って、どの要素が最大化を調べることで、予測クラスがわかります。具体的なコードは次のとおりです。(比較のため、正解ラベルも表示してみました)

w1 = predictions['predictions'][0]['values']
w2 = np.array([np.argmax(x) for x in w1])
print('予測: ',w2)
print('正解: ', np.array(labels))
予測:  [3 8 8 0 6 6 1 4 3 9 0 9 5 7 9 6 5 7 8 6 7 0 4 9 4 4 4 0 9 6 6 5 4 5 9 8 4
 9 9 5 4 6 5 6 0 9 3 9 7 4 9 8 7 3 8 8 5 3 3 5 7 5 6 3 6 2 1 2 3 7 0 6 8 8
 0 2 0 3 3 8 8 1 1 7 2 5 2 4 8 9 0 6 8 6 4 6 6 0 0 7]
正解:  [3 8 8 0 6 6 1 6 3 1 0 9 5 7 9 8 5 7 8 6 7 0 4 9 5 2 4 0 9 6 6 5 4 5 9 2 4
 1 9 5 4 6 5 6 0 9 3 9 7 6 9 8 0 3 8 8 7 7 4 6 7 3 6 3 6 2 1 2 3 7 2 6 8 8
 0 2 9 3 3 8 8 1 1 7 2 5 2 7 8 9 0 3 8 6 4 6 6 0 0 7]

ぱっと見、8割程度で正解が得られていそうなことがわかります。
最後に混同行列表示もしてみます。
ここは、コードでなく結果のみ載せることにします。

スクリーンショット 2021-01-12 16.56.06.png

対角線のマス目(=正解)に多くの件数が集まっていて、そこそこの精度が出ていることがわかりました。

参考記事

Use PyTorch to predict handwritten digits

1
0
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
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?