TadaoYamaokaの日記

山岡忠夫Homeで公開しているプログラムの開発ネタを中心に書いていきます。

AWS inf1 インスタンスで推論を行う

dlshogiをAWS inf1 インスタンスで動かせないか試している。
AWS inf1 インスタンスは、推論に特化したAWS Inferentia チップが搭載されている。

現在のdlshogiは、TensorRTを使用して推論を行っているため、NvidiaGPUで動かすことが前提になっている。
onnxruntime版も用意しているが、推論速度は7.2倍ほど差がある。

dlshogiをAWSで動かそうとすると、NvidiaGPUが搭載されたインスタンスを借りる必要がある。
しかし、NvidiaGPUが搭載されたAWSインスタンスの料金は高い。
AWS inf1 インスタンスは、NvidiaGPUインスタンスに比べるとリーズナブルである。

料金

推論向けのT4が搭載されたg4dnインスタンスと比較すると、バージニア北部で、以下の通りである。

インスタンス vCPU GPU メモリ(GB) スポットインスタンス料金
g4dn.2xlarge 8 1 32 $0.2256 /1 時間
g4dn.12xlarge 48 4 192 $1.1761 /1 時間
inf1.2xlarge 8 1 16 $0.1086 /1 時間
inf1.6xlarge 24 4 48 $0.354 /1 時間

CPU、メモリが同じインスタンスがないが比較的近いスペックだと、同じGPU数で価格は半分以下になる。

推論性能

推論性能は、カタログスペックで、以下の通りである。

T4 65 TFLOP(FP16)
Inferentia 128 TOPS

ただし、dlshogiで実際に性能がでるかは測ってみないとわからない。


ということで、まずはPythonで、dlshogiのモデルの推論ができるか試してみた。

概要

Inferentiaを使用するには、neuron SDKを使用する必要がある。
neuron SDKは、TensorFlowやPyTorchをカスタマイズしたパッケージとして提供される。
Deep Learming AMIを使用すると、フレームワーク別のconda環境が用意されている。
ドキュメント参照:
Compile with Framework API and Deploy on EC2 Inf1 — AWS Neuron documentation

PyTorchで学習したモデルは、直接は利用できないため、Inferentia向けに変換を行う必要がある。
残念ながらモデルの形式は、onnxには対応していない。
onnxに変換前のモデルが必要である(dlshogi with GCTのモデルはonnxでしか公開していないので各自学習したモデルを使用する必要がある)。

なお、onnxからPyTorchに変換するツールを試したが、変換後の推論結果が一致せずうまくいかなかった。

手順

変換

モデルの変換は、inf1インスタンスでなくても実行できる。
c4.xlargeで、Deep Learming AMI(Ubuntu18)を使用して行った。

  • ログイン後、
source activate aws_neuron_pytorch_p36

を実行し、PyTorchのneuron SDKの環境に切り替える。

  • dlshogiのモデルをアップロードする。
  • dlshogiのソースをgit cloneして、pip install -e .でパッケージをインストールする。
  • 以下のスクリプトでモデルを変換する(ネットワークタイプとファイル名は書き換える)。
import torch
import torch_neuron

import torch.nn as nn
from dlshogi.common import *
from dlshogi.network.policy_value_network import policy_value_network
from dlshogi import serializers
from dlshogi import cppshogi

model = policy_value_network('resnet15_swish', add_sigmoid=False)
serializers.load_npz('model-pre5_resnet15_swish_b4096lr004-008', model, False)

class PolicyValueNetworkAddSigmoid(nn.Module):
    def __init__(self, model):
        super(PolicyValueNetworkAddSigmoid, self).__init__()
        self.model = model
        
    def forward(self, x1, x2):
        y1, y2 = self.model(x1, x2)
        return y1, torch.sigmoid(y2)

model = PolicyValueNetworkAddSigmoid(model)
model.eval()

def mini_batch(hcpevec):
    features1 = np.empty((len(hcpevec), FEATURES1_NUM, 9, 9), dtype=np.float32)
    features2 = np.empty((len(hcpevec), FEATURES2_NUM, 9, 9), dtype=np.float32)
    move = np.empty((len(hcpevec)), dtype=np.int64)
    result = np.empty((len(hcpevec)), dtype=np.float32)
    value = np.empty((len(hcpevec)), dtype=np.float32)

    cppshogi.hcpe_decode_with_value(hcpevec, features1, features2, move, result, value)

    z = result.astype(np.float32) - value + 0.5

    return (torch.tensor(features1),
            torch.tensor(features2),
            torch.tensor(move.astype(np.int64)),
            torch.tensor(result.reshape((len(hcpevec), 1))),
            torch.tensor(z),
            torch.tensor(value.reshape((len(value), 1)))
            )

batchsize = 1
hcpevec = np.array([([ 88, 164,  73,  33,  12, 215,  87,  33, 126, 142,  77,  33,  44, 175,  66, 120,  20, 194, 171,  16, 158,  77,  33,  44, 215,  95,  33,  62, 142,  73,  33,  12], 0, 7739, 1, 0)] * batchsize, HuffmanCodedPosAndEval)
x1, x2, t1, t2, z, value = mini_batch(hcpevec)

model_neuron = torch.neuron.trace(model, example_inputs=[x1, x2], dynamic_batch_size=True)

model_neuron.save('model-pre5_resnet15_swish_b4096lr004-008.neuron.pt')
推論

inf1.xlargeをスポットインスタンスで借りて試した。
※スポットインスタンスのvCPUの上限がデフォルト0になっていたので、制限解除の申請が必要だったがすぐに許可された。

  • ログイン後、
source activate aws_neuron_pytorch_p36

を実行し、PyTorchのneuron SDKの環境に切り替える。

  • 変換したモデルをアップロードする。
import torch
import torch_neuron

import torch.nn as nn
from dlshogi.common import *
from dlshogi import cppshogi

model = torch.jit.load('model-pre5_resnet15_swish_b4096lr004-008.neuron.pt')
model.eval()

def mini_batch(hcpevec):
    features1 = np.empty((len(hcpevec), FEATURES1_NUM, 9, 9), dtype=np.float32)
    features2 = np.empty((len(hcpevec), FEATURES2_NUM, 9, 9), dtype=np.float32)
    move = np.empty((len(hcpevec)), dtype=np.int64)
    result = np.empty((len(hcpevec)), dtype=np.float32)
    value = np.empty((len(hcpevec)), dtype=np.float32)

    cppshogi.hcpe_decode_with_value(hcpevec, features1, features2, move, result, value)

    z = result.astype(np.float32) - value + 0.5

    return (torch.tensor(features1),
            torch.tensor(features2),
            torch.tensor(move.astype(np.int64)),
            torch.tensor(result.reshape((len(hcpevec), 1))),
            torch.tensor(z),
            torch.tensor(value.reshape((len(value), 1)))
            )


batchsize = 128
hcpevec = np.array([([ 88, 164,  73,  33,  12, 215,  87,  33, 126, 142,  77,  33,  44, 175,  66, 120,  20, 194, 171,  16, 158,  77,  33,  44, 215,  95,  33,  62, 142,  73,  33,  12], 0, 7739, 1, 0)] * batchsize, HuffmanCodedPosAndEval)
x1, x2, t1, t2, z, value = mini_batch(hcpevec)

print('start')
for i in range(10000):
    y1, y2 = model(x1, x2)
  • 別のコンソールから、/opt/aws/neuron/bin/neuron-topを実行することで、Inferentiaが使用されていることを確認できる。
neuron-top - 12:53:58
Models: 1 loaded, 1 running. NeuronCores: 1 used.
0000:00:1f.0 Utilizations: NC0 66.96%, NC1 0.00%, NC2 0.00%, NC3 0.00%,
Model ID   Device    NeuronCore%   Device Mem   Host Mem   Model Name
10016      nd0:nc0   66.96           29 MB       151 KB    1.5.5.0+3cc38c60b-/tmp/tmpmwpges2q

まとめ

AWS inf1 インスタンスで、dlshogiのモデルの推論ができることが確認できた。
C++から推論するには、LibTorchを使用して推論する必要があるため、dlshogiの推論部分のソース修正が必要になるため、別途試したい。
推論速度もC++で試せるようになったら測定する予定である。