· 5 分 · エンジニアリング

AMDのLemonade:GPUを使用する高速でオープンソースのローカルLLMサーバー…

AMDのLemonade:GPUを使用する高速でオープンソースのローカルLLMサーバー…

AMDのLemonade:GPUとNPUを使用する高速なオープンソースのローカルLLMサーバー

実用的なローカルLLMサーバーは、20トークンのプロンプトへの応答に18秒かかる時点で、「ローカル」としての意味を失います。開発者のノートPCで、このパターンを何度も見てきました。モデルは技術的には動作していても、CPU使用率は700%に張り付き、ファンが回り始め、最初のトークンが届く頃にはユーザーはすでに思考の流れを失っています。

Lemonadeが解決しようとしているのは、このエンジニアリング上の問題です。ローカル推論を、デモ用スクリプトではなく、実際のサービスとして使えるものにすることを目指しています。

AMDのLemonadeは、クライアント側のハードウェアで言語モデルを実行するために設計された、オープンソースのローカルLLMサーバーです。特に、GPUおよびRyzen AI NPUのアクセラレーションを備えたAMDシステムを対象としています。興味深いのは、単に「モデルをローカルで動かす」ことではありません。その方法はすでにいくつもあります。注目すべきはサーバーとしての構成です。ローカルモデルをAPI経由で公開し、利用可能なアクセラレーターに推論を振り分け、通常のアプリケーションコードから使えるようにします。

ローカルのコパイロット、プライベートな文書アシスタント、エージェント用サンドボックス、オフライン推論ワークフローを構築するエンジニアにとって、これは適切な抽象化です。すべてのアプリケーションに、モデルの読み込み、重みの量子化、実行プロバイダーの選択、メモリの固定、トークンのストリーミング、セッションの管理の方法を把握させたくはありません。必要なのは、ローカル推論サービスです。

Lemonadeの実際の用途

大まかに言えば、Lemonadeはローカルモデルサーバーです。アプリケーションとハードウェアの間に位置します。

推論ロジックをアプリに直接組み込む代わりに、ローカルデーモンを実行します:

lemonade-server serve

その後、アプリケーションはHTTP経由でデーモンと通信します。多くの場合、OpenAI形式のチャット補完APIを使用します:

from openai import OpenAI

client = OpenAI(
    base_url="http://localhost:8000/api/v1",
    api_key="not-needed-for-local"
)

response = client.chat.completions.create(
    model="llama-3.1-8b-instruct",
    messages=[
        {"role": "system", "content": "You are a concise coding assistant."},
        {"role": "user", "content": "Write a Python function to chunk text by tokens."}
    ],
    temperature=0.2,
    stream=True,
)

for chunk in response:
    delta = chunk.choices[0].delta.content
    if delta:
        print(delta, end="", flush=True)

APIの形式は重要です。バックエンドがすでにGPT-5.5、Claude Sonnet 4.6、Gemini 3、またはオープンモデルのエンドポイントに対応していれば、多くの場合、同じクライアント抽象化の接続先をLemonadeに変更してローカル実行できます。これにより、新しいSDKに合わせてアプリを書き直すことなく、モデルのルーティング、プライバシーに配慮が必要な機能、オフラインワークフローをテストできます。

実際には、私はローカルLLMサーバーをインフラストラクチャの構成要素として扱っています:

App / IDE / Agent Runtime
        |
        | HTTP streaming chat API
        v
Lemonade local server
        |
        | model loader + scheduler
        v
Execution backend
        |
        | GPU / NPU / CPU fallback
        v
AMD client hardware

この明確な分離によって、実際のシステムで利用できるようになります。

GPUとNPUの両方が重要な理由

ほとんどのエンジニアは、LLM推論でGPUアクセラレーションが重要な理由をすでに理解しています。実行時間の大部分を行列乗算が占めており、GPUは並列演算向けに作られているからです。NPUについては比較的新しく、誤解されやすい部分です。

NPUは「小型のGPU」ではありません。厳しい電力と熱の制約下でニューラルネットワークの演算を行うよう最適化された、専用アクセラレーターです。ノートPCでは、この点が重要です。最適なローカル推論スタックは、30秒のベンチマークで秒あたりのトークン数の最大値が最も高いものとは限りません。多くの場合、ユーザーがSlack、ブラウザー、VS Code、Dockerを実行している間も、応答性を維持できるものが最適です。

理解するための有用な考え方:

  • GPU:大きなモデル、高いスループット、負荷の大きい並列計算に適しています。
  • NPU:低消費電力での常時稼働や、小型または最適化されたモデルに有用です。
  • CPU:信頼できるフォールバックですが、対話型LLMの利用では通常、遅延が大きくなります。
  • ハイブリッドシステム:モデルのサイズ、量子化、利用可能性に応じて処理を振り分ける場合に有用です。

たとえば、ローカルアシスタントでは次のような構成が考えられます:

{
  "routes": {
    "autocomplete": {
      "model": "small-code-model",
      "device": "npu",
      "max_tokens": 64
    },
    "chat": {
      "model": "llama-3.1-8b-instruct-q4",
      "device": "gpu",
      "max_tokens": 1024
    },
    "summarization": {
      "model": "compact-instruct",
      "device": "auto",
      "max_tokens": 512
    }
  }
}

私が好むのは、このパターンです。特定のアクセラレーターに固執せず、ワークロードごとに適切な実行先を選びます。

ローカルLLMサーバーの中でのLemonadeの位置づけ

現在、成熟したローカル推論の選択肢はいくつかあります。Lemonadeの特徴は、AMDを優先したローカルアクセラレーションです。特に、GPUとNPUの両方がクライアントプラットフォームに組み込まれているシステムに重点を置いています。

ツール最適な用途API形式対象ハードウェア主なトレードオフ
LemonadeAMDのローカルGPU/NPUでのモデル提供ローカルHTTP / OpenAIに似た使い方AMDのクライアント向けハードウェア従来のサーバーよりエコシステムが未成熟
Ollamaシンプルなローカルモデル管理REST API幅広いCPU/GPUに対応低レベルの実行制御に制限
llama.cppサーバー移植性の高い量子化推論REST / OpenAI互換モードCPU、Metal、CUDA、Vulkanなどバックエンドの調整に手作業が必要な場合がある
vLLM高スループットのモデル提供OpenAI互換サーバー向けGPUデプロイに必要なリソースや構成が大きい
LM Studioデスクトップでのローカル実験GUI + ローカルサーバー一般消費者向けマシンインフラの自動化にはやや不向き

Lemonadeは、あらゆる推論スタックをそのまま置き換えられるものではありません。データセンターのGPUで数百人の同時ユーザーにサービスを提供するなら、通常はvLLMのような継続的バッチ処理のアーキテクチャのほうが適しています。さまざまなマシン間で可能な限り高い移植性が必要なら、llama.cppは依然として有力な選択肢です。

対象マシンがAMDのノートPC、ワークステーション、またはエッジ端末であり、単発のPythonスクリプトではなく、本格的なローカルサーバーを重視する場合に、Lemonadeは魅力的な選択肢になります。

実用的なローカルアーキテクチャ

ローカルAIアプリケーションでうまく機能するパターンを紹介します。

┌──────────────────────────────┐
│ Desktop App / Backend Service │
└───────────────┬──────────────┘
                │ OpenAI-compatible client
                v
┌──────────────────────────────┐
│ Local Model Gateway           │
│ - retries                     │
│ - model routing               │
│ - prompt templates            │
│ - telemetry                   │
└───────────────┬──────────────┘
                │ localhost HTTP
                v
┌──────────────────────────────┐
│ Lemonade Server               │
│ - model loading               │
│ - streaming responses         │
│ - device selection            │
│ - accelerator backend         │
└───────────────┬──────────────┘
                │
       ┌────────┴────────┐
       v                 v
   AMD GPU            Ryzen AI NPU

私は通常、ローカルサーバーがすでに使いやすいAPIを公開していても、薄いゲートウェイ層を追加します。ゲートウェイによって安定した内部インターフェースを確保でき、ローカルモデルとリモートモデルを切り替えられます。

たとえば、次のように考えます。

import os
from openai import OpenAI

def build_llm_client():
    if os.getenv("LLM_MODE") == "local":
        return OpenAI(
            base_url="http://localhost:8000/api/v1",
            api_key="local"
        )

    return OpenAI(
        base_url=os.environ["REMOTE_LLM_BASE_URL"],
        api_key=os.environ["REMOTE_LLM_API_KEY"]
    )

client = build_llm_client()

そうすれば、アプリのロジックはリクエストの送信先がLemonade、GPT-5.5、Claude Opus 4.8、Gemini 3、あるいはホストされたオープンモデルのどれであるかを意識せずに済みます。この柔軟性は、品質、レイテンシ、プライバシー、コストを評価する際に役立ちます。チームがクラウドへのフォールバックにAI Prime TechのようなマルチモデルAPIプロバイダーを使っている場合は、ローカル経路とリモート経路を同じインターフェースの背後にまとめてください。

モデル選択:理論上の知能より小ささが重要

ローカルLLMサーバーで最もよくある間違いは、マシンに適しているかではなく、リーダーボードでの見栄えを理由にモデルを選ぶことです。

ローカル推論では、モデルが環境に適合するかどうかがすべてです。

  • 量子化されたモデルは、利用可能なアクセラレータのメモリに収まりますか?
  • コンテキスト長は、そのワークロードにとって現実的ですか?
  • 最初のトークンが出るまでにどれくらいかかりますか?
  • ノートPCの通常の熱条件で、スムーズにストリーミングできますか?
  • アプリは、時折発生するCPUへのフォールバックを許容できますか?
  • そのタスクに十分な品質がありますか?

4ビットに量子化された7Bまたは8Bの指示追従モデルは、頻繁にメモリからあふれたり、停止したり、大幅なコンテキスト削減を強いたりする大きなモデルより、ローカル環境で役立つことがよくあります。コーディングタスクでは、ライセンス、対応形式、バックエンドとの互換性に応じて、Qwen、DeepSeek、Llama、MiniMax、Kimi由来のオープンモデルがいずれも妥当な候補になり得ます。

適切なワークフローは、実測に基づくものです。

# Example benchmark flow; adapt names to installed Lemonade commands/models.
lemonade-server pull llama-3.1-8b-instruct-q4
lemonade-server serve --host 127.0.0.1 --port 8000

次に、再現可能なプロンプトを実行します。

import time
from openai import OpenAI

client = OpenAI(base_url="http://localhost:8000/api/v1", api_key="local")

prompt = "Explain how an LRU cache works, then implement one in Python."

start = time.perf_counter()
first_token_at = None
tokens = 0

stream = client.chat.completions.create(
    model="llama-3.1-8b-instruct-q4",
    messages=[{"role": "user", "content": prompt}],
    stream=True,
    temperature=0.1,
    max_tokens=400,
)

for chunk in stream:
    text = chunk.choices[0].delta.content or ""
    if text and first_token_at is None:
        first_token_at = time.perf_counter()
    tokens += len(text.split())

end = time.perf_counter()

print({
    "time_to_first_token_s": round(first_token_at - start, 3),
    "total_time_s": round(end - start, 3),
    "rough_word_rate_per_s": round(tokens / (end - start), 2)
})

これは科学的に完璧なベンチマークではありません。実用的なスモークテストです。ユーザーがどのように感じるかを把握できます。

デバイス選択とフォールバック

優れたローカルサーバーでは、エンジニアが障害の原因を考えられる程度に、デバイス選択が明示されているべきです。「自動」は便利ですが、モデルが知らないうちにGPUからCPUに移り、レイテンシが8xも跳ね上がると問題になります。

本番に近いローカルデプロイでは、私は平凡で確認しやすい設定を好みます。

{
  "server": {
    "host": "127.0.0.1",
    "port": 8000
  },
  "models": [
    {
      "name": "local-chat",
      "path": "./models/llama-3.1-8b-instruct-q4",
      "device": "gpu",
      "context_length": 8192
    },
    {
      "name": "fast-draft",
      "path": "./models/small-instruct-npu",
      "device": "npu",
      "context_length": 4096
    }
  ],
  "fallback": {
    "allow_cpu": false,
    "on_failure": "return_error"
  }
}

Lemonadeで利用できる具体的な設定項目はバージョンによって異なる場合がありますが、原則は変わりません。見えない性能低下を避けることです。GPU経路が失敗したら、アプリ側でそれを把握できるようにしたいと考えています。対話型アプリケーションでは、CPUからの応答を90秒待たせるより、明確なエラーを返すほうがよい場合がよくあります。

よくある落とし穴として、ドライバーやランタイムライブラリはモデルと同じくらい重要です。GPUやNPUによる高速化が有効になっていなくても、サーバーは遅いながらも「動作」することがあります。起動時のログで、有効な実行プロバイダーを必ず確認してください。

有用な起動時チェック:

lemonade-server serve --log-level debug

確認する項目:

loaded model: local-chat
selected device: gpu
execution provider: <accelerated backend>
context length: 8192
quantization: q4

ログにアクセラレータ経路が明確に示されていない場合は、高速化をまだ検証できていないと考えてください。

ストリーミングは必須

ローカルLLMのUXにおいて、ストリーミングは必須です。完了までの総時間が変わらなくても、ユーザーが進捗を見られるため、アプリの体感速度は大きく向上します。

サーバーはトークンのストリーミングをサポートし、クライアントはそれを正しく処理する必要があります。

def stream_answer(client, model, messages):
    stream = client.chat.completions.create(
        model=model,
        messages=messages,
        stream=True,
        temperature=0.2,
        max_tokens=700,
    )

    for event in stream:
        piece = event.choices[0].delta.content
        if piece:
            yield piece

実際には、ストリーミングはデバッグにも役立ちます。最初のトークンまでのレイテンシが高く、その後の生成が速い場合は、モデルの読み込み、プロンプト処理、コンテキスト長がボトルネックになっている可能性があります。最初のトークンはすぐに届くのに生成が遅い場合は、メモリ帯域幅、サーマルスロットリング、CPUへのフォールバックが原因かもしれません。

次の項目を個別に測定してください。

  • コールドスタート時間:サーバーの起動からモデルの準備完了まで。
  • プロンプト処理時間:リクエストの受信から最初のトークンまで。
  • デコード速度:最初のトークンから最後のトークンまで。
  • エンドツーエンドのレイテンシ:アプリケーションのリクエストから応答全体の受信まで。
  • 熱的安定性:使用開始から五分後に同じテストを実施。

最後の項目は見過ごされやすく、実際の製品では深刻な問題になります。

コンテキスト長:気づかないうちにレイテンシを増幅する要因

長いコンテキストウィンドウは便利ですが、コストがかからないわけではありません。8192トークンまたは32768トークンのコンテキストに対応するモデルでも、すべてのリクエストにログ、ドキュメント、チャット履歴を詰め込むと、使い勝手が悪くなることがあります。

ローカルシステムでは、私は通常、ゲートウェイでコンテキストの上限を強制します。

MAX_PROMPT_CHARS = 18_000

def trim_context(system_prompt, history, new_user_message):
    packed = [
        {"role": "system", "content": system_prompt},
        *history[-8:],
        {"role": "user", "content": new_user_message},
    ]

    total = sum(len(m["content"]) for m in packed)
    while total > MAX_PROMPT_CHARS and len(packed) > 2:
        removed = packed.pop(1)
        total -= len(removed["content"])

    return packed

これは粗い方法ですが、効果的です。より成熟した実装では、トークンのカウント、意味を保った圧縮、検索を使います。重要なのは、コンテキスト管理をアプリケーション層の責務とし、サーバーが高速なままでいてくれるという漠然とした期待に任せないことです。

クラウド環境で、1Mのコンテキストを持つFable 5のような大規模コンテキストモデルを使う場合は、設計上の制約が変わります。ローカルのGPU/NPUシステムでは、測定で別の結果が得られない限り、コンテキストのコストは高いと考えるべきです。

Lemonadeのようなローカルモデル提供のベストプラクティス

サーバーを準備済みの状態に保つ

コールド状態からのモデル読み込みは、UXを損なう大きな要因のひとつです。アプリとともにサーバーを起動し、デフォルトモデルをあらかじめ読み込み、プロセスの正常性とは別に準備完了状態を公開してください。

curl http://127.0.0.1:8000/health
curl http://127.0.0.1:8000/models

アプリでは、次の状態を区別する必要があります。

  • サーバープロセスが実行中
  • モデルの読み込みが完了
  • アクセラレータが有効
  • リクエスト経路が正常

モデルのバージョンを固定する

デスクトップアプリの背後で「最新」モデルが知らないうちに変わることがないようにしてください。モデル名、量子化、想定するコンテキスト長を固定します。

{
  "model": "llama-3.1-8b-instruct-q4",
  "revision": "2025-02-local-tested",
  "expected_device": "gpu"
}

トークナイザー、チャットテンプレート、量子化のわずかな変更でも、動作が変わることがあります。

品質テストと速度テストを分ける

多くのワークフローでは、微妙に誤った回答を返す高速なローカルモデルは、遅いモデルよりも問題になります。私は、品質用と速度用のテストセットをそれぞれ維持することを好みます。

evals/
  latency_prompts.jsonl
  task_quality_prompts.jsonl

品質テスト項目の例:

{
  "id": "python_exception_summary_01",
  "messages": [
    {
      "role": "user",
      "content": "Summarize this stack trace and identify the likely bug: ..."
    }
  ],
  "must_include": ["None", "attribute", "guard"],
  "must_not_include": ["database outage"]
}

これは正式な評価の代わりにはなりませんが、モデル、量子化、デバイス経路を変更した際のリグレッションを検出できます。

リモートへのフォールバックを設計する

ローカル推論は、ノートPCの省電力モード、ドライバー更新、モデルファイルの欠落、未対応の量子化、サーマルスロットリングといったありふれた原因で失敗します。重要なタスクなら、フォールバックを設計してください。

try:
    answer = call_local_model(messages, timeout_s=20)
except Exception:
    answer = call_remote_model(messages, timeout_s=30)

プライバシーの扱いを明確にしてください。マシンの外に出してはいけないプロンプトもあります。その場合は、安全を優先して処理を停止してください。

制限とトレードオフ

Lemonadeの強みが最も発揮されるのは、適切なランタイムスタックを備えた対応AMDシステムです。これは、運用で考慮すべき範囲に、ハードウェアの機能、ドライバーの状態、モデル形式の対応状況、アクセラレータバックエンドの成熟度も含まれることを意味します。

主なトレードオフ:

  • 移植性:AMDを中心とした高速化は有用ですが、異種混在のマシン群では複数のバックエンドが必要になる場合があります。
  • モデルの利用可能性:すべてのオープンモデルが、すべての実行経路向けにパッケージ化または最適化されているわけではありません。
  • NPUの制約:NPUは効率的な推論に優れていますが、あらゆるモデル構造やコンテキスト長に適しているわけではありません。
  • 可観測性:ローカルサーバーにも、クラウドサービスと同様にログとメトリクスが必要です。
  • 同時実行:開発者のノートパソコンは、マルチテナントの推論クラスターではありません。

率直に言えば、Lemonadeが魔法のように処理能力を増やすわけではありません。限られたクライアントハードウェアの上で、より優れたローカル配信レイヤーを提供するものです。性能向上は、適切なモデル、適切な量子化、適切なアクセラレーター、そしてそれらの詳細をアプリケーションから隠蔽するサーバーAPIを使うことで得られます。

実践上のポイント

  • 推論をアプリに直接組み込むのではなく、AMDによる高速化を活用するローカルLLMサーバーが必要な場合は、Lemonadeを使用してください。
  • GPU、NPU、CPUは、レイテンシー、消費電力、モデルへの適合性がそれぞれ異なる実行ターゲットとして扱ってください。
  • かろうじて収まる大規模モデルよりも、対話に適したレイテンシーを安定して実現できる小型の量子化モデルを優先してください。
  • ログで高速化が有効か確認してください。「動作している」サーバーでも、CPUで実行されている可能性があります。
  • レスポンスをストリーミングし、最初のトークンが返るまでの時間を測定し、継続的な熱負荷の下でテストしてください。
  • ルーティング、フォールバック、プロンプトの切り詰め、プライバシーポリシーは、アプリケーションゲートウェイに組み込んでください。
  • ローカルAI機能をリリースする前に、モデルのバージョンを固定し、レイテンシーと品質を評価する小規模なデータセットを維持してください。
A

このブログはAI Prime Techが公開しています。記事の草稿はAIの支援を受けて作成されています。モデルID、料金、制限など、バージョンによって異なる詳細については、利用の判断に用いる前に公式ドキュメントで確認してください。

Claude APIをより低価格で利用

Claude Opus 5.5、Sonnet 5、Haiku 4.5、Fable 5.1に加え、GPT-6のモデルを、単一のAPIキーで利用できます。従量課金制で、サブスクリプションは不要です。

APIキーを取得 →
AI Prime Techは独立したサードパーティのAPIゲートウェイです。Claude™およびAnthropic®はAnthropic, PBCの商標です。提携や推奨を示唆するものではありません。