Python+OpenVINOでLLMを実行してみた

テクノロジー
この記事は約22分で読めます。

1.実行環境

 備忘録。2026年7月時点のWindows Updateは全て適用した状態のPC。
 OpenVINOのSystem Requirementsでは、Intel CPUとARM Chipsetのみのサポートとなっているが一応AMD CPUもイケるらしい。

2.環境構築

 上記System Requirementsにて、Pythonは3.10-3.14が要件とのこと。
 AnacondaにてPython 3.14のまっさらな環境を作成した。

 ちなみに、公式ドキュメントによると「OpenVINOランタイムは、動作に特定のC++ライブラリを必要とします」と記載があり、以下の開発環境を推奨していた。今回は、Python以外のコンポーネントは既に導入済みの端末で実験した。

 作成したPython環境には、下表のライブラリをpip installした。

ライブラリ名pip コマンド
OpenVINO GenAIpip install openvino-genai
Optimum Intelpip install optimum[openvino,nncf]
Jupyter Labpip install jupyterlab
ipywidgetspip install ipywidgets

 pip installするライブラリの参考にしたドキュメントはこちら。

 構築後の仮想環境のライブラリは以下のとおり。

annotated-doc==0.0.4
anyio==4.14.2
argon2-cffi==25.1.0
argon2-cffi-bindings==25.1.0
arrow==1.4.0
asttokens==3.0.2
async-lru==2.3.0
attrs==26.1.0
babel==2.18.0
beautifulsoup4==4.15.0
bleach==6.4.0
certifi==2026.6.17
cffi==2.1.0
charset-normalizer==3.4.9
click==8.4.2
colorama==0.4.6
comm==0.2.3
debugpy==1.8.21
decorator==5.3.1
defusedxml==0.7.1
executing==2.2.1
fastjsonschema==2.21.2
filelock==3.30.2
fqdn==1.5.1
fsspec==2026.6.0
h11==0.16.0
hf-xet==1.5.2
httpcore==1.0.9
httpx==0.28.1
huggingface_hub==1.24.0
idna==3.18
ipykernel==7.3.0
ipython==9.15.0
ipython_pygments_lexers==1.1.1
ipywidgets==8.1.8
isoduration==20.11.0
jedi==0.20.0
Jinja2==3.1.6
joblib==1.5.3
json5==0.15.0
jsonpointer==3.1.1
jsonschema==4.26.0
jsonschema-specifications==2025.9.1
jupyter-events==0.12.1
jupyter-lsp==2.3.1
jupyter_builder==1.1.0
jupyter_client==8.9.1
jupyter_core==5.9.1
jupyter_server==2.20.0
jupyter_server_terminals==0.5.4
jupyterlab==4.6.1
jupyterlab_pygments==0.3.0
jupyterlab_server==2.28.0
jupyterlab_widgets==3.0.16
lark==1.3.1
markdown-it-py==4.2.0
MarkupSafe==3.0.3
matplotlib-inline==0.2.2
mdurl==0.1.2
mistune==3.3.3
mpmath==1.3.0
narwhals==2.24.0
nbclient==0.11.0
nbconvert==7.17.1
nbformat==5.10.4
nest-asyncio2==1.7.2
networkx==3.4.2
ninja==1.13.0
nncf==3.2.0
notebook_shim==0.2.4
numpy==2.4.6
openvino==2026.2.1
openvino-genai==2026.2.1.0
openvino-telemetry==2025.2.0
openvino-tokenizers==2026.2.1.0
optimum==2.2.0
optimum-intel==2.0.0
packaging==26.2
pandocfilters==1.5.1
parso==0.8.7
platformdirs==4.10.0
prometheus_client==0.25.0
prompt_toolkit==3.0.52
psutil==7.2.2
pure_eval==0.2.3
pycparser==3.0
pydot==3.0.4
Pygments==2.20.0
pyparsing==3.3.2
python-dateutil==2.9.0.post0
python-json-logger==4.1.0
pywinpty==3.0.5
PyYAML==6.0.3
pyzmq==27.1.0
referencing==0.37.0
regex==2026.7.10
requests==2.34.2
rfc3339-validator==0.1.4
rfc3986-validator==0.1.1
rfc3987-syntax==1.1.0
rich==15.0.0
rpds-py==2026.6.3
safetensors==0.7.0
scikit-learn==1.9.0
scipy==1.18.0
Send2Trash==2.1.0
setuptools==83.0.0
shellingham==1.5.4
six==1.17.0
soupsieve==2.8.4
stack-data==0.6.3
sympy==1.14.0
tabulate==0.10.0
terminado==0.18.1
threadpoolctl==3.6.0
tinycss2==1.5.1
tokenizers==0.22.2
torch==2.13.0
tornado==6.5.7
tqdm==4.68.4
traitlets==5.15.1
transformers==5.0.0
typer==0.27.0
typer-slim==0.24.0
typing_extensions==4.16.0
tzdata==2026.3
uri-template==1.3.0
urllib3==2.7.0
wcwidth==0.8.2
webcolors==25.10.0
webencodings==0.5.1
websocket-client==1.9.0
widgetsnbextension==4.0.15

 ちなみに、optinumをpip installするとこれらのライブラリが一気にインストールされた。
「Successfully installed annotated-doc-0.0.4 click-8.4.2 filelock-3.30.2 fsspec-2026.6.0 hf-xet-1.5.2 huggingface_hub-1.24.0 joblib-1.5.3 markdown-it-py-4.2.0 mdurl-0.1.2 mpmath-1.3.0 narwhals-2.24.0 networkx-3.4.2 ninja-1.13.0 nncf-3.2.0 optimum-2.2.0 optimum-intel-2.0.0 pydot-3.0.4 pyparsing-3.3.2 regex-2026.7.10 rich-15.0.0 safetensors-0.7.0 scikit-learn-1.9.0 scipy-1.18.0 setuptools-83.0.0 shellingham-1.5.4 sympy-1.14.0 tabulate-0.10.0 threadpoolctl-3.6.0 tokenizers-0.22.2 torch-2.13.0 tqdm-4.68.4 transformers-5.0.0 typer-0.27.0 typer-slim-0.24.0」

3.Transformersモデルの実行(CPU推論)

 以下のリンクを参考にモデルを実行してみる。

Inference · Hugging Face
We’re on a journey to advance and democratize artificial intelligence through open source and open science.

 実行可能なモデルは以下のものらしい。

Supported models · Hugging Face
We’re on a journey to advance and democratize artificial intelligence through open source and open science.

 今回はDL済みだったQwen2.5 Coder7B itを使う。

https://huggingface.co/Qwen/Qwen2.5-Coder-7B-Instruct

 実行したコードは以下のとおり。

import os
import psutil
import time


from optimum.intel import OVModelForCausalLM
from transformers import AutoProcessor


process = psutil.Process(os.getpid())
mem_before = process.memory_info().rss / 1024**2  # MB
start = time.time()

model_path = r"H:\LLM_Models\safetensor\Qwen\Qwen2.5-Coder-7B-Instruct"
processor = AutoProcessor.from_pretrained(model_path)

model = OVModelForCausalLM.from_pretrained(model_path)

messages = [
    {"role": "system", "content": "あなたは有能なAIアシスタントです。"},
    {"role": "user", "content": "面白いジョークを話して。またどこが面白いかも解説してください。"},
]

inputs = processor.apply_chat_template(
    messages,
    tokenize=True,
    return_dict=True,
    return_tensors="pt",
    add_generation_prompt=True,
    enable_thinking=False
).to(model.device)
input_len = inputs["input_ids"].shape[-1]

# Generate output
outputs = model.generate(**inputs, max_new_tokens=1024)
response = processor.decode(outputs[0][input_len:], skip_special_tokens=False)

mem_after = process.memory_info().rss / 1024**2  # MB
end = time.time()

print(response)

print("=" * 60)
print(f"実行前メモリ使用量: {mem_before:.2f} MB")
print(f"実行後メモリ使用量: {mem_after:.2f} MB")
print(f"実行時間: {end - start:.3f}")

# OpenVINO IR形式を保存
# 次回以降model_pathに保存済みのOpenVINO IR形式へのpathを渡せば変換が省略される。
open_vino_save_path = r"H:\LLM_Models\open_vino\Qwen\Qwen2.5-Coder-7B-Instruct"
model.save_pretrained(open_vino_save_path)
processor.save_pretrained(open_vino_save_path)

 出力された情報は以下のとおり。

No OpenVINO files were found for H:\LLM_Models\safetensor\Qwen\Qwen2.5-Coder-7B-Instruct, setting export=True to convert the model to the OpenVINO IR. Don’t forget to save the resulting model with .save_pretrained()
torch_dtype is deprecated! Use dtype instead!

loss_type=None was set in the config but it is unrecognized. Using the default loss: ForCausalLMLoss.
c:\ProgramData\anaconda3\envs\openvino_20260717\Lib\site-packages\transformers\cache_utils.py:132: TracerWarning: Converting a tensor to a Python boolean might cause the trace to be incorrect. We can’t record the data flow of Python values, so this value will be treated as a constant in the future. This means that the trace might not generalize to other inputs!
if not self.is_initialized or self.keys.numel() == 0:
c:\ProgramData\anaconda3\envs\openvino_20260717\Lib\site-packages\transformers\masking_utils.py:171: TracerWarning: Converting a tensor to a Python boolean might cause the trace to be incorrect. We can’t record the data flow of Python values, so this value will be treated as a constant in the future. This means that the trace might not generalize to other inputs!
if (padding_length := kv_length + kv_offset – attention_mask.shape[-1]) > 0:
c:\ProgramData\anaconda3\envs\openvino_20260717\Lib\site-packages\optimum\exporters\openvino\patching_utils.py:247: TracerWarning: torch.tensor results are registered as constants in the trace. You can safely ignore this warning if you use this function to create tensors out of constant variables that would be the same every time you call this function. In any other case, this might cause the trace to be incorrect.
torch.tensor(0.0, device=mask.device, dtype=dtype),
c:\ProgramData\anaconda3\envs\openvino_20260717\Lib\site-packages\optimum\exporters\openvino\patching_utils.py:248: TracerWarning: torch.tensor results are registered as constants in the trace. You can safely ignore this warning if you use this function to create tensors out of constant variables that would be the same every time you call this function. In any other case, this might cause the trace to be incorrect.
torch.tensor(torch.finfo(torch.float16).min, device=mask.device, dtype=dtype),
c:\ProgramData\anaconda3\envs\openvino_20260717\Lib\site-packages\transformers\integrations\sdpa_attention.py:77: TracerWarning: Converting a tensor to a Python boolean might cause the trace to be incorrect. We can’t record the data flow of Python values, so this value will be treated as a constant in the future. This means that the trace might not generalize to other inputs!
is_causal = query.shape[2] > 1 and attention_mask is None and is_causal
INFO:nncf:Statistics of the bitwidth distribution:
+—————————+—————————–+—————————————-+
| Weight compression mode | % all parameters (layers) | % ratio-defining parameters (layers) |
+===========================+=============================+========================================+
| int8_asym, per-channel | 100% (198 / 198) | 100% (198 / 198) |
+—————————+—————————–+—————————————-+

もちろんです。以下にジョークをお聞かせします。

「ある日、パン屋さんでおばあちゃんがパンを买いました。
おばあちゃん:「これ何ですか?」
店員:「これは黒パンです。」
おばあちゃん:「ええ、それらしくて美味しいですね。」」

このジョークの面白い点は、「黒パン」の意味が一般的ではありません。私たちが通常考えている「黒パン」とは全く異なるものです。店員の答えが予想外で、笑いのきっかけとなっています。<|im_end|>

実行前メモリ使用量: 860.89 MB
実行後メモリ使用量: 14426.25 MB
実行時間: 95.787 秒

 変換後のモデルサイズの比較は以下のとおり。ちなみに、変換時にはピークで20GB弱メモリを消費した。

変換前のtransformers形式
OpenVINO IR形式に変換後

 小さくなった理由としては、以下のリンク先の「If quantization_config is not provided, model will be exported in 8 bits by default when it has more than 1 billion parameters. You can disable it with load_in_8bit=False.」という記載から、1Billionを超えるパラメータ数のモデルは基本的にInt8型に圧縮されているためと思われ。

Optimization · Hugging Face
We’re on a journey to advance and democratize artificial intelligence through open source and open science.

 続いて、保存したOpenVINO IR形式のQwen2.5 Coderを読み込んで文書生成を行う。コードは一緒で、読み込み先のモデルパスのみ異なる。

process = psutil.Process(os.getpid())
mem_before = process.memory_info().rss / 1024**2  # MB
start = time.time()

processor = AutoProcessor.from_pretrained(open_vino_save_path)
model = OVModelForCausalLM.from_pretrained(open_vino_save_path)

messages = [
    {"role": "system", "content": "あなたは有能なAIアシスタントです。"},
    {"role": "user", "content": "面白いジョークを話して。またどこが面白いかも解説してください。"},
]

inputs = processor.apply_chat_template(
    messages,
    tokenize=True,
    return_dict=True,
    return_tensors="pt",
    add_generation_prompt=True,
    enable_thinking=False
).to(model.device)
input_len = inputs["input_ids"].shape[-1]


outputs = model.generate(**inputs, max_new_tokens=1024)
response = processor.decode(outputs[0][input_len:], skip_special_tokens=False)

mem_after = process.memory_info().rss / 1024**2  # MB

end = time.time()

print(response)
print("=" * 60)
print(f"実行前メモリ使用量: {mem_before:.2f} MB")
print(f"実行後メモリ使用量: {mem_after:.2f} MB")
print(f"実行時間: {end - start:.3f} 秒")

もちろんです、以下に面白いジョークをお伝えします。

ジョーク: 「誰が最強のプログラマーなのか?」

答え: 「AIだよ。なぜなら、他のプログラマーと比べて、コードを書くのが最も効率的だからだよ。」

このジョークは「AI」についての知識と、「効率性」や「比較」に関する思考を含んでいます。AIが他のプログラマーと比べて最強だと主張している点が面白いですね。また、AIが「コードを書く」というタスクで最も優れたという設定も興味深い表現になっています。<|im_end|>

実行前メモリ使用量: 1006.55 MB
実行後メモリ使用量: 14696.58 MB
実行時間: 47.514 秒

 

 ちなみに、普通にtransformersで同じモデル・プロンプトを実行した結果は以下のとおり。

もちろんです、以下に面白いジョークを一つ紹介します。

ジョーク:「何が最も美しいですか?答えは「花」です。なぜなら、春には桜、夏にはバラ、秋には菊、冬には梅があります。」

このジョークの面白さは、「花」という単語が複数の季節で美しく見えるという観点から、一年を通じて美しさがあることを表現しています。また、「何が最も美しいですか?」という一般的な質問に対して、具体的な答えを提案することで、聴衆の興味を引きつける効果もあります。

実行前メモリ使用量: 492.35 MB
実行後メモリ使用量: 14095.08 MB
実行時間: 79.201 秒

 ここまでのipynbをHTMLに出力したものは以下のリンク先からどうぞ。

4.その他(GPU、NPU推論について)

 GPUを使った推論はIntel製GPUまたはiGPU搭載CPUでのみ実行可能とのことなので、私の環境では試せず…。

Why can’t OpenVINO support NVIDIA GPU’s? · openvinotoolkit openvino · Discussion #34009
Hello everyone, This is my first time contributing to an open source project and it is by far one of the nicest projects…

 NPUについては、以下のドキュメントを参考に実行方法についてできる限り検証した。というのも、私はIntel NPU搭載CPUを持っていないため、モデルの実行ができないためである。

NPU Device — OpenVINO??? documentation
OpenVINO??? supports the Neural Processing Unit, a low-power processing device dedicated to running AI inference.
# NPUが認識されるかチェック
# ['CPU', 'GPU', 'NPU']のようにNPUが表示されればOK
import openvino as ov

core = ov.Core()

print("利用可能なデバイス:", core.available_devices)

for device in core.available_devices:
    print(device, core.get_property(device, "FULL_DEVICE_NAME"))

# 必要なライブラリのインポート
import openvino
import openvino_genai
import transformers
import nncf

print("OpenVINO   :", openvino.__version__)
print("Transformers:", transformers.__version__)
print("NNCF       :", nncf.__version__)

コマンドプロンプトからSafetensorsモデルをNPU向けに変換。仮想環境を立ち上げた状態で、以下のコマンドを叩けばOpenVINO IR(NPU用)にモデルを変換できる。

optimum-cli export openvino -m "H:\LLM_Models\safetensor\Qwen\Qwen2.5-Coder-7B-Instruct" --task text-generation-with-past --weight-format int4 --sym --ratio 1.0 --group-size -1 "H:\LLM_Models\open_vino\Qwen\Qwen2.5-Coder-7B-Instruct-int4-npu"
コマンドプロンプトの画面
変換前メモリ使用量は25GB
変換中のメモリ消費のピークは48GB程度なので、差し引き23GB程度のリソースが必要
変換にて生成されたファイル群

 以下のようなコードでNPU推論ができる…はず(実行環境がないので妄想)

import os
import time
import psutil
import openvino_genai as ov_genai
from transformers import AutoTokenizer

model_path = (
    r"H:\LLM_Models\open_vino\Qwen"
    r"\Qwen2.5-Coder-7B-Instruct-int4-npu"
)

process = psutil.Process(os.getpid())
mem_before = process.memory_info().rss / 1024**2
start = time.time()

tokenizer = AutoTokenizer.from_pretrained(
    model_path,
    trust_remote_code=True,
)

messages = [
    {
        "role": "system",
        "content": "あなたは有能なAIアシスタントです。",
    },
    {
        "role": "user",
        "content": (
            "Pythonで高速な素数判定関数を書いて、"
            "処理内容も説明してください。"
        ),
    },
]

prompt = tokenizer.apply_chat_template(
    messages,
    tokenize=False,
    add_generation_prompt=True,
)

pipeline_config = {
    "MAX_PROMPT_LEN": 1024,
    "MIN_RESPONSE_LEN": 128,
    "GENERATE_HINT": "BEST_PERF",
    "CACHE_DIR": os.path.join(model_path, "npu_cache"),
}

pipe = ov_genai.LLMPipeline(
    model_path,
    "NPU",
    pipeline_config,
)

result = pipe.generate(
    prompt,
    max_new_tokens=512,
    do_sample=False,
)

end = time.time()
mem_after = process.memory_info().rss / 1024**2

print(result)

print("=" * 60)
print(f"実行前メモリ使用量: {mem_before:.2f} MB")
print(f"実行後メモリ使用量: {mem_after:.2f} MB")
print(f"メモリ増加量      : {mem_after - mem_before:+.2f} MB")
print(f"実行時間          : {end - start:.3f} 秒")

コメント