TVM動かしてみた
TensorRT5を動かしたときからだいぶ時間が経ってしまったけど、今回はTVMを動かしてみた。
公式ドキュメントがそれなりに充実してるけど、あまり日本語の情報はないみたいなので参考になれば。
コードはこちら。
TVMの公式ドキュメントはこちら
環境
動作させた環境は以下で、一通りインストールはできている前提。
OS: Ubuntu16.04
Python: 3.8 (非推奨 今のところ世の中的には3.7までにしておいたほうが無難らしい)
GPU: GTX 1080 Ti
CUDA: 10.2
cuDNN: 7.6.5
TVM インストール
公式ドキュメントを参考にソースからビルドを進める。
とりあえず動かしたいだけならソースからのビルドではなくDockerのほうがラクそう。
何を有効にするかでビルドの前準備が結構大変になる。
手元で有効にしたのは以下
CUDA、LLVM、MKL BLAS、MKL DNN、NNPACK、CUDNN、DNNL CODEGEN、ANTLR
BLASはopen blasで良さそうだし、MKL-DNNとかNNPACKとかが実際に動いてくれてるかはよくわからない。 DNNL CODEGENとかANTLRは今回の範囲ではなくても問題なさそう(AutoTVMとかで必要になる?)。
Pythonパッケージインストール
今回のコードを動かすのに必要なPythonパッケージのインストール
$pip install -r requirements.txt
余談だけど、Python3.8向けにはまだPyPIにTensorFlowのオフィシャルパッケージが存在しない。 世の中的にはまだ3.8向けの正式サポートはもう少し先らしい。 今回はちょこちょこ問題潰しつつbazelでビルドしました。
モデルの準備
KerasのMobilenetV1学習済みモデルをダウンロード。
実行
$python run_tvm.py cat.jpg . . . Classification Result: 1 tiger cat 0.439913 2 tabby 0.434570 3 Egyptian cat 0.104559 4 lynx 0.011487 5 tiger 0.003490 Evaluate inference time cost... Inference time 0: 0.990710 Inference time 1: 0.999410 Inference time 2: 0.995719 Inference time 3: 0.984265 Inference time 4: 0.985593 Inference time 5: 0.986964 Inference time 6: 0.991390 Inference time 7: 0.992035 Inference time 8: 1.000641 Inference time 9: 1.001794 Mean inference time (std dev): 0.992852 ms (0.006003 ms)
Kerasモデルからrelayを使ってモデル変換して、引数で与えた画像に対する推論を10回実行している。 だいたい 1 msec / image = 1000 fps。 CUDA、cuDNNのバージョンは違えどほとんど前回のTensorRTと差がない。
コードは以下。
今回も基本的にはいくつかのサンプルコードの寄せ集めみたいな感じ。
あとがき
build_module
のあたりでかなり大量にCannot find config ...
といったメッセージが出てくる。
Cannot find config for target=cuda -model=1080ti, workload=('conv2d', (1, 3, 225, 225, 'float32'), (32, 3, 3, 3, 'float32'), (2, 2), (0, 0, 0, 0), (1, 1), 'NCHW', 'float32'). A fallback configuration is used, which may bring great performance regression. Cannot find config for target=cuda -model=1080ti, workload=('conv2d', (1, 32, 112, 112, 'float32'), (64, 32, 1, 1, 'float32'), (1, 1), (0, 0, 0, 0), (1, 1), 'NCHW', 'float32'). A fallback configuration is used, which may bring great performance regression. Cannot find config for target=cuda -model=1080ti, workload=('conv2d', (1, 64, 56, 56, 'float32'), (128, 64, 1, 1, 'float32'), (1, 1), (0, 0, 0, 0), (1, 1), 'NCHW', 'float32'). A fallback configuration is used, which may bring great performance regression. Cannot find config for target=cuda -model=1080ti, workload=('conv2d', (1, 128, 56, 56, 'float32'), (128, 128, 1, 1, 'float32'), (1, 1), (0, 0, 0, 0), (1, 1), 'NCHW', 'float32'). A fallback configuration is used, which may bring great performance regression. Cannot find config for target=cuda -model=1080ti, workload=('conv2d', (1, 128, 28, 28, 'float32'), (256, 128, 1, 1, 'float32'), (1, 1), (0, 0, 0, 0), (1, 1), 'NCHW', 'float32'). A fallback configuration is used, which may bring great performance regression. Cannot find config for target=cuda -model=1080ti, workload=('conv2d', (1, 256, 28, 28, 'float32'), (256, 256, 1, 1, 'float32'), (1, 1), (0, 0, 0, 0), (1, 1), 'NCHW', 'float32'). A fallback configuration is used, which may bring great performance regression. Cannot find config for target=cuda -model=1080ti, workload=('conv2d', (1, 256, 14, 14, 'float32'), (512, 256, 1, 1, 'float32'), (1, 1), (0, 0, 0, 0), (1, 1), 'NCHW', 'float32'). A fallback configuration is used, which may bring great performance regression. Cannot find config for target=cuda -model=1080ti, workload=('conv2d', (1, 512, 14, 14, 'float32'), (512, 512, 1, 1, 'float32'), (1, 1), (0, 0, 0, 0), (1, 1), 'NCHW', 'float32'). A fallback configuration is used, which may bring great performance regression. Cannot find config for target=cuda -model=1080ti, workload=('conv2d', (1, 512, 7, 7, 'float32'), (1024, 512, 1, 1, 'float32'), (1, 1), (0, 0, 0, 0), (1, 1), 'NCHW', 'float32'). A fallback configuration is used, which may bring great performance regression. Cannot find config for target=cuda -model=1080ti, workload=('conv2d', (1, 1024, 7, 7, 'float32'), (1024, 1024, 1, 1, 'float32'), (1, 1), (0, 0, 0, 0), (1, 1), 'NCHW', 'float32'). A fallback configuration is used, which may bring great performance regression. Cannot find config for target=cuda -model=1080ti, workload=('conv2d', (1, 1024, 1, 1, 'float32'), (1000, 1024, 1, 1, 'float32'), (1, 1), (0, 0, 0, 0), (1, 1), 'NCHW', 'float32'). A fallback configuration is used, which may bring great performance regression. Cannot find config for target=cuda -model=1080ti, workload=('depthwise_conv2d_nchw', (1, 1024, 7, 7, 'float32'), (1024, 1, 3, 3, 'float32'), (1, 1), (1, 1, 1, 1), (1, 1), 'float32'). A fallback configuration is used, which may bring great performance regression. Cannot find config for target=cuda -model=1080ti, workload=('depthwise_conv2d_nchw', (1, 512, 15, 15, 'float32'), (512, 1, 3, 3, 'float32'), (2, 2), (0, 0, 0, 0), (1, 1), 'float32'). A fallback configuration is used, which may bring great performance regression. Cannot find config for target=cuda -model=1080ti, workload=('depthwise_conv2d_nchw', (1, 512, 14, 14, 'float32'), (512, 1, 3, 3, 'float32'), (1, 1), (1, 1, 1, 1), (1, 1), 'float32'). A fallback configuration is used, which may bring great performance regression. Cannot find config for target=cuda -model=1080ti, workload=('depthwise_conv2d_nchw', (1, 256, 29, 29, 'float32'), (256, 1, 3, 3, 'float32'), (2, 2), (0, 0, 0, 0), (1, 1), 'float32'). A fallback configuration is used, which may bring great performance regression. Cannot find config for target=cuda -model=1080ti, workload=('depthwise_conv2d_nchw', (1, 256, 28, 28, 'float32'), (256, 1, 3, 3, 'float32'), (1, 1), (1, 1, 1, 1), (1, 1), 'float32'). A fallback configuration is used, which may bring great performance regression. Cannot find config for target=cuda -model=1080ti, workload=('depthwise_conv2d_nchw', (1, 128, 57, 57, 'float32'), (128, 1, 3, 3, 'float32'), (2, 2), (0, 0, 0, 0), (1, 1), 'float32'). A fallback configuration is used, which may bring great performance regression. Cannot find config for target=cuda -model=1080ti, workload=('depthwise_conv2d_nchw', (1, 128, 56, 56, 'float32'), (128, 1, 3, 3, 'float32'), (1, 1), (1, 1, 1, 1), (1, 1), 'float32'). A fallback configuration is used, which may bring great performance regression. Cannot find config for target=cuda -model=1080ti, workload=('depthwise_conv2d_nchw', (1, 64, 113, 113, 'float32'), (64, 1, 3, 3, 'float32'), (2, 2), (0, 0, 0, 0), (1, 1), 'float32'). A fallback configuration is used, which may bring great performance regression. Cannot find config for target=cuda -model=1080ti, workload=('depthwise_conv2d_nchw', (1, 32, 112, 112, 'float32'), (32, 1, 3, 3, 'float32'), (1, 1), (1, 1, 1, 1), (1, 1), 'float32'). A fallback configuration is used, which may bring great performance regression.
fallback configurationを使うのでパフォーマンス低下があるかもということなので、この辺りはAutoTVMを使うと改善されるかもしれない。 それなりの数のfallbackが出てるのでAutoTVMにはちょっと期待。 次回はAutoTVMでのチューニングを試してみる予定。
TensorRT5がリリースされたので試してみた
タイトルの通り正式版としてTensorRT 5.0.2.6が出たということでPython APIを試してみた。
やったことはTensorFlowのモデルを変換して動かしてみるところまで。
公式ドキュメントを参考に進めたけど情報が盛りだくさんでちょっとわかりにくかったので改めてまとめてみた。
コードはこちら。
TensorRTのPythonインタフェースに関する公式ドキュメントはこちら。
環境
動作させた環境は以下で、一通りインストールはできている前提。
OS: Ubuntu16.04
Python: 3.5
GPU: GTX 1080 Ti
CUDA: 10.0
cuDNN: 7.3.0
TensorRT インストール
公式ドキュメントを参考に以下からダウンロード。 docs.nvidia.com
Ubuntuに関しては14.04から18.04まで、14.04と16.04はCUDA9.0と10.0をそれぞれサポート。18.04はCUDA10.0だけ。
システムにインストールしてもよければdebパッケージのほう、自分で管理したければtarのほうを選ぶ。
自分はPython関連はvirtualenvで管理したかったのでtarのほうのwhlをpipで、それ以外はdebパッケージから入れた。
Pythonパッケージインストール
今回のコードを動かすのに必要なPythonパッケージのインストール
$pip install Pillow pycuda numpy
モデルの準備
MobilenetV1の学習済みモデルをTensorFlow modelsからダウンロード。
モデルの変換
Pythonパッケージがインストールできていればconvert-to-uff
というコマンドが使えるようになっているので、以下の通りuff形式に変換する。
$convert-to-uff mobilenet_v1_1.0_224_frozen.pb -o mobilenet_v1_1.0_224.uff
実行
$python run_tensorrt.py cat.jpg build engine... allocate buffers load input Inference time 0: 0.8375644683837891 [msec] Inference time 1: 0.7922649383544922 [msec] Inference time 2: 0.8244514465332031 [msec] Inference time 3: 0.7870197296142578 [msec] Inference time 4: 0.8032321929931641 [msec] Inference time 5: 0.7877349853515625 [msec] Inference time 6: 0.8432865142822266 [msec] Inference time 7: 0.8103847503662109 [msec] Inference time 8: 0.8285045623779297 [msec] Inference time 9: 0.8194446563720703 [msec] Classification Result: 1 tiger cat 0.45609813928604126 2 cougar 0.2968028783798218 3 Persian cat 0.24455446004867554 4 leopard 0.00040596097824163735 5 cheetah 0.00032873026793822646
変換したuffモデルを読み込んで引数で与えた画像に対する推論を10回実行している。 だいたい 0.8 msec / image = 1250 fps。 画像のロードとかGPUへのデータ転送は測定に入れてないにしても速い。
コードは以下。
基本的にはサンプルコードそのままで、時間測定のために非同期での実行をやめたくらい。
あとがき
DTYPE
のところをfloat16
にして試してみたけど、1080Tiは非対応ということで警告が出て速度も速くはならなかった。
int8
はこのモデルのままではダメで、int8
に量子化したTensofFlowのモデルをuffにすればできるかもしれない。この辺りはまた後でやってみたい。
あと、認識結果がTensorFlowと比べて悪くなってる気がするので、まだどこかおかしいかもしれない。
TensorFlowがchannel lastだけどTensorRTがchannel firstなところが気になって、register_input
のorderを変えたけど特に変化はなく、入力のtransposeを外すとさらに変な結果になってしまった。
完全におかしいわけじゃないので、前処理が違うとかかもしれない。
知っている人がいたら教えてほしい。
TwitterのApplication-only authenticationを試してみた
Application-only authentication
今さらだけどTwitterのAPIバージョンが1.1になってユーザー認証しないとAPI叩けないのかなとか思ってたら、ちょっと前に新しい認証方式が出てたみたいなので試してみたというお話。
タイトルにもあるようにApplication-only authenticationっていう方式でOAuth2.0にもとづいたものらしい。従来のOAuth1.0を使った認証と比べるとだいぶ簡単で、API回数制限も検索なんかはユーザーごとのものよりもかなり緩和されてる。その代わりにユーザー認証が必要なユーザーのタイムラインとかDMみたいなAPIが使えないっていう制限はある。
立ち位置的にはこれまでOAuthなしで使えてたAPIの置き換えみたいなものかと。
もうちょっとちゃんとしたまとめ
Perlで実装
このPerlでの実装を参考にしてこのページの訳的な感じで認証方法を簡単にまとめてみた。具体的な手順は以下。
まずはここでアプリケーションの登録をしてコンシューマーキーとシークレットをもらってくる。で、この2つをURIエンコードした文字列を":"でつないだものをBase64エンコードしてあげる。
Pythonで書くとこんな感じ。
token_credential = urllib.quote(oauth_consumer_key) + ':' + urllib.quote(oauth_consumer_secret)
credential = base64.b64encode(token_credential)
アクセストークン取得
次にこの文字列を使ってアクセストークンを取得してくる。リクエストのAuthorizationヘッダに"Basic " + さっきの文字列を突っ込んで、Content-Typeに"application/x-www-form-urlencoded;charset=UTF-8"を入れる。リクエストボディには"grant-type=client_credentials"を入れておく。
で、https://api.twitter.com/oauth2/tokenにPOSTでリクエストを送るとアクセストークンの入ったJSONが返ってくる。通常のOAuthと比べると特別なライブラリもなしでアクセストークンを取得できるのがわかるかと。
url = 'https://api.twitter.com/oauth2/token' value = {'grant_type': 'client_credentials'} data = urllib.urlencode(value) req = urllib2.Request(url) req.add_header('Authorization', 'Basic ' + credential) req.add_header('Content-Type', 'application/x-www-form-urlencoded;charset=UTF-8') response = urllib2.urlopen(req, data) json_response = json.loads(response.read()) access_token = json_response['access_token']
署名付きリクエスト
後はこのアクセストークンを使って、リクエストのAuthorizationヘッダに"Bearer " + アクセストークンを付けてAPIアクセスをするだけ。なんて簡単(´∀`)
とりあえず検索APIを叩いてみる。
url = 'https://api.twitter.com/1.1/search/tweets.json' query = {'q': 'japan'} req = urllib2.Request(url+'?'+urllib.urlencode(query)) req.add_header('Authorization', 'Bearer ' + access_token) response = urllib2.urlopen(req) json_response = json.loads(response.read()) json_str = json.dumps(json_response)
最後にまとめ。
とりあえず検索APIの回数制限が450回らしいので451回アクセスしてみるスクリプト。
ちょうどヨルダン戦やってるのでjapanを検索してみたり。
大量に結果が吐き出されます。
回数制限を超えるとこんな感じでエラーが返ってくる。
$python test_app_only_auth.py ... {"errors":[{"message":"Rate limit exceeded","code":88}]} 450
検索とかタイムラインからのデータ取得とかするならこれからはこれ使うことになるかと。
せっかくなんでこれ使ってなんかやってみようかな。