OpenVINO を使用した LLM 指示追従パイプライン

この Jupyter ノートブックは、ローカルへのインストール後にのみ起動できます。

GitHub

LLM は “Large Language Model” の略で、受け取った入力に基づいて人間のようにテキストを理解して、生成するように設計された人工知能モデルの一種です。LLM は、大規模なテキスト・データセットでトレーニングされ、パターン、文法、意味関係を学習し、一貫した文脈に関連した応答を生成できるようにします。大規模言語モデル (LLM) の中核となる機能の 1 つは、自然言語の指示に従うことです。指示従うモデルは、プロンプトに応じてテキストを生成でき、執筆支援、チャットボット、コンテンツ生成などのタスクによく使用されます。

このチュートリアルでは、一般的な LLM と OpenVINO を使用して、指示追従テキスト生成パイプラインを実行する方法を検討します。Hugging Face Transformers ライブラリーの事前トレーニング済みモデルを使用します。ユーザー・エクスペリエンスを簡素化するために、Hugging Face Optimum Intel ライブラリーを使用してモデルを OpenVINO™ IR 形式に変換します。

このチュートリアルは次のステップで構成されます。

  • 前提条件のインストール

  • OpenVINO と Hugging Face Optimum の統合を使用して、パブリックソースからモデルをダウンロードして変換

  • OpenVINO NNCF を使用してモデルの重みを INT8 および INT4 に圧縮

  • 指示追従推論パイプラインを作成

  • 指示追従パイプラインを実行

目次

必要条件

%pip uninstall -q -y openvino openvino-dev openvino-nightly optimum optimum-intel
%pip install -q openvino-nightly "nncf>=2.7" "transformers>=4.36.0" onnx "optimum>=1.16.1" "accelerate" "datasets" gradio "git+https://github.com/huggingface/optimum-intel.git" --extra-index-url https://download.pytorch.org/whl/cpu

推論用のモデルを選択

このチュートリアルではさまざまなモデルがサポートされており、提供されたオプションから 1 つを選択して、オープンソース LLM ソリューションの品質を比較できます。

注: 一部のモデルの変換には、ユーザーによる追加アクションが必要になる場合があり、変換には少なくとも 64GB RAM が必要です。

利用可能なオプションには以下があります。

  • tiny-llama-1b-chat - これは、TinyLlama/TinyLlama-1.1B-intermediate-step-1431k-3T をベースに微調整されたチャットモデルです。TinyLlama プロジェクトは、Llama 2 と同じアーキテクチャーとトークナイザーを採用し、3 兆個のトークンで 11 億個の Llama モデルを事前トレーニングすることを目的としています。これは、TinyLlama を、Llama 上に構築された多くのオープンソース・プロジェクトにプラグインできることを意味します。さらに、TinyLlama はコンパクトで、パラメーターは 1.1B のみです。このコンパクトさにより、制限された計算量とメモリー使用量を要求する多くのアプリケーションに対応できます。モデルの詳細については、モデルカードをご覧ください。

  • phi-2 - Phi-2 は 27 億のパラメーターを持つトランスフォーマーです。これは、Phi-1.5 と同じデータソースを使用してトレーニングされ、さまざまな NLP 合成テキストとフィルター処理されたウェブサイト (安全性と教育的価値のため) で構成される新しいデータソースで強化されました。常識、言語理解、論理的推論をテストするベンチマークに対して評価したところ、Phi-2 は 130 億未満のパラメーターを持つモデルの中でほぼ最高のパフォーマンスを示しました。モデルの詳細については、モデルカードをご覧ください。

  • dolly-v2-3b - Dolly 2.0 は、商用利用が許可されている Databricks マシンラーニング・プラットフォームでトレーニングされた指示追従 LLM です。Pythia は、ブレインストーミング、分類、クローズド QA、生成、情報抽出、オープン QA、要約など、さまざまな機能ドメインで Databricks の従業員によって生成された約 15,000 の指示/応答の微調整レコードに基づいてトレーニングされています。Dolly 2.0 は、自然言語命令を処理し、指定された指示に追従する応答を生成することによって機能します。クローズド質疑応答、要約、生成など幅広い用途に使用できます。モデルの詳細については、モデルカードをご覧ください。

  • red-pajama-3b-instruct - GPT-NEOX アーキテクチャーに基づいた 2.8B パラメーターの事前トレーニング済み言語モデル。モデルは、HELM コアシナリオと重複するタスクを除外して、GPT-JT データの数ショット・アプリケーション向けに微調整されました。モデルの詳細については、モデルカードを参照してください。

  • mistral-7b - Mistral-7B-v0.2 大規模言語モデル (LLM) は、70 億のパラメーターを備えた事前トレーニング済みの生成テキストモデルです。モデルの詳細については、モデルカード論文およびリリースに関するブログの投稿を参照してください。

from config import SUPPORTED_LLM_MODELS
import ipywidgets as widgets
model_ids = list(SUPPORTED_LLM_MODELS)

model_id = widgets.Dropdown(
    options=model_ids,
    value=model_ids[1],
    description="Model:",
    disabled=False,
)

model_id
Dropdown(description='Model:', index=1, options=('tiny-llama-1b', 'phi-2', 'dolly-v2-3b', 'red-pajama-instruct…
model_configuration = SUPPORTED_LLM_MODELS[model_id.value]
print(f"Selected model {model_id.value}")
Selected model phi-2

Optimum Intel を使用してモデルをインスタンス化

Optimum Intel を使用すると、Hugging Face Hub から最適化されたモデルをロードし、Hugging Face API を使用して OpenVINO ランタイムで推論を実行するパイプラインを作成できます。Optimum 推論モデルは、Hugging Face Transformers モデルと API の互換性があります。つまり、AutoModelForXxx クラスを対応する OVModelForXxx クラスに置き換えるだけで済みます。

以下は RedPajama モデルの例です。

-from transformers import AutoModelForCausalLM
+from optimum.intel.openvino import OVModelForCausalLM
from transformers import AutoTokenizer, pipeline

model_id = "togethercomputer/RedPajama-INCITE-Chat-3B-v1"
-model = AutoModelForCausalLM.from_pretrained(model_id)
+model = OVModelForCausalLM.from_pretrained(model_id, export=True)

モデルクラスの初期化は、from_pretrained メソッドの呼び出しから始まります。Transformers モデルをダウンロードして変換する場合は、パラメーター export=True を追加する必要があります。save_pretrained メソッドを使用して、変換されたモデルを次回に使用するため保存できます。トークナイザー・クラスとパイプライン API は Optimum モデルと互換性があります。

生成プロセスを最適化し、メモリーをより効率的に使用するには、use_cache=True オプションを有効にします。出力側は自動回帰であるため、出力トークンの非表示状態は、その後の生成ステップごとに計算されると同じままになります。したがって、新しいトークンを生成するたびに再計算するのは無駄であるように思えます。キャッシュを使用すると、モデルは計算後に非表示の状態を保存します。モデルは各タイムステップで最後に生成された出力トークンのみを計算し、保存された出力トークンを非表示のトークンに再利用します。これにより、変圧器モデルの生成の複雑さが \(O(n^3)\) to \(O(n^2)\) に軽減されます。仕組みの詳細については、この記事を参照してください。このオプションを使用すると、モデルは前のステップの非表示状態 (キャッシュされたアテンション・キーと値) を入力として取得し、さらに現在のステップの非表示状態を出力として提供します。これは、次のすべての反復では、前のステップから取得した新しいトークンと、次のトークン予測を取得するためのキャッシュされたキー値のみを提供するだけで十分であることを意味します。

モデルの重みを圧縮

重み圧縮アルゴリズムは、モデルの重みを圧縮することを目的としており、大規模言語モデル (LLM) など、重みのサイズがアクティベーションのサイズよりも相対的に大きい大規模モデルのモデル・フットプリントとパフォーマンスを最適化するために使用できます。INT8 圧縮と比較して、INT4 圧縮はパフォーマンスをさらに向上させますが、予測品質は若干低下します。

Optimum Intel を使用した重み圧縮

Optimum Intel の OVQuantizer クラスでサポートされるモデルの NNCF を介した重み圧縮を有効にするには、OVModelForCausalLM モデルに使用する必要があります。OVQuantizer.quantize(save_directory=save_dir, weights_only=True) 重みの圧縮を有効にします。このアプローチの使用例は、llm-chatbot ノートブックにあります。

NNCF を使用した重み圧縮

NNCF を直接使用して、OpenVINO モデルの重み圧縮を実行することもできます。nncf.compress_weights 関数は、OpenVINO モデル・インスタンスを受け入れ、線形レイヤーと埋め込みレイヤーの重みを圧縮します。このノートブックでは、int4 圧縮と int8 圧縮の両方のバリアントを検討します。

注: このチュートリアルには、FP16 および INT4/INT8 重み圧縮シナリオの変換モデルが含まれます。最初の実行ではメモリーを消費し時間がかかる可能性があります。以下で圧縮精度を手動で制御できます。

注: dGPU 上の INT4/INT8 圧縮モデルではスピードアップされない可能性があります。

from IPython.display import display

prepare_int4_model = widgets.Checkbox(
    value=True,
    description="Prepare INT4 model",
    disabled=False,
)
prepare_int8_model = widgets.Checkbox(
    value=False,
    description="Prepare INT8 model",
    disabled=False,
)
prepare_fp16_model = widgets.Checkbox(
    value=False,
    description="Prepare FP16 model",
    disabled=False,
)

display(prepare_int4_model)
display(prepare_int8_model)
display(prepare_fp16_model)
Checkbox(value=True, description='Prepare INT4 model')
Checkbox(value=False, description='Prepare INT8 model')
Checkbox(value=False, description='Prepare FP16 model')
from pathlib import Path
import shutil
import logging
import openvino as ov
import nncf
from optimum.intel.openvino import OVModelForCausalLM
from optimum.utils import NormalizedTextConfig, NormalizedConfigManager
import gc

NormalizedConfigManager._conf['phi'] = NormalizedTextConfig

nncf.set_log_level(logging.ERROR)

pt_model_id = model_configuration["model_id"]
fp16_model_dir = Path(model_id.value) / "FP16"
int8_model_dir = Path(model_id.value) / "INT8_compressed_weights"
int4_model_dir = Path(model_id.value) / "INT4_compressed_weights"

core = ov.Core()

def convert_to_fp16():
    if (fp16_model_dir / "openvino_model.xml").exists():
        return
    ov_model = OVModelForCausalLM.from_pretrained(pt_model_id, export=True, compile=False, load_in_8bit=False)
    ov_model.half()
    ov_model.save_pretrained(fp16_model_dir)
    del ov_model
    gc.collect()


def convert_to_int8():
    if (int8_model_dir / "openvino_model.xml").exists():
        return
    int8_model_dir.mkdir(parents=True, exist_ok=True)
    if fp16_model_dir.exists():
        model = core.read_model(fp16_model_dir / "openvino_model.xml")
        shutil.copy(fp16_model_dir / "config.json", int8_model_dir / "config.json")
    else:
        ov_model = OVModelForCausalLM.from_pretrained(pt_model_id, export=True, compile=False, load_in_8bit=False)
        ov_model.half()
        ov_model.config.save_pretrained(int8_model_dir)
        model = ov_model._original_model
        del ov_model
        gc.collect()

    compressed_model = nncf.compress_weights(model)
    ov.save_model(compressed_model, int8_model_dir / "openvino_model.xml")
    del ov_model
    del compressed_model
    gc.collect()


def convert_to_int4():
    compression_configs = {
        "mistral-7b": {
            "mode": nncf.CompressWeightsMode.INT4_SYM,
            "group_size": 64,
            "ratio": 0.6,
        },
        'red-pajama-3b-instruct': {
            "mode": nncf.CompressWeightsMode.INT4_ASYM,
            "group_size": 128,
            "ratio": 0.5,
        },
        "dolly-v2-3b": {"mode": nncf.CompressWeightsMode.INT4_ASYM, "group_size": 32, "ratio": 0.5},
        "default": {
            "mode": nncf.CompressWeightsMode.INT4_ASYM,
            "group_size": 128,
            "ratio": 0.8,
        },
    }

    model_compression_params = compression_configs.get(
        model_id.value, compression_configs["default"]
    )
    if (int4_model_dir / "openvino_model.xml").exists():
        return
    int4_model_dir.mkdir(parents=True, exist_ok=True)
    if not fp16_model_dir.exists():
        model = OVModelForCausalLM.from_pretrained(pt_model_id, export=True, compile=False, load_in_8bit=False).half()
        model.config.save_pretrained(int4_model_dir)
        ov_model = model._original_model
        del model
        gc.collect()
    else:
        ov_model = core.read_model(fp16_model_dir / "openvino_model.xml")
        shutil.copy(fp16_model_dir / "config.json", int4_model_dir / "config.json")
    compressed_model = nncf.compress_weights(ov_model, **model_compression_params)
    ov.save_model(compressed_model, int4_model_dir / "openvino_model.xml")
    del ov_model
    del compressed_model
    gc.collect()


if prepare_fp16_model.value:
    convert_to_fp16()
if prepare_int8_model.value:
    convert_to_int8()
if prepare_int4_model.value:
    convert_to_int4()
INFO:nncf:NNCF initialized successfully. Supported frameworks detected: torch, onnx, openvino
/home/ea/work/genai_env/lib/python3.8/site-packages/torch/cuda/__init__.py:138: UserWarning: CUDA initialization: The NVIDIA driver on your system is too old (found version 11080). Please update your GPU driver by downloading and installing a new version from the URL: http://www.nvidia.com/Download/index.aspx Alternatively, go to: https://pytorch.org to install a PyTorch version that has been compiled with your version of the CUDA driver. (Triggered internally at ../c10/cuda/CUDAFunctions.cpp:108.)
  return torch._C._cuda_getDeviceCount() > 0
No CUDA runtime is found, using CUDA_HOME='/usr/local/cuda'

さまざまな圧縮タイプのモデルサイズを比較してみましょう。

fp16_weights = fp16_model_dir / "openvino_model.bin"
int8_weights = int8_model_dir / "openvino_model.bin"
int4_weights = int4_model_dir / "openvino_model.bin"

if fp16_weights.exists():
    print(f"Size of FP16 model is {fp16_weights.stat().st_size / 1024 / 1024:.2f} MB")
for precision, compressed_weights in zip([8, 4], [int8_weights, int4_weights]):
    if compressed_weights.exists():
        print(
            f"Size of model with INT{precision} compressed weights is {compressed_weights.stat().st_size / 1024 / 1024:.2f} MB"
        )
    if compressed_weights.exists() and fp16_weights.exists():
        print(
            f"Compression rate for INT{precision} model: {fp16_weights.stat().st_size / compressed_weights.stat().st_size:.3f}"
        )
Size of model with INT4 compressed weights is 1734.02 MB

推論用のデバイスとモデルバリアントを選択

注: dGPU 上の INT4/INT8 圧縮モデルではスピードアップされない可能性があります。

core = ov.Core()
device = widgets.Dropdown(
    options=core.available_devices + ["AUTO"],
    value="CPU",
    description="Device:",
    disabled=False,
)

device
Dropdown(description='Device:', options=('CPU', 'GPU.0', 'GPU.1', 'AUTO'), value='CPU')
available_models = []
if int4_model_dir.exists():
    available_models.append("INT4")
if int8_model_dir.exists():
    available_models.append("INT8")
if fp16_model_dir.exists():
    available_models.append("FP16")

model_to_run = widgets.Dropdown(
    options=available_models,
    value=available_models[0],
    description="Model to run:",
    disabled=False,
)

model_to_run
Dropdown(description='Model to run:', options=('INT4',), value='INT4')
from transformers import AutoTokenizer

if model_to_run.value == "INT4":
    model_dir = int4_model_dir
elif model_to_run.value == "INT8":
    model_dir = int8_model_dir
else:
    model_dir = fp16_model_dir
print(f"Loading model from {model_dir}")

model_name = model_configuration["model_id"]
ov_config = {"PERFORMANCE_HINT": "LATENCY", "NUM_STREAMS": "1", "CACHE_DIR": ""}

tok = AutoTokenizer.from_pretrained(model_name)

ov_model = OVModelForCausalLM.from_pretrained(
    model_dir,
    device=device.value,
    ov_config=ov_config,
)
Loading model from phi-2/INT4_compressed_weights
Special tokens have been added in the vocabulary, make sure the associated word embeddings are fine-tuned or trained.
Compiling the model to CPU ...

指示追従推論パイプラインを作成

run_generation 関数は、ユーザー指定のテキスト入力を受け入れ、それをトークン化して、生成プロセスを実行します。テキスト生成は反復プロセスであり、トークンの最大数または生成停止条件に達するまで、次の各トークンは以前に生成されたものに依存します。生成が完了するまで待たずに中間生成結果を取得するには、Hugging Face ストリーミング API の一部として提供される TextIteratorStreamer を使用します。

以下の図は、指示追従パイプラインがどのように動作するかを示しています。

generation pipeline)

生成パイプライン

最初の反復では、ユーザーはトークナイザーを使用してトークン ID に変換された指示を提供し、その後、モデルに提供される準備された入力を提供しました。モデルは、すべてのトークンの確率をロジット形式で生成します。予測された確率に基づいて次のトークンが選択される方法は、選択されたデコード方法によって決まります。最も一般的なデコード方法の詳細については、このブログをご覧ください。

テキスト生成の品質を制御できるパラメーターがいくつかあります。

  • Temperature は、AI が生成したテキストの創造性のレベルを制御するパラメーターです。temperature を調整することで、AI モデルの確率分布に影響を与え、テキストの焦点を絞ったり、多様にしたりできます。
    次の例を考えてみましょう。AI モデルは次のトークンの確率で “猫は ____ です” という文を完成させる必要があります。
    遊んでいる: 0.5
    眠っている: 0.25
    食べている: 0.15
    ドライブしている: 0.05
    飛んでいる: 0.05
    • 低温 (例: 0.2): AI モデルはより集中的かつ決定的になり、最も高い確率のトークン (“遊んでいる”) を選択します。

    • 中温 (例: 1.0): AI モデルは創造性と集中力のバランスを維持し、大きな偏りのない確率に基づいてトークン (“遊んでいる”、“眠っている”、“食べている”など) を選択します。

    • 高温 (例: 2.0): AI モデルはより冒険的になり、可能性の低いトークン (“ドライブしている” や “飛んでいる”など) を選択する可能性が高くなります。

  • Top-p、核サンプリングとも呼ばれる累積確率に基づいて AI モデルによって考慮される、トークンの範囲を制御するために使用されるパラメーターです。top-p 値を調整することで、AI モデルのトークン選択に影響を与え、焦点を絞ったり、多様性を持たせることができます。猫と同じ例を使用して、次の top_p 設定を検討してください。

    • 低 top_p (例: 0.5): AI モデルは、 “playing” など、累積確率が最も高いトークンのみを考慮します。

    • 中 top_p (例: 0.8): AI モデルは、“playing”、“sleeping”、“eating” など、累積確率がより高いトークンを考慮します。

    • 高 top_p (例: 1.0): I モデルは、“driving” や “flying” などの確率の低いトークンを含むすべてのトークンを考慮します。

  • Top-k は、人気のあるサンプリング戦略です。累積確率が確率 P を超える最小の単語のセットから選択する Top-P と比較して、Top-K サンプリングでは、次の可能性が最も高い K 個の単語がフィルタリングされ、確率の集合が K 個の次の単語のみに再分配されます。猫の例では、k=3 の場合、“playing”、“sleeping” および “eating” だけが次の単語として考慮されます。

生成プロセスを最適化し、メモリーをより効率的に使用するには、use_cache=True オプションを有効にします。出力側は自動回帰であるため、出力トークンの非表示状態は、その後の生成ステップごとに計算されると同じままになります。したがって、新しいトークンを生成するたびに再計算するのは無駄であるように思えます。キャッシュを使用すると、モデルは計算後に非表示の状態を保存します。モデルは各タイムステップで最後に生成された出力トークンのみを計算し、保存された出力トークンを非表示のトークンに再利用します。これにより、変圧器モデルの生成の複雑さが O(n^3) から O(n^2) に軽減されます。仕組みの詳細については、この記事を参照してください。このオプションを使用すると、モデルは前のステップの非表示状態 (キャッシュされたアテンション・キーと値) を入力として取得し、さらに現在のステップの非表示状態を出力として提供します。これは、次のすべての反復では、前のステップから取得した新しいトークンと、次のトークン予測を取得するためのキャッシュされたキー値のみを提供するだけで十分であることを意味します。

生成サイクルは、シーケンストークンの終わりに達するまで繰り返されます。または、最大のトークンが生成されるときに中断されることもあります。すでに述べたように、生成全体がストリーミング API を使用し、新しいトークンを出力キューに追加し、準備ができたらそれらをプリントするまで待つことなく、現在生成されたトークンをプリントできるようになります。

インポートの設定

from threading import Thread
from time import perf_counter
from typing import List
import gradio as gr
from transformers import AutoTokenizer, TextIteratorStreamer
import numpy as np

ユーザープロンプトのテンプレートを準備

効果的に生成するために、モデルは特定の形式での入力を期待します。以下のコードは、追加のコンテキストを提供してユーザー指示をモデルに渡すためのテンプレートを準備します。

tokenizer = AutoTokenizer.from_pretrained(model_name)
tokenizer_kwargs = model_configuration.get("toeknizer_kwargs", {})


def get_special_token_id(tokenizer: AutoTokenizer, key: str) -> int:
    """
    Gets the token ID for a given string that has been added to the tokenizer as a special token.

    Args:
        tokenizer (PreTrainedTokenizer): the tokenizer
        key (str): the key to convert to a single token

    Raises:
        RuntimeError: if more than one ID was generated

    Returns:
        int: the token ID for the given key
    """
    token_ids = tokenizer.encode(key)
    if len(token_ids) > 1:
        raise ValueError(f"Expected only a single token for '{key}' but found {token_ids}")
    return token_ids[0]

response_key = model_configuration.get("response_key")
tokenizer_response_key = None

if response_key is not None:
    tokenizer_response_key = next((token for token in tokenizer.additional_special_tokens if token.startswith(response_key)), None)

end_key_token_id = None
if tokenizer_response_key:
    try:
        end_key = model_configuration.get("end_key")
        if end_key:
            end_key_token_id = get_special_token_id(tokenizer, end_key)
        # Ensure generation stops once it generates "### End"
    except ValueError:
        pass

prompt_template = model_configuration.get("prompt_template", "{instruction}")
end_key_token_id = end_key_token_id or tokenizer.eos_token_id
pad_token_id = end_key_token_id or tokenizer.pad_token_id
Special tokens have been added in the vocabulary, make sure the associated word embeddings are fine-tuned or trained.

メイン生成関数

前述したように、run_generation 関数は生成を開始するエントリーポイントです。指定された入力命令をパラメーターとして取得し、モデル応答を返します。

def run_generation(user_text:str, top_p:float, temperature:float, top_k:int, max_new_tokens:int, perf_text:str):
    """
    Text generation function

    Parameters:
      user_text (str): User-provided instruction for a generation.
      top_p (float):  Nucleus sampling. If set to < 1, only the smallest set of most probable tokens with probabilities that add up to top_p or higher are kept for a generation.
      temperature (float): The value used to module the logits distribution.
      top_k (int): The number of highest probability vocabulary tokens to keep for top-k-filtering.
      max_new_tokens (int): Maximum length of generated sequence.
      perf_text (str): Content of text field for printing performance results.
    Returns:
      model_output (str) - model-generated text
      perf_text (str) - updated perf text filed content
    """

    # Prepare input prompt according to model expected template
    prompt_text = prompt_template.format(instruction=user_text)

    # Tokenize the user text.
    model_inputs = tokenizer(prompt_text, return_tensors="pt", **tokenizer_kwargs)

    # Start generation on a separate thread, so that we don't block the UI. The text is pulled from the streamer
    # in the main thread. Adds timeout to the streamer to handle exceptions in the generation thread.
    streamer = TextIteratorStreamer(tokenizer, skip_prompt=True, skip_special_tokens=True)
    generate_kwargs = dict(
        model_inputs,
        streamer=streamer,
        max_new_tokens=max_new_tokens,
        do_sample=True,
        top_p=top_p,
        temperature=float(temperature),
        top_k=top_k,
        eos_token_id=end_key_token_id,
        pad_token_id=pad_token_id
    )
    t = Thread(target=ov_model.generate, kwargs=generate_kwargs)
    t.start()

    # Pull the generated text from the streamer, and update the model output.
    model_output = ""
    per_token_time = []
    num_tokens = 0
    start = perf_counter()
    for new_text in streamer:
        current_time = perf_counter() - start
        model_output += new_text
        perf_text, num_tokens = estimate_latency(current_time, perf_text, new_text, per_token_time, num_tokens)
        yield model_output, perf_text
        start = perf_counter()
    return model_output, perf_text

アプリケーションのヘルパー

インタラクティブなユーザー・インターフェイスを作成するには、Gradio ライブラリーを使用します。以下のコードは、UI 要素との通信に使用される便利な関数を提供します。

def estimate_latency(current_time:float, current_perf_text:str, new_gen_text:str, per_token_time:List[float], num_tokens:int):
    """
    Helper function for performance estimation

    Parameters:
      current_time (float): This step time in seconds.
      current_perf_text (str): Current content of performance UI field.
      new_gen_text (str): New generated text.
      per_token_time (List[float]): history of performance from previous steps.
      num_tokens (int): Total number of generated tokens.

    Returns:
      update for performance text field
      update for a total number of tokens
    """
    num_current_toks = len(tokenizer.encode(new_gen_text))
    num_tokens += num_current_toks
    per_token_time.append(num_current_toks / current_time)
    if len(per_token_time) > 10 and len(per_token_time) % 4 == 0:
        current_bucket = per_token_time[:-10]
        return f"Average generation speed: {np.mean(current_bucket):.2f} tokens/s. Total generated tokens: {num_tokens}", num_tokens
    return current_perf_text, num_tokens

def reset_textbox(instruction:str, response:str, perf:str):
    """
    Helper function for resetting content of all text fields

    Parameters:
      instruction (str): Content of user instruction field.
      response (str): Content of model response field.
      perf (str): Content of performance info filed

    Returns:
      empty string for each placeholder
    """
    return "", "", ""

指示追従パイプラインを実行

これで、モデルの機能を調べる準備が整いました。このデモは、テキスト命令を使用してモデルと通信できるシンプルなインターフェイスを提供します。[ユーザー指示] フィールドに指示を入力するか、事前定義された例から 1 つを選択し、[送信] ボタンをクリックして生成を開始します。さらに、高度な生成パラメーターに変更できます。

  • Device - 推論デバイスを切り替えることができます。新しいデバイスが選択されるたびにモデルが再コンパイルされるため、時間がかかることに注意してください。

  • Max New Tokens - 生成されるテキストの最大サイズ。

  • Top-p (nucleus sampling) - < 1 に設定すると、合計が top_p 以上になる確率が高いトークンの最小セットのみが 1 回の生成にわたって保持されます。

  • Top-k - top-k フィルター処理のために保持する最も確率の高い語彙トークンの数。

  • Temperature - ロジット分布をモジュール化するために使用される値。

examples = [
    "Give me a recipe for pizza with pineapple",
    "Write me a tweet about the new OpenVINO release",
    "Explain the difference between CPU and GPU",
    "Give five ideas for a great weekend with family",
    "Do Androids dream of Electric sheep?",
    "Who is Dolly?",
    "Please give me advice on how to write resume?",
    "Name 3 advantages to being a cat",
    "Write instructions on how to become a good AI engineer",
    "Write a love letter to my best friend",
]



with gr.Blocks() as demo:
    gr.Markdown(
        "# Question Answering with " + model_id.value + " and OpenVINO.\n"
        "Provide instruction which describes a task below or select among predefined examples and model writes response that performs requested task."
    )

    with gr.Row():
        with gr.Column(scale=4):
            user_text = gr.Textbox(
                placeholder="Write an email about an alpaca that likes flan",
                label="User instruction"
            )
            model_output = gr.Textbox(label="Model response", interactive=False)
            performance = gr.Textbox(label="Performance", lines=1, interactive=False)
            with gr.Column(scale=1):
                button_clear = gr.Button(value="Clear")
                button_submit = gr.Button(value="Submit")
            gr.Examples(examples, user_text)
        with gr.Column(scale=1):
            max_new_tokens = gr.Slider(
                minimum=1, maximum=1000, value=256, step=1, interactive=True, label="Max New Tokens",
            )
            top_p = gr.Slider(
                minimum=0.05, maximum=1.0, value=0.92, step=0.05, interactive=True, label="Top-p (nucleus sampling)",
            )
            top_k = gr.Slider(
                minimum=0, maximum=50, value=0, step=1, interactive=True, label="Top-k",
            )
            temperature = gr.Slider(
                minimum=0.1, maximum=5.0, value=0.8, step=0.1, interactive=True, label="Temperature",
            )

    user_text.submit(run_generation, [user_text, top_p, temperature, top_k, max_new_tokens, performance], [model_output, performance])
    button_submit.click(run_generation, [user_text, top_p, temperature, top_k, max_new_tokens, performance], [model_output, performance])
    button_clear.click(reset_textbox, [user_text, model_output, performance], [user_text, model_output, performance])

if __name__ == "__main__":
    demo.queue()
    try:
        demo.launch(height=800)
    except Exception:
        demo.launch(share=True, height=800)

# If you are launching remotely, specify server_name and server_port
# EXAMPLE: `demo.launch(server_name='your server name', server_port='server port in int')`
# To learn more please refer to the Gradio docs: https://gradio.app/docs/