WebLLM を使用してローカルおよびオフライン対応の chatbot を作成する

公開日: 2024 年 1 月 13 日

これは、LLM と chatbot に関する 3 部構成のシリーズの第 2 回です。前回の記事では、オンデバイス LLM とブラウザ内 LLM のメリットとデメリットについて説明しました。

クライアントサイド AI について理解が深まったところで、ToDo リスト ウェブ アプリケーションに WebLLM を追加しましょう。コードは GitHub リポジトリの web-llm ブランチにあります。

WebLLM は、ML コンパイルが提供する LLM 用のウェブベースのランタイムです。WebLLM をスタンドアロン アプリケーションとして試すことができます。このアプリケーションは、Gemini などのクラウド バックアップのチャット アプリケーションにヒントを得ていますが、LLM 推論はクラウドではなくデバイスで実行されます。プロンプトとデータはデバイスから送信されることはなく、モデルのトレーニングに使用されることはありません。

デバイスでモデル推論を実行するため、WebLLM は WebAssemblyWebGPU を組み合わせています。WebAssembly では中央処理装置(CPU)で効率的な計算が可能ですが、WebGPU ではデバイスのグラフィック プロセッシング ユニット(GPU)に低レベルでアクセスできます。

Browser Support

  • Chrome: 113.
  • Edge: 113.
  • Firefox Technology Preview: supported.
  • Safari Technology Preview: supported.

Source

WebLLM をインストールする

WebLLM は npm パッケージとして利用できます。このパッケージを ToDo リスト アプリケーションに追加するには、npm install @mlc-ai/web-llm を実行します。

モデルの選択

次に、ローカルで実行する LLM を決定する必要があります。さまざまなモデルが用意されています。

決定するには、次の主な用語と数字を把握しておく必要があります。

  • トークン: LLM が処理できるテキストの最小単位。
  • コンテキスト ウィンドウ: モデルが処理できるトークンの最大数。
  • パラメータまたは重み: トレーニング中に学習された内部変数。数十億単位でカウントされます。
  • 量子化: 重みを表すビット数。ビット数が多いほど精度は高くなりますが、メモリ使用量も増加します。
  • 浮動小数点数形式: 32 ビットの浮動小数点数(フル精度、F32)は精度が高く、16 ビットの浮動小数点数(半精度、F16)は速度が速くメモリ使用量が少ない一方で、互換性のあるハードウェアが必要です。

これらのキーワードは、モデル名の一部になる傾向があります。たとえば、Llama-3.2-3B-Instruct-q4f32_1-MLC には次の情報が含まれます。

  • モデルは LLaMa 3.2 です。
  • このモデルには 30 億のパラメータがあります。
  • 指示やプロンプト スタイルのアシスタント(Instruct)向けにファインチューニングされています。
  • 4 ビット(q4)均一(_1)量子化を使用します。
  • フル精度の 32 ビット浮動小数点数を使用します。
  • これは、ML コンパイルで作成された特別なバージョンです。

ユースケースに適したモデルを判断するには、さまざまなモデルをテストする必要があります。

30 億個のパラメータとパラメータあたり 4 ビットのモデルの場合、この記事の執筆時点ではファイルサイズが 1.4 GB にも達する可能性があります。このモデルを初めて使用する前に、アプリでユーザーのデバイスにダウンロードする必要があります。30 億個のモデルでも動作しますが、翻訳機能や雑学知識に関しては、70 億個のモデルのほうが優れた結果が得られます。3.3 GB 以上の場合は、サイズが大幅に大きくなります。

WebLLM エンジンを作成して、ToDo リスト チャットボットのモデルのダウンロードを開始するには、次のコードをアプリケーションに追加します。

import {CreateMLCEngine} from '@mlc-ai/web-llm';
const engine = await CreateMLCEngine('Llama-3.2-3B-Instruct-q4f32_1-MLC', {
  initProgressCallback: ({progress}) =>  console.log(progress);
});

CreateMLCEngine メソッドは、モデル文字列とオプションの構成オブジェクトを受け取ります。initProgressCallback メソッドを使用して、モデルのダウンロード プログレスをクエリし、ユーザーが待機している間に表示できます。

Cache API: LLM をオフラインで実行する

モデルはウェブサイトのキャッシュ ストレージにダウンロードされます。Cache API は、ウェブサイトやウェブ アプリケーションをオフラインで実行できるようにするために、Service Workers とともに導入されました。これは、AI モデルをキャッシュに保存するための最適なストレージ メカニズムです。HTTP キャッシュとは対照的に、Cache API はデベロッパーが完全に制御できるプログラム可能なキャッシュです。

ダウンロードが完了すると、WebLLM はネットワーク経由でモデルファイルをリクエストするのではなく、Cache API からモデルファイルを読み取るため、WebLLM は完全にオフラインで動作できるようになります。

すべてのウェブサイト ストレージと同様に、キャッシュはオリジンごとに分離されます。つまり、2 つのオリジン(example.comexample.net)が同じストレージを共有することはできません。これらの 2 つのウェブサイトで同じモデルを使用する場合は、モデルを別々にダウンロードする必要があります。

DevTools を使用してキャッシュを検査するには、[Application] > [Storage] に移動して、キャッシュ ストレージを開きます。

会話を設定する

モデルは、一連の初期プロンプトで初期化できます。一般に、メッセージのロールには次の 3 つがあります。

  • システム指示: この指示は、モデルの動作、役割、キャラクターを定義します。また、グラウンドングにも使用できます。つまり、トレーニング セットに含まれないカスタムデータ(ドメイン固有のデータなど)をモデルにフィードします。指定できるシステム プロンプトは 1 つだけです。
  • ユーザー プロンプト: ユーザーが入力したプロンプト。
  • アシスタント プロンプト: アシスタントからの回答(省略可)。

ユーザー プロンプトとアシスタント プロンプトは、LLM の動作や応答方法に関する自然言語の例を LLM に提供することで、N ショット プロンプトに使用できます。

以下に、ToDo リスト アプリの会話を設定する最小限の例を示します。

const messages = [
  { role: "system",
    content: `You are a helpful assistant. You will answer questions related to
    the user's to-do list. Decline all other requests not related to the user's
    todos. This is the to-do list in JSON: ${JSON.stringify(todos)}`
  },
  {role: "user", content: "How many open todos do I have?"}
];

最初の質問に回答する

チャット完了機能は、前に作成した WebLLM エンジンのプロパティとして公開されます(engine.chat.completions)。モデルがダウンロードされたら、このプロパティで create() メソッドを呼び出してモデル推論を実行できます。このユースケースでは、レスポンスをストリーミングして、生成中にユーザーが読み取りを開始できるようにし、待ち時間の認識を短縮します。

const chunks = await engine.chat.completions.create({  messages,  stream: true, });

このメソッドは、非表示の AsyncIterator クラスのサブクラスである AsyncGenerator を返します。for await...of ループを使用して、チャンクが届くのを待ちます。ただし、レスポンスには新しいトークン(delta)のみが含まれるため、完全なレスポンスを自分で組み立てる必要があります。

let reply = '';

for await (const chunk of chunks) {
  reply += chunk.choices[0]?.delta.content ?? '';
  console.log(reply);
}

ウェブでは常にストリーミング レスポンスを処理する必要がありました。DOMImplementation などの API を使用して、これらのストリーミング レスポンスを処理し、HTML を効率的に更新できます。

結果は純粋に文字列ベースです。JSON または他のファイル形式として解釈するには、まず解析する必要があります。

ただし、WebLLM にはいくつかの制限があります。アプリケーションは初回使用前に巨大なモデルをダウンロードする必要がありますが、このモデルはオリジン間で共有できないため、別のウェブアプリが同じモデルを再度ダウンロードしなければならない場合があります。WebGPU はネイティブに近い推論パフォーマンスを達成しますが、ネイティブの速度には達しません。

デモ

これらの欠点は、Google が提案した探索 API である Prompt API によって解消されます。この API はクライアントサイドで実行されますが、Chrome にダウンロードされた一元化されたモデルを使用します。つまり、複数のアプリケーションが同じモデルを最大実行速度で使用できます。

詳しくは、次の記事の Prompt API を使用した chatbot 機能の追加をご覧ください。