見出し画像

5分もかからず Llama2 を使える Ollama を Macbook で試す

#雑記

JavaScript (Bun) で Ollama で Llama2 を動かしたので、試したことをまとめる


Ollama とは

Ollama は、ローカル環境で Llama2 などのLLMを使えるようにするツール。Go で書かれている

Get up and running with large language models, locally.
Run Llama 2, Code Llama, and other models. Customize and create your own.

https://ollama.com/


何ができるのかはこの記事が詳しい


LangChain が2023年12月に発表した調査「LangChain State of AI 2023」によると、OSSモデルを使う際に利用されたプロバイダとして llama.cpp を抑えて 3 位になっている。

https://blog.langchain.dev/langchain-state-of-ai-2023/


Ollama 公式がJSライブラリ (と Python) を提供している


環境

私は普段 JavaScript に親しみがあるため、今回はJSで試してみた。なお、ランタイムは Bun (Webkit) を利用

環境は MacBook Pro 16-inch, 2021 (Apple M1 Max, Memory 64 GB, macOS Sonoma 14.4.1)


準備


事前準備として、https://ollama.com/ で Mac用のアプリケーションを落としてインストールする。

CLI でチャットするには `ollama run llama2` を実行するだけ。モデルのダウンロードが数分かかるが、終わったらチャットできるようになる。この間、5分とかからない。

❯ ollama run llama2
>>> Why is the sky blue?

The sky appears blue because of a phenomenon called Rayleigh scattering. This is the scattering of light by small 
particles in the atmosphere, such as molecules of gases like nitrogen and oxygen. These particles act as tiny 
scaffolds, bouncing or reflecting the light in all directions, giving the sky its blue color.

The reason why the sky appears blue instead of other colors has to do with the way that light interacts with the 
atmosphere. When sunlight enters Earth's atmosphere, it encounters tiny molecules of gases like nitrogen and oxygen. 
These molecules are so small that they don't absorb or reflect much light at all, but instead scatter it in all 
directions.

The blue color of the sky is due to the way that shorter wavelengths of light (like blue and violet) are scattered 
more than longer wavelengths (like red and orange). This is known as Rayleigh scattering, named after Lord Rayleigh, 
who first described the phenomenon in the late 19th century.

The amount of scattering that occurs depends on the size of the particles in the atmosphere and the wavelength of the
light. As a result, the blue color of the sky appears to be more intense during the daytime when the sunlight is more
direct and there are fewer particles in the air to scatter it. At sunrise and sunset, when the sun's rays pass 
through more of the Earth's atmosphere, the blue color can appear more muted or reddened due to the additional 
scattering of light by larger particles like dust and water droplets.

So, in short, the sky appears blue because of the way that light interacts with the small molecules of gases in the 
atmosphere, leading to a preferential scattering of shorter wavelengths of light and giving the sky its 
characteristic blue color.

>>> /?
Available Commands:
  /set            Set session variables
  /show           Show model information
  /load <model>   Load a session or model
  /save <model>   Save your current session
  /bye            Exit
  /?, /help       Help for a command
  /? shortcuts    Help for keyboard shortcuts

Use """ to begin a multi-line message.

>>> Send a message (/? for help)

`ollama run llama2` や `ollama pull llama2` でDLできるのは4ビット量子化された Llama2 7B。

By default, Ollama uses 4-bit quantization.

https://ollama.com/library/llama2


他のパラメータ数や量子化のモデルを利用したい場合、タグを変えることで利用できる。詳しくは https://ollama.com/library/llama2/tags などを参考にすると良さそう

ollama run llama2:70b


後はプロジェクトを立ち上げて oolama ライブラリをインストール

bun init
bun install ollama


JS (Bun) で実行

最小コードは以下。

import ollama from "ollama";

const response = await ollama.chat({
  model: "llama2",
  messages: [{ role: "user", content: "Why is the sky blue?" }],
});
console.log(response.message.content);


ストリーミングをやりたいならこう

const response = await ollama.chat({
  model: "llama2",
  messages: [{ role: "user", content: "Why is the sky blue?" }],
  stream: true,
});

for await (const part of response) {
  process.stdout.write(part.message.content);
}


逐次出力される


以下のようなコードでストリーミングのレスポンスを見てみる

const response = await ollama.chat({
  model: "llama2",
  messages: [{ role: "user", content: "Why is the sky blue?" }],
  stream: true,
});

const chatResponses: ChatResponse[] = []
for await (const part of response) {
  chatResponses.push(part);
  process.stdout.write(part.message.content);
}

console.log("chatResponses.length:", chatResponses.length);
console.log("first chatResponses:", chatResponses.at(0));
console.log("last chatResponses:", chatResponses.at(-1));

実行結果

❯ bun run --bun src/index.ts

The sky appears blue because of a phenomenon called Rayleigh scattering. When sunlight enters Earth's atmosphere, it encounters tiny molecules of gases such as nitrogen and oxygen. These molecules scatter the light in all directions, but they scatter shorter (blue) wavelengths more than longer (red) wavelengths. This is known as Rayleigh scattering.

As a result of this scattering, the blue light is distributed throughout the atmosphere, giving the sky its blue appearance. The red light, on the other hand, is absorbed by the atmosphere and heats up, causing it to rise and create sunsets and sunrises.

It's worth noting that the color of the sky can vary depending on the time of day and atmospheric conditions. For example, during sunrise and sunset, the sky can take on hues of red, orange, and pink due to the scattering of light by larger particles in the atmosphere. Additionally, pollution and dust in the atmosphere can also affect the color of the sky, making it appear more hazy or yellowish.
 -------------------------------------------------- 

chatResponses.length: 243

 -------------------------------------------------- 

first chatResponses: {
  model: "llama2",
  created_at: "2024-03-29T06:54:42.721979Z",
  message: {
    role: "assistant",
    content: "\n",
  },
  done: false,
}

 -------------------------------------------------- 

last chatResponses: {
  model: "llama2",
  created_at: "2024-03-29T06:54:47.175066Z",
  message: {
    role: "assistant",
    content: "",
  },
  done: true,
  total_duration: 4632502500,
  load_duration: 1636375,
  prompt_eval_duration: 177175000,
  eval_count: 243,
  eval_duration: 4452922000,
}

Claude3 による解説


このオブジェクトには、以下のような情報が含まれています:

  1. model: 使用されたモデルの名前 ("llama2")

  2. created_at: レスポンスが生成された日時 ("2024-03-29T05:50:03.72272Z")

  3. message: アシスタントの返答を表すオブジェクト

    • role: メッセージの役割 ("assistant")

    • content: 空の文字列 ("")

  4. done: ストリーミングが完了したことを示すブール値 (true)

  5. total_duration: 全処理の所要時間をナノ秒で表した値 (7467276167ナノ秒 ≒ 7.467秒)

  6. load_duration: モデルのロードにかかった時間をナノ秒で表した値 (605590750ナノ秒 ≒ 0.606秒)

  7. prompt_eval_count: プロンプトの評価回数 (26回)

  8. prompt_eval_duration: プロンプトの評価にかかった時間をナノ秒で表した値 (88166000ナノ秒 ≒ 0.088秒)

  9. eval_count: 全評価回数 (377回)

  10. eval_duration: 全評価にかかった時間をナノ秒で表した値 (6772994000ナノ秒 ≒ 6.773秒)

これらの情報は、ストリーミングレスポンスの最後に送られてくるメタデータで、チャットの生成プロセスに関する詳細な統計情報を提供しています。


(ここまでClaude3 による解説)

📝 chatResponses.length と eval_count がどちらも 243 であった。


JSON フォーマットのレスポンスを検証

以下のようにしてレスポンスをJSON形式に指定できる。2024/03/29 時点では "json" しか指定できないとのこと

const response = await ollama.chat({
  model: "llama2",
  messages: [{ role: "user", content: "Why is the sky blue?" }],
  format: "json",
  stream: true,
});

3回試行した結果を出力のまま記載。全て key がバラバラだったがJSONとしては成り立っている。

{
"type": "answer",
"topLevel": true,
"id": "1",
"text": "The sky appears blue because of a phenomenon called Rayleigh scattering. When sunlight enters Earth's atmosphere, it encounters tiny molecules of gases such as nitrogen and oxygen. These molecules scatter the light in all directions, but they scatter shorter (blue) wavelengths more than longer (red) wavelengths. This is known as Rayleigh scattering. As a result, the blue light is dispersed throughout the atmosphere, giving the sky its blue appearance."
}
{ "@type": "Answer", "description": "The sky appears blue because of a phenomenon called Rayleigh scattering. When sunlight enters Earth's atmosphere, it encounters tiny molecules of gases such as nitrogen and oxygen. These molecules scatter the light in all directions, but they scatter shorter (blue) wavelengths more than longer (red) wavelengths. This is known as Rayleigh scattering. As a result, the blue light is dispersed throughout the atmosphere, giving the sky its characteristic blue color.", "id": "8352", "name": "Why is the sky blue?" }
{
"data": [
{
"text": "The sky appears blue because of a phenomenon called Rayleigh scattering. When sunlight enters Earth's atmosphere, it encounters tiny molecules of gases such as nitrogen and oxygen. These molecules scatter the light in all directions, but they scatter shorter (blue) wavelengths more than longer (red) wavelengths. This is known as Rayleigh scattering.",
"id": "1",
"images": [
{
"url": "https://upload.wikimedia.org/wikipedia/commons/thumb/7/7c/Sun_%281964%29_-_Rayleigh_scattering.png/500px-Sun_%281964%29_-_Rayleigh_scattering.png",
"alt": "Rayleigh scattering of sunlight"
}
]
}
],
"explanation": [
{
"text": "The blue color of the sky is due to a phenomenon called Rayleigh scattering, which occurs when sunlight enters Earth's atmosphere. The light encounters tiny molecules of gases such as nitrogen and oxygen, which scatter the light in all directions. However, these molecules scatter shorter (blue) wavelengths more than longer (red) wavelengths, which is why the sky appears blue.",
"id": "2",
"images": [
{
"url": "https://upload.wikimedia.org/wikipedia/commons/thumb/7/7c/Sun_%281964%29_-_Rayleigh_scattering.png/500px-Sun_%281964%29_-_Rayleigh_scattering.png",
"alt": "Rayleigh scattering of sunlight"
}
]
}
]
}%                                                                                                                         


フォーマット指定の生成結果への影響を概観

フォーマット指定がレスポンスタイムや評価回数に及ぼす影響を軽く調べる

雑だがこのようなコードを用意した


const nanoToSeconds = (nano: number) => (nano / 1e9).toFixed(2);

console.log(
  "| No. | format | content_length | eval_count | prompt_eval_count | total_duration | load_duration | prompt_eval_duration | eval_duration |"
);
console.log(
  "| --- | ------ | -------------- | ---------- | ----------------- | -------------- | ------------- | -------------------- | ------------- |"
);
const outputResult = (i: number, format: string, response: ChatResponse) =>
  console.log(
    `| ${i} | ${format} | ${response.message.content.length} | ${
      response.eval_count
    } | ${response.prompt_eval_count} | ${nanoToSeconds(
      response.total_duration
    )} | ${nanoToSeconds(response.load_duration)} | ${nanoToSeconds(
      response.prompt_eval_duration
    )} | ${nanoToSeconds(response.eval_duration)} |`
  );

const iteration = 20;

for (let i = 1; i < iteration / 2 + 1; i++) {
  const response = await ollama.chat({
    model: "llama2",
    messages: [{ role: "user", content: "Why is the sky blue?" }],
  });
  outputResult(i, "chat", response);
}

for (let i = iteration / 2 + 1; i < iteration + 1; i++) {
  const response = await ollama.chat({
    model: "llama2",
    messages: [{ role: "user", content: "Why is the sky blue?" }],
    format: "json",
  });
  outputResult(i, "json", response);
}

雰囲気分かれば良かったので試行回数は 66 回。

全体の集計結果は以下。各 duration は秒換算 (正直 nanoseconds のまま取得したら良かった。失敗した)。

トークン数は取得できなかったため文字数になってしまうが、中央値922文字を中央値4.23秒で生成している。体感では ChatGPT の GPT-3.5 よりは遅いが GPT-4 や Claude3 よりは早いと感じた。

ただ format: json のものと通常の結果を混ぜた集計のため、あまり当てにならない数字であろう。



format 別は以下。それぞれ20件、46件の集計。

format: json の方が文字数は少ない傾向があるようにも思えるが、JSONの平均値の約791.35と、44%も差があるため大分ばらつきがあるようだ。

トークン値が取れなかったため文字数あたりの生成時間の中央値は、以下になった

chat: 0.00387
json: 0.00601

json の方が 1.5 倍以上の時間を要したようにも見える。JSON の場合のトークンの取り扱いにも依りそうなので「見える」だけの数字だが。


度々遭遇したエラー

フォーマット json 指定で以下のエラーが度々起きた (体感では20回中1回は必ず起こるような頻度)

204 |         throw new Error("Did not receive done or success response in stream.");
205 |       }();
206 |     } else {
207 |       const message = await itr.next();
208 |       if (!message.value.done && message.value.status !== "success") {
209 |         throw new Error("Expected a completed response.");
                    ^
error: Expected a completed response.
      at /Users/yoshikouki/src/github.com/yoshikouki/hakoniwa/node_modules/ollama/dist/shared/ollama.be8fd0da.mjs:209:15

また、たまに TimeoutError: The operation timed out も起こった。こちらは頻繁に再現しなかったため定かではないが、 format: json の時にしか起こっていないようだ


今後試したいこと

今の私はLLMを使ってJSONで出力するところに興味が強いため、以下を今後試していきたい

  • JSONの出力結果に対する客観的な指標を探したい

  • プロンプトでJSONの制御がどこまでできるか

  • パラメタ数や量子化によって生成結果にどのような影響があるか

  • 4070 Ti SUPER を使った場合との比較

  • 70Bが動くか


いいなと思ったら応援しよう!