Node.jsで実現するLLMの新しい可能性 〜llama2をTypeScriptで呼び出す方法〜
はじめに
先日、Python 環境(と言いつつ、C++でビルドしたやつを呼び出すライブラリ)で meta が作って公開してる LLM モデルの llama2 をローカルで動かしてみた
TypeScript でもできるのかなと思って調べてみた
ちなみに、本家は PyTorch を使うっぽいけど、いまいち使い方わからない・・・。Mac で動くんやろか?
調べると、node-llama-cpp ってのを作ってる人がいた
llama.cpp って、llama を C++で動くようにしたやつを Node.js から呼べるようにしたもの
さらに、langchain.js からも呼び出せるみたい
llama.cpp
まず、llama.cpp は Apple silicon で使えることを目的としてて、Apple シリコンの GPU を使うための Apple 版 CUDA の Metal に対応してくれている。すごい。
Apple silicon is a first-class citizen - optimized via ARM NEON, Accelerate and Metal frameworks
Metal 部分を使ってるコードはここっぽいけど、これ以上突き進む気力はなかった。
ちなみに、ファイル名にもある GGML ってのが LLM のフォーマットらしい。ただ、llama.cpp は GGML じゃなくて GGUF 使うようになったみたい。
GGUF は、GGML Universal Format らしい。
モデルは llama 派生モデルがたくさんあって、それは huggingface 上でたくさん提供されているので、気になるものを取得したら ok
node-llama-cpp 試してみた
簡単だった
node-llama-cpp が、プリビルドされたバイナリも提供してくれているみたい
ビルドする方法も提供してくれているので楽ちん
こんな感じで、node-llama-cpp
は使える。ここでは、ELYZA の日本語モデルを使ってみた。
import { fileURLToPath } from "url";
import path from "path";
import { LlamaModel, LlamaContext, LlamaChatSession } from "node-llama-cpp";
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const model = new LlamaModel({
modelPath: path.join(
__dirname,
"models",
"ELYZA-japanese-Llama-2-7b-fast-instruct-q4_K_M.gguf"
),
});
const context = new LlamaContext({ model });
const session = new LlamaChatSession({ context });
const q1 = "元気?";
console.log("User: " + q1);
const a1 = await session.prompt(q1);
console.log("AI: " + a1);
langchain 試してみた
こっちも簡単
import { LlamaCpp } from "@langchain/community/llms/llama_cpp";
const llamaPath =
"/Users/hoge/hello-node-llama-cpp/models/ELYZA-japanese-Llama-2-7b-fast-instruct-q4_K_M.gguf";
const model = new LlamaCpp({ modelPath: llamaPath });
const question = "ラマって何?";
console.log(`You: ${question}`);
const response = await model.invoke(question);
console.log(`AI : ${response}`);
Chat model もこんな感じで stream 出力できて楽ちん。
import { ChatLlamaCpp } from "@langchain/community/chat_models/llama_cpp";
const llamaPath =
"/Users/hoge/hello-node-llama-cpp/models/ELYZA-japanese-Llama-2-7b-fast-instruct-q4_K_M.gguf";
const model = new ChatLlamaCpp({ modelPath: llamaPath, temperature: 0.7 });
const stream = await model.stream("ラマって何?");
for await (const chunk of stream) {
console.log(chunk.content);
}
コードを見てみた
node-llama-cpp で、どうやって、llama.cpp を呼び出しているか気になったのでコードを見てみた
どうも、llama/addon.cpp
で Node-API ってのを使って呼び出せるようにしているみたい。
ビルドしたものは require で呼び出せるようになるみたい
langchain.js の方は単に node-llama-cpp を呼び出しているだけみたい
llama.cpp は ggml-metal.m あたりがそれっぽい処理書かれてそう
Makefile も環境によってビルドするものが変わるから興味深い感じ。Mac だと 以下のように Metal 環境のそれっぽいビルドの依存関係が書かれてる。
ifdef LLAMA_METAL
MK_CPPFLAGS += -DGGML_USE_METAL
MK_LDFLAGS += -framework Foundation -framework Metal -framework MetalKit
OBJS += ggml-metal.o
ifdef LLAMA_METAL_NDEBUG
MK_CPPFLAGS += -DGGML_METAL_NDEBUG
endif
endif # LLAMA_METAL
ifdef LLAMA_METAL
ggml-metal.o: ggml-metal.m ggml-metal.h
$(CC) $(CFLAGS) -c $< -o $@
endif # LLAMA_METAL
サクッとしか書いてないけど、よくわからなかったので、chatgpt に聞きながら読んでた(そこは llama に聞けよ)
補足
llama.cpp は他の言語でも呼び出せるように色々取り組みがあるみたい。すごい。
自分の PC で動かしたいだけなら、Ollama がコマンドラインでいい感じに生成できるし、API サーバーっぽくも動くのでおすすめ
おわりに
- llama すごい
- llama.cpp すごい
- Node-API すごい
- 生成 AI は 別に Python じゃなくてもいんじゃね?
Discussion