NetBSD/amd64でllama.cppを使ってみる

はじめに

生成AIが私の知りたいことに答えてくれたことはないのだが、手元の端末のみで動くのであれば、遊ぶのには良いかもしれない。 pkgsrcには含まれていないが、pkgsrc-wipには、llama.cppが含まれている。 llama.cppのうち、C++で書かれた部分は正常にビルドでき、動くようである。 モデルのフォーマットを変換するためのPythonスクリプトは、 いろいろと現在のpkgsrcに含まれていないパッケージが必要であり動かない。 ただ、現在のllama.cppで扱うことのできるGGUF形式のモデルファイルをそのままダウンロードすることができるので、 使ってみるには、Pythonスクリプトは不要である。

どのモデルが動かせるか?

ChatGPTのようなチャットのできるモデルを使ってみたいと考えている。 動かそうとしているPCは、CPUとしてAMD Ryzen 7 5800Uを搭載した RAM 16GBのHP Envy 13である。

用意されているLLaMA 2のモデルでは、70B Q8_0のモデル(llama-2-70b-chat.Q8_0.gguf)が最大であるようだった。 これは、 https://huggingface.co/TheBloke/Llama-2-70B-Chat-GGUF によると、76GB程度のRAMが必要ということなので、到底利用することはできない。

13B Q8_0のモデル(llama-2-13b-chat.Q8_0.gguf)は、 https://huggingface.co/TheBloke/Llama-2-13B-chat-GGUF によると、16GB程度のRAMが必要とのことで、 試してみたが、スラッシングを起こして、動作が非常に緩慢であり実用できなかった。

と言うことで、 https://huggingface.co/TheBloke/Llama-2-7B-Chat-GGUF にあるRAMを 7GB弱利用する7B Q4_K_Mのモデル(llama-2-7b-chat.Q4_K_M.gguf)を試してみると 実用的な速度で動作させることができた。 このモデルは「medium, balanced quality - recommended」とされている。

モデルを動かす準備をする

llama.cppをビルドするには、以下のようにすれば良い。

# cd /usr/pkgsrc/wip/llama.cpp
# make install

これで、/usr/pkg/bin/llama-cliというコマンドがインストールされる。

また、自分のホームディレクトリーに、llama.cpp用の作業用ディレクトリーを用意し、 その中に必要なものを置くようにする。

$ mkdir -p ~/llama.cpp/models
$ cd ~/llama.cpp/models
$ ftp https://huggingface.co/TheBloke/Llama-2-7B-Chat-GGUF/resolve/main/llama-2-7b-chat.Q4_K_M.gguf

対話形式で利用したいが、最初に前提条件を毎回手入力するのは面倒である。 以下のような決まり文句を~/llama.cpp/prompt.txtとして用意する。

$ cat ~/llama.cpp/prompt.txt
You (Alice) are a helpful assistant. Please answer a human (User)'s questions.
User: Hello.
Alice:

この決まり文句を最初は以下のように書いていたのだが、なぜか全く反応が返って来なかった。

You (Alice) are a helpful assistant. Please answer a human (User)'s questions.
User: Hello. I am glad to see you.
Alice:

モデルを動かす

llama.cppの配布物に含まれているexamples/chat.shを参考に、 以下のように起動させた。

$ cd ~/llama.cpp
$ llama-cli -m ./models/llama-2-7b-chat.Q4_K_M.gguf -c 512 -b 1024 -n 256 --repeat_penalty 1.0 -t 8 --color -i -r "User:" --in-prefix " " -f prompt.txt
Log start
main: build = 0 (unknown)
main: built with gcc (nb2 20240221) 12.3.0 for x86_64--netbsd
main: seed  = 1718791141
llama_model_loader: loaded meta data with 19 key-value pairs and 291 tensors from ./models/llama-2-7b-chat.Q4_K_M.gguf (version GGUF V2)
llama_model_loader: Dumping metadata keys/values. Note: KV overrides do not apply in this output.
llama_model_loader: - kv   0:                       general.architecture str              = llama
llama_model_loader: - kv   1:                               general.name str              = LLaMA v2
llama_model_loader: - kv   2:                       llama.context_length u32              = 4096
llama_model_loader: - kv   3:                     llama.embedding_length u32              = 4096
llama_model_loader: - kv   4:                          llama.block_count u32              = 32
llama_model_loader: - kv   5:                  llama.feed_forward_length u32              = 11008
llama_model_loader: - kv   6:                 llama.rope.dimension_count u32              = 128
llama_model_loader: - kv   7:                 llama.attention.head_count u32              = 32
llama_model_loader: - kv   8:              llama.attention.head_count_kv u32              = 32
llama_model_loader: - kv   9:     llama.attention.layer_norm_rms_epsilon f32              = 0.000001
llama_model_loader: - kv  10:                          general.file_type u32              = 15
llama_model_loader: - kv  11:                       tokenizer.ggml.model str              = llama
llama_model_loader: - kv  12:                      tokenizer.ggml.tokens arr[str,32000]   = ["<unk>", "<s>", "</s>", "<0x00>", "<...
llama_model_loader: - kv  13:                      tokenizer.ggml.scores arr[f32,32000]   = [0.000000, 0.000000, 0.000000, 0.0000...
llama_model_loader: - kv  14:                  tokenizer.ggml.token_type arr[i32,32000]   = [2, 3, 3, 6, 6, 6, 6, 6, 6, 6, 6, 6, ...
llama_model_loader: - kv  15:                tokenizer.ggml.bos_token_id u32              = 1
llama_model_loader: - kv  16:                tokenizer.ggml.eos_token_id u32              = 2
llama_model_loader: - kv  17:            tokenizer.ggml.unknown_token_id u32              = 0
llama_model_loader: - kv  18:               general.quantization_version u32              = 2
llama_model_loader: - type  f32:   65 tensors
llama_model_loader: - type q4_K:  193 tensors
llama_model_loader: - type q6_K:   33 tensors
llm_load_vocab: special tokens cache size = 259
llm_load_vocab: token to piece cache size = 0.1684 MB
llm_load_print_meta: format           = GGUF V2
llm_load_print_meta: arch             = llama
llm_load_print_meta: vocab type       = SPM
llm_load_print_meta: n_vocab          = 32000
llm_load_print_meta: n_merges         = 0
llm_load_print_meta: n_ctx_train      = 4096
llm_load_print_meta: n_embd           = 4096
llm_load_print_meta: n_head           = 32
llm_load_print_meta: n_head_kv        = 32
llm_load_print_meta: n_layer          = 32
llm_load_print_meta: n_rot            = 128
llm_load_print_meta: n_embd_head_k    = 128
llm_load_print_meta: n_embd_head_v    = 128
llm_load_print_meta: n_gqa            = 1
llm_load_print_meta: n_embd_k_gqa     = 4096
llm_load_print_meta: n_embd_v_gqa     = 4096
llm_load_print_meta: f_norm_eps       = 0.0e+00
llm_load_print_meta: f_norm_rms_eps   = 1.0e-06
llm_load_print_meta: f_clamp_kqv      = 0.0e+00
llm_load_print_meta: f_max_alibi_bias = 0.0e+00
llm_load_print_meta: f_logit_scale    = 0.0e+00
llm_load_print_meta: n_ff             = 11008
llm_load_print_meta: n_expert         = 0
llm_load_print_meta: n_expert_used    = 0
llm_load_print_meta: causal attn      = 1
llm_load_print_meta: pooling type     = 0
llm_load_print_meta: rope type        = 0
llm_load_print_meta: rope scaling     = linear
llm_load_print_meta: freq_base_train  = 10000.0
llm_load_print_meta: freq_scale_train = 1
llm_load_print_meta: n_ctx_orig_yarn  = 4096
llm_load_print_meta: rope_finetuned   = unknown
llm_load_print_meta: ssm_d_conv       = 0
llm_load_print_meta: ssm_d_inner      = 0
llm_load_print_meta: ssm_d_state      = 0
llm_load_print_meta: ssm_dt_rank      = 0
llm_load_print_meta: model type       = 7B
llm_load_print_meta: model ftype      = Q4_K - Medium
llm_load_print_meta: model params     = 6.74 B
llm_load_print_meta: model size       = 3.80 GiB (4.84 BPW)
llm_load_print_meta: general.name     = LLaMA v2
llm_load_print_meta: BOS token        = 1 '<s>'
llm_load_print_meta: EOS token        = 2 '</s>'
llm_load_print_meta: UNK token        = 0 '<unk>'
llm_load_print_meta: LF token         = 13 '<0x0A>'
llm_load_tensors: ggml ctx size =    0.15 MiB
llm_load_tensors:        CPU buffer size =  3891.24 MiB
..................................................................................................
llama_new_context_with_model: n_ctx      = 512
llama_new_context_with_model: n_batch    = 512
llama_new_context_with_model: n_ubatch   = 512
llama_new_context_with_model: flash_attn = 0
llama_new_context_with_model: freq_base  = 10000.0
llama_new_context_with_model: freq_scale = 1
llama_kv_cache_init:        CPU KV buffer size =   256.00 MiB
llama_new_context_with_model: KV self size  =  256.00 MiB, K (f16):  128.00 MiB, V (f16):  128.00 MiB
llama_new_context_with_model:        CPU  output buffer size =     0.12 MiB
llama_new_context_with_model:        CPU compute buffer size =    70.50 MiB
llama_new_context_with_model: graph nodes  = 1030
llama_new_context_with_model: graph splits = 514

system_info: n_threads = 8 / 16 | AVX = 1 | AVX_VNNI = 0 | AVX2 = 1 | AVX512 = 0 | AVX512_VBMI = 0 | AVX512_VNNI = 0 | AVX512_BF16 = 0 | FMA = 1 | NEON = 0 | SVE = 0 | ARM_FMA = 0 | F16C = 1 | FP16_VA = 0 | WASM_SIMD = 0 | BLAS = 1 | SSE3 = 1 | SSSE3 = 1 | VSX = 0 | MATMUL_INT8 = 0 | LLAMAFILE = 1 |
main: interactive mode on.
Reverse prompt: 'User:'
Input prefix: ' '
sampling:
        repeat_last_n = 64, repeat_penalty = 1.000, frequency_penalty = 0.000, presence_penalty = 0.000
        top_k = 40, tfs_z = 1.000, top_p = 0.950, min_p = 0.050, typical_p = 1.000, temp = 0.800
        mirostat = 0, mirostat_lr = 0.100, mirostat_ent = 5.000
sampling order:
CFG -> Penalties -> top_k -> tfs_z -> typical_p -> top_p -> min_p -> temperature
generate: n_ctx = 512, n_batch = 1024, n_predict = 256, n_keep = 1


== Running in interactive mode. ==
 - Press Ctrl+C to interject at any time.
 - Press Return to return control to the AI.
 - To return control without starting a new line, end your input with '/'.
 - If you want to submit another line, end your input with '\'.

 You (Alice) are a helpful assistant. Please answer a human (User)'s questions.
User: Hello.
Alice: Hello! How can I assist you today? Is there anything you need help with?

ここで「 Hello! How can I assist you today? Is there anything you need help with?」の部分が、モデルの生成した部分である。 また、本来はここで"User: "と表示されるのを期待しているのだが、なかなか表示されない。 表示されることもある。 良く理由は分かっていない。 ただ、"User: "が表示されなくても、利用はできる。

指示をしてみると、それっぽい返答をしてくれる。

 Tell me the largest city in Asia.
Alice: The largest city in Asia is Tokyo, Japan. It has a population of over
38 million people and is known for its vibrant culture, cutting-edge technology,
and historic landmarks such as the Tokyo Tower and the Meiji Shrine. Would you
like to know anything else?

「Tell me the largest city in Asia.」の部分が私がキーボードから入力した部分である。

Amazon Kindle Fireタブレットで、放送大学の学生向けのインターネット配信を視聴する

放送大学では学生向けには、テレビで放送されている講義のビデオと、ラジオで放送されている講義のオーディオがウェブ上で配信されている。 これはどうやら、Widevine CDMを使っているようだ。 そして、ウェブブラウザーのuser agent stringを見て視聴で...