概要
Intel Arcを使ってHuggingfaceにある好きなLLMを使用したいのでllama.cppをインストールする。前回の投稿でIPEX-LLM Ollamaを動かすためのSYCL環境はセットアップ済みなので、ここでは足りないoneAI環境とllama.cppをセットアップする。
はじめからセットアップするのならIntel公式サイトを参照のこと。
なお、SYCLとoneAPIについてChatGPTによると…
| Tearm | note |
|---|---|
| SYCL | The Khronos Groupで策定したC++ベースのGPUプログラミング言語仕様 CUDAに相当する |
| oneAPI | Intelの開発エコプラットフォームの総称 CPU・GPU・FPGAなど異なる計算デバイスを同じコードで扱うことを目標として設計 |
The Khronos Groupは他にopenCL, openGL, Vulkanなどの仕様を策定しているよう。ちなにみ定期出張でよく行くHillsboro, Oregonの隣のBeavertonが所在らしい。
こんな流れで進める。
- GPUドライバ(不足分)インストール
- llama.cppのビルド
- llama-serverで動作速度計測
1. GPUドライバ(不足分)インストール
1.0 基本ビルド環境
入っていると思うけれど念の為。
sudo apt install -y cmake build-essential git
前回の投稿で入れたopenCL周りの確認
clinfo -l
groups | grep render
結果
Platform #0: Intel(R) OpenCL Graphics
`-- Device #0: Intel(R) Arc(TM) Graphics
ihmon adm cdrom sudo dip plugdev users lpadmin ollama render
1.1 oneAPIリポジトリ追加
Intel の oneAPI パッケージを apt から使えるように設定する。
sudo wget -qO /etc/apt/trusted.gpg.d/intel-oneapi.asc \
https://apt.repos.intel.com/intel-gpg-keys/GPG-PUB-KEY-INTEL-SW-PRODUCTS.PUB
echo "deb https://apt.repos.intel.com/oneapi all main" | \
sudo tee /etc/apt/sources.list.d/intel-oneapi.list
sudo apt update
1.2 oneAPIコンパイラとoneAPI MKL(Math Kernel Library)をインストール
oneAPIのコンパイラと計算用ライブラリをインストールし、oneAPI環境を読み込む。
sudo apt install -y intel-oneapi-compiler-dpcpp-cpp intel-oneapi-mkl-devel
source /opt/intel/oneapi/setvars.sh --force
2. llama.cppのビルド
2.1 llama.cppをclone
cd ~
git clone https://github.com/ggml-org/llama.cpp
cd llama.cpp
2.2 SYCLビルドの設定
CコンパイラをoneAPIに固定しMKLのパスを明示
rm -rf build-sycl
cmake -S . -B build-sycl \
-DGGML_SYCL=ON \
-DCMAKE_BUILD_TYPE=Release \
-DMKL_DIR="$MKLROOT/lib/cmake/mkl" \
-DCMAKE_C_COMPILER=/opt/intel/oneapi/compiler/2025.3/bin/icx \
-DCMAKE_CXX_COMPILER=/opt/intel/oneapi/compiler/2025.3/bin/icpx
-
DGGML_SYCL=ON… SYCL backend を有効にする -
DMKL_DIR… oneMKL の CMake モジュール場所 -
DCMAKE_{C,CXX}_COMPILER… SYCL 対応コンパイラ指定
2.3 ビルド実行
cmake --build build-sycl -j
結果
build-sycl/bin/llama-cli
build-sycl/bin/llama-server
2.4 llama.cpp動作確認
Intel Arcへモデルをオフロードして動かしてみる。
適当なフォルダにGGUFフォーマットのLLM(ここではqwen2.5-coder-7b-instruct-q4_k_m.gguf)を置いておく。
量子化モデルは下記からいただくことが多い。
https://huggingface.co/unsloth
source /opt/intel/oneapi/setvars.sh --force
export ONEAPI_DEVICE_SELECTOR=level_zero:gpu
build-sycl/bin/llama-cli \
-m ~/Documents/models/qwen2.5-coder-7b-instruct-q4_k_m.gguf \
-p "Hello world" \
-n 512 \
-ngl 99 \
-c 4096
-
-n… 生成token数 -
-ngl… GPU offload割合(99:完全GPU ~ 0:完全CPU) -
-c… 最大コンテキスト長
3. llama-serverで動作速度計測
3.1 準備
llama-serverをデフォルトのポート8080で起動する。
source /opt/intel/oneapi/setvars.sh --force
export ONEAPI_DEVICE_SELECTOR=level_zero:gpu
$HOME/llama.cpp/build-sycl/bin/llama-server -m ~/Documents/models/Qwen2.5-Coder-7B-Instruct-Q4_K_M.gguf -n 512 -ngl 99 -c 4096 --port 8080
GPUが使われているか確認するのならモニタを別ターミナルで起動しておく。htopはIntel Arcを認識できず使えなかった。
sudo intel_gpu_top
3.2 動作確認と計測
前回の投稿で使用したOllama版をllama.cpp版へ変更したものが下記。llama.cppはopenaiパケージを使用するのでpip install openaiしておくこと。
llm_db_llama.pyで関数を定義する。
import sqlite3
import os
import re
import pandas as pd
import numpy as np
import datetime
from openai import OpenAI # ollama から openai に変更
# --- Configuration ---
# llama.cpp server のアドレスを指定
client = OpenAI(base_url="http://localhost:8080/v1", api_key="sk-no-key-required")
MODEL = "qwen2.5-coder:7b" # llama.cpp側でロードしているモデルが使われるため、任意でOK
def execute_pandas_logic_from_llm(user_prompt, df=None, history=None, max_retries=1):
now = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
if history is None:
history = []
working_df = df.copy() if df is not None else None
if working_df is not None:
context_data = f"Target: Modify existing DataFrame 'df'.\nColumns: {working_df.dtypes.to_string()}\nSample:\n{working_df.head(2).to_string()}"
mode_instruction = "Modify the existing variable 'df'."
else:
context_data = "Target: Create a new DataFrame 'df' from scratch."
mode_instruction = "You must define 'df'."
system_prompt = f"""
You are a Pandas expert. {mode_instruction}
[Context]
{context_data}
[Rules]
1. Use ENGLISH for all code.
2. Respond ONLY with executable Python code.
3. The final result of the user's request MUST be stored back into the variable 'df'.
4. Do not include markdown code blocks.
5. If the result is a Series or a single value, convert it back to a DataFrame and assign it to 'df'.
"""
messages = [{'role': 'system', 'content': system_prompt}]
messages.extend(history)
messages.append({'role': 'user', 'content': user_prompt})
for attempt in range(max_retries + 1):
# --- llama.cpp (OpenAI互換) へのリクエストに変更 ---
response = client.chat.completions.create(
model=MODEL,
messages=messages,
temperature=0.0 # コード生成なので決定論的に
)
# レスポンスの取得形式を変更
full_content = response.choices[0].message.content
# --- (以下、exec処理やリトライロジックは変更なし) ---
match = re.search(r"```python\n?(.*?)\n?```", full_content, re.DOTALL | re.IGNORECASE)
clean_code = match.group(1).strip() if match else full_content.strip()
local_vars = {"pd": pd, "np": np, "df": working_df, "datetime": datetime}
try:
exec(clean_code, local_vars)
new_df = local_vars.get('df')
history.append({'role': 'user', 'content': user_prompt})
history.append({'role': 'assistant', 'content': f"```python\n{clean_code}\n```"})
return new_df, history
except Exception as e:
error_msg = str(e)
if attempt < max_retries:
messages.append({'role': 'assistant', 'content': clean_code})
messages.append({'role': 'user', 'content': f"Error: {error_msg}\nPlease fix the code."})
else:
print("Maximum retries reached.")
return df, history
jupyterで10回呼び出したときの平均処理時間を計測する。
import time
from llm_db_llama import execute_pandas_logic_from_llm
time_hist = []
for i in range(11):
timer_start = time.time()
user_input = "10行3列(列名はcol1, col2, col3)のDataFrameを作成してください。各要素は平均1.0、標準偏差1.0の正規分布から乱数生成してください。乱数のseedは42にしてください。"
current_df, pandas_history = execute_pandas_logic_from_llm(user_input)
# display(current_df)
# print(pandas_history)
user_input = "current_dfの各行ではなく、各列の平均値を計算してください。"
current_df, pandas_history = execute_pandas_logic_from_llm(
user_input, df=current_df, history=pandas_history
)
# display(current_df)
# print(pandas_history)
timer_end = time.time()
timer_elapsed = timer_end - timer_start
print(f"経過時間: {timer_elapsed:.2f}秒")
time_hist.append(timer_elapsed)
print(f"平均経過時間: {sum(time_hist[1:])/len(time_hist[1:]):.2f}秒")
前回のOllamaの結果と比べると2秒ほど悪化している。同じ7BのモデルでもOllamaでPullできるモデルの量子化条件がわからないので単純比較はできないが、目くじらを立てるほどの違いではないだろう。
| 平均処理時間 | 使用したOllama | |
|---|---|---|
| 内蔵Intel Arc 140T | 13.46 秒 | llama.cpp(今回) |
| 内蔵Intel Arc 140T | 11.27 秒 | IPEX-LLM Ollama |
| Core Ultra7 255H CPU | 45.62 秒 | 標準Ollama |
ネット環境がなく、ディスクリートGPUが搭載できないエッジ環境といった限られた条件で、再帰的に内部でプロンプトを生成しあう制御システムなどで使えそう。
以上