PyTorch 1.5 レシピ : 量子化, テキスト, モデル最適化 : 動的量子化

PyTorch 1.5 レシピ : 量子化, テキスト, モデル最適化 : 動的量子化 (翻訳/解説)
翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 05/18/2020 (1.5.0)

* 本ページは、PyTorch 1.5 Recipes の以下のページを翻訳した上で適宜、補足説明したものです:

* サンプルコードの動作確認はしておりますが、必要な場合には適宜、追加改変しています。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。

 

無料セミナー開催中 クラスキャット主催 人工知能 & ビジネス Web セミナー

人工知能とビジネスをテーマにウェビナー (WEB セミナー) を定期的に開催しています。スケジュールは弊社 公式 Web サイト でご確認頂けます。
  • お住まいの地域に関係なく Web ブラウザからご参加頂けます。事前登録 が必要ですのでご注意ください。
  • Windows PC のブラウザからご参加が可能です。スマートデバイスもご利用可能です。

お問合せ : 本件に関するお問い合わせ先は下記までお願いいたします。

株式会社クラスキャット セールス・マーケティング本部 セールス・インフォメーション
E-Mail:sales-info@classcat.com ; WebSite: https://www.classcat.com/
Facebook: https://www.facebook.com/ClassCatJP/

 

量子化, テキスト, モデル最適化 : 動的量子化

このレシピでは LSTM-スタイルのリカレント・ニューラルネットワーク上の推論を高速化するために動的量子化をどのように利用するかを見ます。これはモデル重みのサイズを削減してモデル実行をスピードアップします。

 

イントロダクション

ニューラルネットワークを設計するとき作られる幾つかのトレードオフがあります。モデル開発と訓練の間にリカレント・ニューラルネットワークの層の数とパラメータの数を変更できてそしてモデルサイズ and/or モデルレイテンシー or スループットに対して精度をトレードオフできます。そのような変更は多くの時間と計算リソースを取る可能性があります、何故ならばモデル訓練に渡り反復しているからです。量子化は訓練が完了した後の既知のモデルのパフォーマンスとモデル精度の間の類似のトレードオフを作る方法を貴方に与えます。

単一セッションでそれを試すことができてそしてモデルサイズを確実に本質的に削減して多くの精度を失うことなく本質的なレイテンシー削減を得られるかもしれません。

 

動的量子化とは何でしょう?

ネットワークの量子化はそれを重み and/or 活性化について減 (= reduced) 精度整数表現を使用するように変換することを意味します。これはモデルサイズを節約して CPU や GPU 上でより高いスループットの数学演算の利用を可能にします。

浮動小数点から整数値に変換するとき本質的には浮動小数点値にあるスケール因子を乗算して結果を整数に丸めています。様々な量子化アプローチはスケール因子を決定するアプローチする方法の点で異なります。

ここで説明される動的量子化に伴う主要アイデアは活性化のためのスケール因子を実行時に観測されるデータ範囲に基づいて動的に決定することです。これはスケール因子が各観測データセットについて出来る限り信号が保存されるように「調整」されることを確実にします。

その一方でモデルパラメータはモデル変換の間に知られます、そしてそれらは前もって変換されて INT8 形式にストアされます。

量子化モデルの算術はベクトル化 INT8 命令を使用して成されます。accumulation はオーバーフローを回避するために典型的には INT16 か INT32 で行なわれます。次の層が量子化されるか出力のために FP32 に変換される場合、この高精度値は INT8 にスケールバックされます。

動的量子化は (LSTM モデルを配備に向けて変換する標準的なパートとしての) 製品パイプラインに追加されるためにそれが上手く適合されるようにするパラメータ調整からは比較的自由です、

Note: ここで取られるアプローチ上の制限

このレシピは PyTorch における動的量子化の特徴とそれを利用するためのワークフローへの素早いイントロダクションを提供します。焦点はモデルを変換するために使用される特定の関数を説明することにあります。簡潔さと明瞭さのために幾つかの単純化を行ないます。

 

  1. 最小限の LSTM ネットワークから始めます。
  2. ランダム隠れ状態でネットワークを単純に初期化します。
  3. ランダム入力でネットワークをテストします。
  4. このチュートリアルではネットワークを訓練はしません。
  5. ネットワークの量子化形式が (そこから始めた) 浮動小数点ネットワークよりも小さくて高速に動作することを見ます。
  6. 出力値が FP32 ネットワークの出力と一般に同じレベルであることを見ますが、実際の訓練ネットワーク上で期待される精度損失をここでは実演していません。

動的量子化がどのように成されるかを見てそしてメモリ使用とレイテンシー時間の示唆に富む削減を見ることができるでしょう。訓練された LSTM 上でモデル精度の高レベルを保存できるテクニックの実演の提供はより進んだチュートリアルに委ねます。そのより厳密な処理に直ちに移動することを望むのであれば、進んだ動的量子化チュートリアル に進んでください。

 

ステップ

このレシピは 5 ステップを持ちます。

  1. セットアップ – ここでは非常に単純な LSTM を定義し、モジュールをインポートし、そして幾つかのランダム入力 tensor を設置します。
  2. 量子化を行なう – ここでは浮動小数点モデルをインスタンス化してからそれの量子化バージョンを作成します。
  3. モデルサイズを見る – ここではモデルサイズが小さくなったことを示します。
  4. レイテンシーを見る – ここでは 2 つのモデルを実行してモデル実行時間 (レイテンシー) を比較します。
  5. 精度を見る – ここでは 2 つのモデルを実行して出力を比較します。

 

1: セットアップ

これはレシピの残りのためのセットアップをする少しの簡単なコードです。

ここでインポートする独特なモジュールは torch.quantization でこれは PyTorch の量子化演算子と変換関数を含みます。非常に単純な LSTM モデルも定義して幾つかの入力をセットアップします。

# import the modules used here in this recipe
import torch
import torch.quantization
import torch.nn as nn
import copy
import os
import time

# define a very, very simple LSTM for demonstration purposes
# in this case, we are wrapping nn.LSTM, one layer, no pre or post processing
# inspired by
# https://pytorch.org/tutorials/beginner/nlp/sequence_models_tutorial.html, by Robert Guthrie
# and https://pytorch.org/tutorials/advanced/dynamic_quantization_tutorial.html
class lstm_for_demonstration(nn.Module):
  """Elementary Long Short Term Memory style model which simply wraps nn.LSTM
     Not to be used for anything other than demonstration.
  """
  def __init__(self,in_dim,out_dim,depth):
     super(lstm_for_demonstration,self).__init__()
     self.lstm = nn.LSTM(in_dim,out_dim,depth)

  def forward(self,inputs,hidden):
     out,hidden = self.lstm(inputs,hidden)
     return out, hidden


torch.manual_seed(29592)  # set the seed for reproducibility

#shape parameters
model_dimension=8
sequence_length=20
batch_size=1
lstm_depth=1

# random data for input
inputs = torch.randn(sequence_length,batch_size,model_dimension)
# hidden is actually is a tuple of the initial hidden state and the initial cell state
hidden = (torch.randn(lstm_depth,batch_size,model_dimension), torch.randn(lstm_depth,batch_size,model_dimension))

 

2: 量子化を行なう

今は面白いパートに取り掛かります。最初に float_lstm と呼称されるモデルのインスタンスを作成してからそれを量子化します。

ここでは以下の関数を使用します (ドキュメント参照) :

torch.quantization.quantize_dynamic()

これはモデル、それから (それらが出現する場合量子化したかった) サブモジュールのリスト、それからターゲットにしているデータ型を取ります。この関数は元のモデルの量子化バージョンを新しいモジュールとして返します。

それらがそれが取る総てです。

 # here is our floating point instance
float_lstm = lstm_for_demonstration(model_dimension, model_dimension,lstm_depth)

# this is the call that does the work
quantized_lstm = torch.quantization.quantize_dynamic(
    float_lstm, {nn.LSTM, nn.Linear}, dtype=torch.qint8
)

# show the changes that were made
print('Here is the floating point version of this module:')
print(float_lstm)
print('')
print('and now the quantized version:')
print(quantized_lstm)

 

3: モデルサイズを見る

オーケー、そして私達はモデルを量子化しました。それは私達に何をもたらすのでしょう?そうですね、最初の恩恵は FP32 モデルパラメータを INT8 値 (そして幾つかの記録されたスケール因子) で置き換えたことです。これはストアして移すために約 75% 未満のデータであることを意味します。デフォルト値で下で示される削減は 75% 未満ですが、上のモデルサイズを増やせば (例えばモデル次元を 80 のような何かに設定できます) これはパラメータ値により更に支配されたストアされたモデルサイズとして 4x 小さい方向に収斂します。

def print_size_of_model(model, label=""):
    torch.save(model.state_dict(), "temp.p")
    size=os.path.getsize("temp.p")
    print("model: ",label,' \t','Size (KB):', size/1e3)
    os.remove('temp.p')
    return size

# compare the sizes
f=print_size_of_model(float_lstm,"fp32")
q=print_size_of_model(quantized_lstm,"int8")
print("{0:.2f} times smaller".format(f/q))

# note that this value is wrong in PyTorch 1.4 due to https://github.com/pytorch/pytorch/issues/31468
# this will be fixed in 1.5 with https://github.com/pytorch/pytorch/pull/31540

 

4: レイテンシーを見る

2 番目の恩恵は量子化モデルは典型的にはより高速に動作することです。これは少なくとも以下を含む効果の組み合わせによります :

  1. パラメータデータを移動するに費やされる少ない時間
  2. より高速な INT8 演算

見るように超単純なネットワークの量子化バージョンはより高速に動作します。これはより複雑なネットワークでも一般に真ですが、”your mileage may vary” と言われるように、モデルの構造と実行しているハードウェアを含む幾つかの因子に依拠します。

# compare the performance
print("Floating point FP32")
# %timeit float_lstm.forward(inputs, hidden)

print("Quantized INT8")
# %timeit quantized_lstm.forward(inputs,hidden)

 

5: 精度を見る

ここで精度を注意深く見ていきます、何故ならば正しく訓練されたものではなくランダムに初期化されたネットワークで作業しているからです。けれども、量子化ネットワークが元のものと “in the same ballpark” (同じレベル) にある出力 tensor を生成することを素早く示すことは価値があると考えます。

より詳細な分析についてはこのレシピの最後で参照されているより進んだチュートリアルを見てください。

# run the float model
out1, hidden1 = float_lstm(inputs, hidden)
mag1 = torch.mean(abs(out1)).item()
print('mean absolute value of output tensor values in the FP32 model is {0:.5f} '.format(mag1))

# run the quantized model
out2, hidden2 = quantized_lstm(inputs, hidden)
mag2 = torch.mean(abs(out2)).item()
print('mean absolute value of output tensor values in the INT8 model is {0:.5f}'.format(mag2))

# compare them
mag3 = torch.mean(abs(out1-out2)).item()
print('mean absolute value of the difference between the output tensors is {0:.5f} or {1:.2f} percent'.format(mag3,mag3/mag1*100))

 

Learn More

動的量子化とは何か、それはどのような恩恵をもたらすかを説明し、そして単純な LSTM モデルを素早く量子化するために torch.quantization.quantize_dynamic() 関数を使用しました。

 

追加のリソース

 
以上