勤務校でSurface Pro 10が支給されました。Core Ultraを搭載した2024年モデルの2 in 1タブレット端末です。キャッチコピーは「AI を搭載したポータブルな法人向けタブレット」です。

ただ、普通に使っている分には搭載されているAIを使いこなしていることを感じることがありません。2024年10月23日記事執筆時点ではCopilotはβ版ですし、ローカルで動いていません。Intel AI Boost NPUが搭載されているので「AI搭載」と表現されているわけですが、使わないと意味がありません。

というわけで今回はSurface Pro 10のIntel NPUのリソースを使ってローカルでLLMを動かしてみました。

実行環境の用意

Miniconda3のインストール

まずは環境を整えます。Pythonで動かすのでMiniconda3をインストールしました。

Miniconda — Anaconda documentation

Miniconda3-latest-Windows-x86_64.exeを実行して、ひとまずPython3が動く環境を用意しました。

Intel NPUのデバイスドライバを更新する

Surface Pro 10の出荷時にインストールされているNPUドライバではLLM実行時にエラーが出ました。なのでまずはIntelのサイトより最新版のデバイスドライバをダウンロードしてデバイスドライバより手動で更新します。 https://downloadmirror.intel.com/825735/NPU_Win_Release_Notes_v2540.pdf

仮想環境を整える

Pythonの開発環境を整える際は、用途に応じてバージョンなどに気をつけるため仮想環境を用意するそうです。Miniconda3(Anaconda3も同じ)のcondaコマンドでLLM用の仮想環境を作ります。

conda create -n llm python=3.10.6

多分バージョン3.11以降でも動きそうですが、参考にしたサイトがこのバージョンで環境を構築していたのでひとまずこのバージョンにしました。

conda activate llm

環境が用意できたら有効化しておきます。そのあとで、Intel NPUを扱うためのライブラリをインストールします。

pip install torch
pip install intel_npu_acceleration_library

参考にしたサイトはこちらです。 【西川和久の不定期コラム】Core UltraにせっかくNPUが付いてるのでLLMや画像生成を動かしてみた!ミニPC「UH125 Pro」で - PC Watch

ライブラリの一部を修正する

記事執筆時点でのintel_npu_acceleration_libraryをそのまま使おうとすると、推論時にエラーが出ました。なのでこちらの記事を参考に修正します。

Intel® NPU Acceleration Library を使って LocalLLM を Intel NPU で動かす #Python - Qiita

ただGithubを見る限りプルリクは通っている気がするので、恐らく次に公開されるバージョン以降では修正されていると思います。(参照元のサイトにもこのような記述がありましたが、まだアップデートされておらず、僕自身も手動で修正しました。)

ちなみにMiniconda3のcondaコマンドで作った仮想環境でpipすると、修正対象のllm.pyはこのパスにありました。

C:\Users\ユーザー名\AppData\Local\miniconda3\envs\llm\Lib\site-packages\intel_npu_acceleration_library\nn\llm.py

LLMのモデルをローカルにダウンロードしておく

本来ならプログラムの初回実行時にモデルをダウンロードする内容がソースコードに書かれているんですが、僕の環境ではうまく動かなかったので、実行前にLLMのモデルをダウンロードしておきました。

今回の動作検証に使ったモデルは「TinyLlama/TinyLlama-1.1B-Chat-v1.0」です。

僕の場合はこちらの場所にフォルダを作りました。

C:\Users\ユーザー名\models\tinyllama

Hugging Faceにて公開されているファイルを一通り上記フォルダにダウンロードして保存しておきます。

TinyLlama/TinyLlama-1.1B-Chat-v1.0 at main

実際に推論させてみる

今回動かしたプログラムのソースコードはこちらです。

from torch.profiler import profile, ProfilerActivity

from transformers import AutoTokenizer, TextStreamer, AutoModelForCausalLM

from threading import Thread

import intel_npu_acceleration_library

import torch

import time

import sys

import os

  

# SSL検証を無効化

os.environ['CURL_CA_BUNDLE'] = ''

os.environ['REQUESTS_CA_BUNDLE'] = ''

  

# モデルIDを使用

model_id = "TinyLlama/TinyLlama-1.1B-Chat-v1.0"

  

# モデルのローカルパス

local_model_path = "./models/tinyllama"

  

# モデルをロード(公式コードにより近い形で)

model = AutoModelForCausalLM.from_pretrained(

    local_model_path,  # ローカルパスを使用

    local_files_only=True,

    use_cache=True

).eval()

  

tokenizer = AutoTokenizer.from_pretrained(

    local_model_path,  # ローカルパスを使用

    local_files_only=True,

    use_default_system_prompt=True

)

tokenizer.pad_token_id = tokenizer.eos_token_id

streamer = TextStreamer(tokenizer, skip_special_tokens=True)

  

print("Compile model for the NPU")

model = intel_npu_acceleration_library.compile(model)

print("モデルのコンパイルが成功しました")

  

query = "What is the meaning of life?"

prefix = tokenizer(query, return_tensors="pt")["input_ids"]

  

generation_kwargs = dict(

    input_ids=prefix,

    streamer=streamer,

    do_sample=True,

    top_k=50,

    top_p=0.9,

)

  

print("Run inference")

try:

    _ = model.generate(**generation_kwargs)

except Exception as e:

    print(f"推論エラー: {str(e)}")

    import traceback

    print("\nエラーの詳細:")

    print(traceback.format_exc())

元ネタはIntel NPU Acceleration Libraryにあったサンプルですが、LLMをローカルから参照するように変更してあります。

Large Language models — Intel® NPU Acceleration Library documentation

実際に推論している様子がこちらです。

Surface Pro 10のローカルでLLMが動いている様子

タスクマネージャーを見てみるとNPUの使用率が上がっていることが確認できました。最大で約40%ほど使っていました。ただCPUがずっと100%に張り付いていて、他の作業ができなくなるほどでした。

というか支給されたSurface Pro 10はメモリ8GBでSSD256GB。これでローカルLLMが動いているのも信じられないです。動作はかなり遅いんですけどね。英単語が出てくるごとに感動を覚えます。全然実用的ではありませんけどね。

使い勝手なんて話し出したらChatGPTの方が使いやすいのは当然です。ただ今回は、せっかくNPUを搭載しているんだから、使ってみないとなという好奇心から動作検証に至りました。実用的ではありませんが「先生のタブレット、AI入ってるんだー」なんて言いながら、子どもたちに見せようかな。