ローカル+CPU+Python で Large-Language Model(LLM)を動かしてみた
🔗 目次
はじめに
本記事では,「ローカル環境で GPU も無いけど LLM を試してみたい」という方向けに軽量な LLM を動かす方法をご紹介します.
簡単に試すことができるように,基本的には python があれば動く最小構成になっています.
動作確認環境
| スペック | |
|---|---|
| PC | MacBook Pro 2020モデル |
| CPU | Intel Core i5 1.4 GHz クアッドコア |
| メモリ | 8 GB |
| Python | 3.13.1 |
| llama-cpp-python | 0.3.9 |
| LLM モデル形式 | gguf(phi-3-mini 4bit / 16bit) |
環境構築
フォルダ構成
./playground/ # 作業フォルダ
┣━ .venv/ # Python 仮想環境
┣━ models/ # モデル格納用フォルダ
┃ ┗━ Phi-3-mini-4k-instruct-q4.gguf
┗━ sample.py # 実行スクリプト
準備
terminal または コマンドプロンプトで下記を実行します.
- 仮想環境構築
$ python -m venv .venv - activate
- Linux/Mac
$ source .venv/bin/activate - Windows
.\.venv\Scripts\activate.exe
- Linux/Mac
- ライブラリインストール
$ pip install -U pip $ pip install llama-cpp-python - モデルダウンロード
今回のデモでは Phi-3-mini-4k-instruct-gguf という Microsoft のモデルを使用します.
リンクから Phi-3-mini-4k-instruct-q4.gguf というモデルをダウンロードし,models/ フォルダに格納します.
これで動かす準備は完了です.
sample.py は後ほど出てきます.
使用技術説明
今回は手軽に動かすために llama-cpp-python というライブラリと phi という LLM モデルを使用しました.
llama-cpp-python
llama-cpp-python とは
llama-cpp-python は,llama.cpp(C/C++ で書かれた LLM 推論エンジン)を Python から扱うためのラッパーライブラリです.
主な特徴:
-
.gguf形式のモデルを手軽にロード・推論可能 - ローカルで動作
- CPU で動作可能
裏側では llama.cpp を呼び出す仕組みになっており,Python ユーザーでも高速・軽量な LLM 体験が可能になります.
gguf とは
gguf は、ggml(軽量推論用フォーマット)の後継であり,
軽量化・量子化された LLM モデルを効率的に扱うためのファイル形式です.
主な特徴:
- モデルをさまざまな bit 精度で格納可能
- llama.cpp ライブラリから使用可能
- 多くのオープンモデルが対応
Phi-3-mini-4k-instruct-gguf
Phi-3-mini-4k-instruct とは
Phi-3-mini-4k-instruct は,Microsoft によって開発された軽量 LLM です.
主な特徴:
- 約 3.8B パラメータと非常に軽量
-
.gguf形式に変換することで、llama-cpp系ライブラリで高速・ローカル実行が可能(Phi-3-mini-4k-instruct-gguf)
プロンプトフォーマット
phi モデルでは,下記のようなフォーマットで入力を行います.
<|user|>\nQuestion <|end|>\n<|assistant|>
例えば,
<|user|>
あなたは何ができますか?<|end|>
<|assistant|>
上記のようなプロンプトをモデルに入力すると,モデルが <|assistant|> 以降の続きを出力します.
Phi に関してはこのようなフォーマットでプロンプトを与えます.
デモ
実行方法
下記のコードを sample.py にコピーして実行してください.
query の内容を変更すると,LLM からの回答もそれに沿った内容になります.
from llama_cpp import Llama
llm = Llama(
model_path="./models/Phi-3-mini-4k-instruct-q4.gguf",
n_ctx=2048, # コンテキスト長(質問+回答)
n_threads=4, # CPUスレッド数(環境に合わせて調整)
)
query = "こんにちは!あなたは何が得意ですか?\n"
prompt = (
"<|user|>\n"
f"{query}"
"<|end|>\n"
"<|assistant|>\n"
)
output = llm(prompt, max_tokens=256, stop=["<|end|>", "<|user|>", "<|assistant|>"])
print(output["choices"][0]["text"].strip())
実行例1
- 質問
query = "こんにちは!あなたは何が得意ですか?\n" - 回答
こんにちは! 私の得意とするスキルには、多言語処理、大規模データベースの検索、 精度の高い情報の提供、およびAIに基づく自然な会話があります。 私はテキストベースのインタラクティブな対話での質の高い解答を提供することで知られており、 さまざまな分野の質問に対して対応することができます。
実行例2
- 質問
query = "ダイエットを続けられる一言ちょうだい\n" - 回答
"継続されるダイエットは、自己管理と意図を持って行動することが大切です。 それぞれの目標に注意を払い、毎日の選択を見直してみてください。"
どちらもそれっぽい回答得られました!
ダイエットに関してはなかなか辛辣なお言葉をいただきました.
4bit vs 16bit
Phi-3-mini-4k-instruct-gguf には,Phi-3-mini-4k-instruct-fp16.gguf と Phi-3-mini-4k-instruct-q4.gguf があります.これはモデルの bit 精度を表しています.fp16 は 16bit で,q4 は 4bitです.
一般的に,fp16 の方が精度は良いですが推論に時間がかかり,q4 の方が精度は劣る分,推論時間が短いです.
今回は同じプロンプトを入力して,推論時間と回答を比較してみようと思います.
質問1
query = "あなた自身のことを説明してください.\n"
| 4bit | 16bit | |
|---|---|---|
| 処理時間 [s] | 36 | 5548 (=約92分) |
-
4bit モデルの回答
私はオープンAIアシスタントの言語モデルであり、 大量のデータに基づいて機能しています。 私の主な機能には、質問に答えたり、テキストを生成したり、 言語を理解したりする能力が含まれます。 私は大文字小文字を区別せず、情報を提供し、 単語やパラグラフを細かく編集することができます。 私は開発者が定義したガイドラインに基づいて操作されており、 一人一人のユーザーにカスタマイズ可能な体験を提供するためのサポートを行っています。 ただし、私は個人の意見や感情を -
16bit モデルの回答
私はオープンAIの言語モデルです。 私は大量のテキストデータを学習し、 それを基にユーザーの質問や入力に対して適切な回答や情報を生成することができます。 私のプログラミングはTensorFlowやPyTorchのような 計算機のための機械学習フレームワークを使用して行われ、 機械学習アルゴリズムとディープラーニングを駆使しています。 私の目的は情報を提供し、ユーザーが問いの意図を理解し、 それに応じた助言をすることにあります。 ただし、私は個人的な意見や感情
どちらのモデルの回答も似たような要旨の内容ですが,16bit モデルの方がより具体的な内容を語っているように見えます.
質問2(論理クイズ)
query = "この問題解ける?\n"
quiz = "【問題】\n\
A、B、Cの3人の中で1人だけが正直者です。残りの2人は嘘をついています。\n\
次の3人の言葉から正直者を見つけてください。 \n\
A「私は正直者です。」\n\
B「Aはうそつきです。私が正直者です。」\n\
C「Bはうそつきです。本当の正直者は私です。」\n\
A、B、C のいずれかで答えてください。\n"
| 4bit | 16bit | |
|---|---|---|
| 処理時間 [s] | 40 | 4338 (=約72分) |
-
4bit モデルの回答
正直者はBです。 説明: Aが正直者であるという前提で考えると、Aが自己を正直者と表明しているため、 Aが嘘をついている可能性が高いです。 Bが正直者であるという前提で考えると、BがAが嘘をついていると結論づけることができますが、 これによりBも嘘をついていることになります。 Cが正直者であるという前提で考えると、 CがBを嘘該当者としていると結論づけます。 この場合、AとCが嘘をついていることになります。 したがって、嘘をつく人は3人ということに -
16bit モデルの回答
正直者はBです。 説明: Aが正直者であるとすると、BとCが嘘をついていることになります。 Bが正直者だった場合、Aは嘘をついていると言っているので嘘ではないのですが、 Cの発言は不可解であり、矛盾してしまいます。 Cが正直者であるとすると、Bはうそつきであるため、 Bの発言は真実ではありません。 したがって、Bが正直者であると判断します。
一応,どちらのモデルも回答は正解ですが,説明に大きな差が出ました.
4bit モデルは説明の論理が破綻しています.
16bit は言っていることは理解できるけれど,納得するにはあと少し必要,といった印象でした.
ちなみに,ちょっとプロンプトを変更すると回答もコロコロ変わるので,試してみても面白いです.
この比較から,4bit モデルは高速に動作する一方で,推論の正確性や説明力にはやや難があることがわかります.
また,16bit モデルは非常に重いですが,より整った論理的回答が得られやすいという結果になりました.
用途に応じてモデルの精度を選ぶ判断材料として,こういった比較は非常に重要です.
まとめ
今回は、ローカル環境 + CPU + Python というシンプルな構成で,
軽量な LLM(Phi-3-mini)を実行する方法をご紹介しました.
また,bit 精度の異なるモデル(4bit vs 16bit)で推論速度と回答の質がどう異なるかも比較しました.
AI モデルを扱うにおいて,速度と精度は常にトレードオフの関係です.
自分のマシンスペックや用途に合わせて,最適なモデル構成を選ぶことが重要だと実感できる内容だったのではないでしょうか.
📎 あわせて読みたい:RAG(Retrieval-Augmented Generation)
本記事のモチベーションは,Retrieval-Augmented Generation(RAG)を試す中で生まれました.
RAG は,LLMを再学習せずに、外部データを検索・活用して回答を補強する技術です.
興味がある方は,以下の記事で解説していますので,ぜひあわせてどうぞ👇