すべてのプロダクト
Search
ドキュメントセンター

Platform For AI:PAI-Bladeを使用したTensorFlow BERTモデルの最適化

最終更新日:Aug 15, 2024

トランスフォーマーからの双方向エンコーダ表現 (BERT) は、事前にトレーニングされた言語表現モデルです。 BERTモデルは、さまざまな自然言語処理 (NLP) タスクで最高の推論パフォーマンスを達成し、近年のNLP分野でのブレークスルーです。 ただし、BERTモデルには多数のパラメータと膨大な量のコンピューティングワークロードが含まれます。 したがって、実際の生産シナリオでは、BERTモデルの最適化が非常に必要です。 このトピックでは、Machine Learning Platform for AI (PAI)-Bladeを使用して、TensorFlowによってトレーニングされたBERTモデルを最適化する方法について説明します。

制限事項

このトピックの手順で使用する環境は、次のバージョン要件を満たす必要があります。

  • システム環境: LinuxでのPython 3.6以降とCompute Unified Device Architecture (CUDA) 10.0

  • フレームワーク: TensorFlow 1.15

  • 推論最適化ツール: PAI-Blade V3.16.0以降

手順

PAI-Bladeを使用してBERTモデルを最適化するには、次の手順を実行します。

  1. ステップ1: 準備

    最適化するモデルをダウンロードし、tokenizersライブラリを使用してテストデータを準備します。

  2. ステップ2: PAI-Bladeを使用してモデルを最適化

    blade.optimizeメソッドを呼び出してモデルを最適化し、最適化されたモデルを保存します。

  3. ステップ3: モデルのパフォーマンスと精度を確認する

    元のモデルと最適化モデルの推論速度と推論結果をテストして、生成された最適化レポートの情報を検証します。

  4. ステップ4: 最適化モデルのロードと実行

    PAI-Blade SDKを統合して、推論用に最適化されたモデルをロードします。

ステップ1: 準備をする

  1. 次のコマンドを実行して、トークナイザーライブラリをインストールします。

    pip3 install tokenizers
  2. 次のコマンドを実行してモデルをダウンロードし、指定したディレクトリにモデルを解凍します。

    wget http://pai-blade.oss-cn-zhangjiakou.aliyuncs.com/tutorials/bert_example/nlu_general_news_classification_base.tar.gz
    mkdir nlu_general_news_classification_base
    tar zxvf nlu_general_news_classification_base.tar.gz -C nlu_general_news_classification_base
  3. TensorFlowが提供するsaved_model_cliコマンドを実行して、モデルの基本情報を表示します。

    saved_model_cli show --dir nlu_general_news_classification_base --all

    次の出力が返されます。

    tag-set: 'serve' を持つ

    MetaGraphDef with tag-set: 'serve' contains the following SignatureDefs:
    
    signature_def['serving_default']:
      The given SavedModel SignatureDef contains the following input(s):
        inputs['input_ids'] tensor_info:
            dtype: DT_INT32
            shape: (-1, -1)
            name: input_ids:0
        inputs['input_mask'] tensor_info:
            dtype: DT_INT32
            shape: (-1, -1)
            name: input_mask:0
        inputs['segment_ids'] tensor_info:
            dtype: DT_INT32
            shape: (-1, -1)
            name: segment_ids:0
      The given SavedModel SignatureDef contains the following output(s):
        outputs['logits'] tensor_info:
            dtype: DT_FLOAT
            shape: (-1, 28)
            name: app/ez_dense/BiasAdd:0
        outputs['predictions'] tensor_info:
            dtype: DT_INT32
            shape: (-1)
            name: ArgMax:0
        outputs['probabilities'] tensor_info:
            dtype: DT_FLOAT
            shape: (-1, 28)
            name: Softmax:0
      Method name is: tensorflow/serving/predict

    前の出力では、ニュース分類モデルは、3つの入力テンソル、すなわち、input_ids:0input_mask:0、およびsegment_ids:0と、3つの出力テンソル、すなわち、logitspredictions、およびprobabilitiesとを有する。predictions の下にネストされているArgMax:0は、ニュースが属するカテゴリを示します。

  4. tokenizersライブラリを呼び出して、テストデータを準備します。

    from tokenizers import BertWordPieceTokenizer
    
    # Initialize a tokenizer by using the vocab.txt file in the model directory. 
    tokenizer = BertWordPieceTokenizer('./nlu_general_news_classification_base/vocab.txt')
    
    # Group four pieces of news into a batch for encoding. 
    news = [
        'Declaration of a national public health emergency in Mexico due to over 1,000 confirmed cases. Chinanews.com reported on March 31 that the number of confirmed COVID-19 cases in Mexico had exceeded 1,000. On March 30, the Mexican government declared a national public health emergency. Solutions were launched to curb the spread of COVID-19. ',
        'Data released by the National Bureau of Statistics showed that the Manufacturing Purchasing Managers' Index (PMI) for China was 50.1% in August, 0.3 percentage points lower than that in July but still higher than the critical point. ',
        'Reported on August 31, China standard time, in the final round of group stage matches of men's blind football at Tokyo 2020 Paralympic Games, China beat hosts Japan 2-0 and advanced to the semi-final match. China's football player Zhu Ruiming scored a brace in this match. ',
        'By August 30, the Zhurong rover has been traveling on the surface of Mars for 100 days. In the past 100 days, it has traveled a total of 1,064 meters in the south from its landing spot. It carries six scientific instruments and has obtained around 10 GB of raw scientific data. ',
    ]
    tokenized = tokenizer.encode_batch(news)
    
    # Expand the sequence to 128 characters in length. 
    def pad(seq, seq_len, padding_val):
        return seq + [padding_val] * (seq_len - len(seq))
    
    input_ids = [pad(tok.ids, 128, 0) for tok in tokenized]
    segment_ids = [pad(tok.type_ids, 128, 0) for tok in tokenized]
    input_mask = [ pad([1] * len(tok.ids), 128, 0) for tok in tokenized ]
    
    # The final test data is in the format of TensorFlow feed_dict arguments. 
    test_data = {
        "input_ids:0": input_ids,
        "segment_ids:0": segment_ids,
        "input_mask:0": input_mask,
    }
  5. モデルをロードし、テストデータに対して推論を実行します。

    import tensorflow.compat.v1 as tf
    import json
    
    # Load the label mapping file and obtain the category names that correspond to the output integers. 
    with open('./nlu_general_news_classification_base/label_mapping.json') as f:
        MAPPING = {v: k for k, v in json.load(f).items()}
    
    # Load and run the model. 
    cfg = tf.ConfigProto()
    cfg.gpu_options.allow_growth = True
    with tf.Session(config=cfg) as sess:
        tf.saved_model.loader.load(sess, ['serve'], './nlu_general_news_classification_base')
        result = sess.run('ArgMax:0', test_data)
        print([MAPPING[r] for r in result])

    次の推論結果が期待どおりに返されます。

    ['International', 'Finance', 'Sports', 'Science']

ステップ2: PAI-Bladeを使用してモデルを最適化する

  1. blade.optimizeモデルを最適化するメソッドを呼び出します。 次のサンプルコードに例を示します。 詳細については、「Pythonメソッド」をご参照ください。

    import blade
    
    saved_model_dir = 'nlu_general_news_classification_base'
    optimized_model, _, report = blade.optimize(
        saved_model_dir,       # The path of the model file. 
        'o1',                  # Lossless optimization. 
        device_type='gpu',     # Optimization for GPU devices. 
        test_data=[test_data]  # The test data. 
    )

    モデルを最適化するときは、次の項目に注意してください。

    • blade.optimizeメソッドの最初の戻り値は、最適化されたモデルを示します。 データ型は元のモデルと同じままです。 この例では、SavedModelファイルのパスが入力で指定され、最適化されたSavedModelファイルのパスが返されます。

    • inputsおよびoutputsパラメーターを設定する必要はありません。 PAI-Bladeは、入力ノードと出力ノードを自動的に推測します。

  2. 最適化の完了後に最適化レポートを表示します。

    print("Report: {}".format(report))

    次のサンプルコードは、最適化レポートの例を示しています。

    Report: {
      "software_context": [
        {
          "software": "tensorflow",
          "version": "1.15.0"
        },
        {
          "software": "cuda",
          "version": "10.0.0"
        }
      ],
      "hardware_context": {
        "device_type": "gpu",
        "microarchitecture": "T4"
      },
      "user_config": "",
      "diagnosis": {
        "model": "nlu_general_news_classification_base",
        "test_data_source": "user provided",
        "shape_variation": "dynamic",
        "message": "",
        "test_data_info": "input_ids:0 shape: (4, 128) data type: int64\nsegment_ids:0 shape: (4, 128) data type: int64\ninput_mask:0 shape: (4, 128) data type: int64"
      },
      "optimizations": [
        {
          "name": "TfStripUnusedNodes",
          "status": "effective",
          "speedup": "na",
          "pre_run": "na",
          "post_run": "na"
        },
        {
          "name": "TfStripDebugOps",
          "status": "effective",
          "speedup": "na",
          "pre_run": "na",
          "post_run": "na"
        },
        {
          "name": "TfAutoMixedPrecisionGpu",
          "status": "effective",
          "speedup": "1.46",
          "pre_run": "35.04 ms",
          "post_run": "24.02 ms"
        },
        {
          "name": "TfAicompilerGpu",
          "status": "effective",
          "speedup": "2.43",
          "pre_run": "23.99 ms",
          "post_run": "9.87 ms"
        }
      ],
      "overall": {
        "baseline": "35.01 ms",
        "optimized": "9.90 ms",
        "speedup": "3.54"
      },
      "model_info": {
        "input_format": "saved_model"
      },
      "compatibility_list": [
        {
          "device_type": "gpu",
          "microarchitecture": "T4"
        }
      ],
      "model_sdk": {}
    }

    上記の最適化レポートは、2つの最適化項目、すなわちTfAutoMixedPrecisionGpuおよびTfAicompilerGpuが有効になることを示している。 モデルの推論時間は、35 msから9.9 msに3.54倍短縮されます。 上記の最適化レポートは参考用です。 モデルの実際の最適化効果が優勢です。 最適化レポートのフィールドの詳細については、「最適化レポート」をご参照ください。

  3. 最適化モデルのパスを表示します。

    print("Optimized model: {}".format(optimized_model))

    次のような情報が表示されます。

    Optimized model: /root/nlu_general_news_classification_base_blade_opt_20210901141823/nlu_general_news_classification_base

    出力には、最適化モデルの新しいパスが表示されます。

ステップ3: モデルの性能と精度を確認する

最適化が完了したら、Pythonスクリプトを実行して最適化レポートの情報を確認できます。

  1. benchmarkメソッドを定義し、モデルを10回ウォームアップしてから、モデルを1,000回実行して、推論速度を示すモデルの平均推論時間を取得します。

    import time
    
    def benchmark(model, test_data):
        tf.reset_default_graph()
        with tf.Session() as sess:
            sess.graph.as_default()
            tf.saved_model.loader.load(sess, ['serve'], model)
            # Warmup!
            for i in range(0, 10):
                result = sess.run('ArgMax:0', test_data)
            # Benchmark!
            num_runs = 1000
            start = time.time()
            for i in range(0, num_runs):
                result = sess.run('ArgMax:0', test_data)
            elapsed = time.time() - start
            rt_ms = elapsed / num_runs * 1000.0
            # Show the result!
            print("Latency of model: {:.2f} ms.".format(rt_ms))
            print("Predict result: {}".format([MAPPING[r] for r in result]))
  2. benchmarkメソッドを呼び出して、元のモデルを検証します。

    benchmark('nlu_general_news_classification_base', test_data)

    次のような情報が表示されます。

    モデルの

    Latency of model: 36.20 ms.
    Predict result: ['International', 'Finance', 'Sports', 'Science']

    出力は、元のモデルの推論時間が36.20ミリ秒であることを示しています。 この値は基本的に「35.01 ms」と一致します。これは、最適化レポートの「全体」フィールドの下にネストされた「ベースライン」の値です。 予測結果 ['International', 'Finance', 'Sports', 'Science'] は予想通りである。 前の出力の推論時間は参照用です。 モデルの実際の推論時間が優先されます。

  3. benchmarkメソッドを呼び出して、最適化されたモデルを検証します。

    import os
    os.environ['TAO_COMPILATION_MODE_ASYNC'] = '0'
    
    benchmark(optimized_model, test_data)

    前述の最適化レポートは、AICompilerがモデルに最適化効果をもたらすことを示しています。 ただし、AICompilerは非同期に動作し、コンパイル中の推論に元のモデルを使用できます。 検証の精度を確保するには、benchmarkメソッドを呼び出す前に、環境変数TAO_COMPILATION_MODE_ASYNCを0に設定して、コンパイルモードを同期に変更する必要があります。

    次のような情報が表示されます。

    Latency of model: 9.87 ms.
    Predict result: ['International', 'Finance', 'Sports', 'Science']

    出力は、最適化モデルの推論時間が9.87ミリ秒であることを示しています。 この値は基本的に「9.90 ms」と一致します。これは、最適化レポートの「全体」フィールドの下にネストされた「最適化」の値です。 予測結果 ['International', 'Finance', 'Sports', 'Science'] は予想通りである。 前の出力の推論時間は参照用です。 モデルの実際の推論時間が優先されます。

ステップ4: 最適化されたモデルをロードして実行する

検証が完了したら、最適化されたモデルをデプロイできます。 PAI-Bladeは、統合できるPython用のSDKとC ++ 用のSDKを提供しています。 SDK For C ++ の使用方法の詳細については、「SDKを使用して推論用のTensorFlowモデルをデプロイする」をご参照ください。 次のセクションでは、SDK for Pythonを使用してモデルをデプロイする方法について説明します。

  1. オプション: 試用期間中に、次の環境変数設定を追加して、認証の失敗によるプログラムの予期しない停止を防止します。

    export BLADE_AUTH_USE_COUNTING=1
  2. PAI-Bladeを使用するように認証されます。

    export BLADE_REGION=<region>
    export BLADE_TOKEN=<token>

    ビジネス要件に基づいて、次のパラメーターを設定します。

    • <region>: PAI-Bladeを使用するリージョンです。 PAI-BladeユーザーのDingTalkグループに参加して、PAI-Bladeを使用できるリージョンを取得できます。 DingTalkグループのQRコードについては、「アクセストークンの取得」をご参照ください。

    • <token>: PAI-Bladeを使用するために必要な認証トークン。 PAI-BladeユーザーのDingTalkグループに参加して、認証トークンを取得できます。 DingTalkグループのQRコードについては、「アクセストークンの取得」をご参照ください。

  3. 最適化されたモデルをロードして実行します。

    推論コードにimport blade.ru ntime.torchを追加します。 これに加えて、PAI-Blade SDKを統合するための追加のコードを記述したり、元の推論コードを変更したりする必要はありません。

    import tensorflow.compat.v1 as tf
    import blade.runtime.tensorflow
    # Replace <your_optimized_model_path> with the path of the optimized model. 
    savedmodel_dir = <your_optimized_model_path>
    # Replace <your_infer_data> with the data on which you want to perform inference. 
    infer_data = <your_infer_data>
    
    with tf.Session() as sess:
        sess.graph.as_default()
        tf.saved_model.loader.load(sess, ['serve'], savedmodel_dir)
        result = sess.run('ArgMax:0', infer_data)