はじめに
hacomonoのIoT部マネージャーの岩貞(さとちゃん)です。
最近hacomonoの影響もあってか、サウナにちょっとずつハマっています。
友人の結婚祝いで名古屋に行くことがあったので SENSE sauna 行ってきました。大衆浴場のサウナしか行ったことがなかったので、小スペースで友人だけと会話しながら入れるサウナはすごく良かったです。
良いサウナ場をこれからも探そうと思います笑
今回の話
技適がついに通ったラズパイ5。少し静観してたのですが、スイッチサイエンスに4GBモデルの在庫があるということでIoTのSW開発メンバーを中心に研究開発目的で購入してお配りしました。
あわせて自分も手元にGETしたので、眠らせるのはもったいないということで「火入れだけでも…」と思い動かしてみました。
その時の簡単なセットアップ手順とついでに流行りっぽいもの動かしてみました。
環境
Raspberry Pi 5 4GBモデル
安定のスイッチサイエンスさんから買いました。
Raspberry Pi 5 / 4GBwww.switch-science.com
電源は5V3Aでも動作するということでラズパイ4のものを流用。
ケースもなし、ファンやヒートシンクもなしです。
電源はまだ専用のものが用意できていないというのと、ケースやファンも売り切れていたので致し方なし。
イメージ書き込み
- 適当なSD見繕う (過去使ってたものを見つけてきた)
- Raspberry Pi Imagerでラズパイ5向けのイメージを書き込み
- 昔はSDへのイメージ書き込みも面倒だったけど、今はImagerで一発。脳死で行ける(便利な世界)
- 書き込み前に色々事前に設定出来るが、自宅のWiFi設定をしておくのとSSHの設定をしておくのをおすすめする。
- キーボードやら、マウスやら、ディスプレイやらを引っ張ってこなくてもターミナル入ってあれこれ出来る
- 色々準備すると机の上がとっ散らかるのでとても大事。
ラズパイ5のセットアップ
- SDに書き込めたらラズパイ5に挿して、電源ON
- 起動したらSSHで接続
$ ssh pi@raspberrypi.local
- ラズパイ5の証を確認しておく。
pi@raspberrypi:~ $ lscpu Architecture: aarch64 CPU op-mode(s): 32-bit, 64-bit Byte Order: Little Endian CPU(s): 4 On-line CPU(s) list: 0-3 Vendor ID: ARM Model name: Cortex-A76 Model: 1 Thread(s) per core: 1 Core(s) per cluster: 4 Socket(s): - Cluster(s): 1 Stepping: r4p1 CPU(s) scaling MHz: 62% CPU max MHz: 2400.0000 CPU min MHz: 1500.0000 BogoMIPS: 108.00 Flags: fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm lrcpc dcpop asimddp Vulnerabilities: Gather data sampling: Not affected Itlb multihit: Not affected L1tf: Not affected Mds: Not affected Meltdown: Not affected Mmio stale data: Not affected Retbleed: Not affected Spec rstack overflow: Not affected Spec store bypass: Mitigation; Speculative Store Bypass disabled via prctl Spectre v1: Mitigation; __user pointer sanitization Spectre v2: Mitigation; CSV2, BHB Srbds: Not affected Tsx async abort: Not affected
うん、Cortex-A76だ。間違いない。
- SWアップデートしておく
$ sudo apt update
$ sudo apt upgrade
$ reboot #念の為reboot
以上、特にこだわらなければセットアップはこれで完了。
生成AI動かしてみる
ここまで来たら後は良しなに。
何しようかなと思って他の方のラズパイ5の投稿見つつ最近さわれてなかったLLM動かすのが面白そうだったので本当に書いてあるとおりに動かす。 qiita.com
# Python仮想環境作る $ python -m venv llama2 $ cd llama2 $ . bin/activate # ついでに環境も切り替えておく # モデルをDL $ wget https://huggingface.co/TheBloke/TinyLlama-1.1B-Chat-v1.0-GGUF/resolve/main/tinyllama-1.1b-chat-v1.0.Q4_K_M.gguf
※ここでvimもinstallしておく
$ sudo apt install neovim
記事記載の通りにvimとかでファイル作成する。
$ vim llama2.py
↓中身
import sys from llama_cpp import Llama llm = Llama(model_path="tinyllama-1.1b-chat-v1.0.Q4_K_M.gguf") print(llm("<user>\n" + sys.argv[1] + "\n<assistant>\n", max_tokens=40)['choices'][0]["text"] + "...")
これだけで準備完了。
動かしてみる。
(llama2) pi@raspberrypi:~/llama2 $ python llama2.py "what is Empire State Building?" llama_model_loader: loaded meta data with 23 key-value pairs and 201 tensors from tinyllama-1.1b-chat-v1.0.Q4_K_M.gguf (version GGUF V3 (latest)) 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 = tinyllama_tinyllama-1.1b-chat-v1.0 llama_model_loader: - kv 2: llama.context_length u32 = 2048 llama_model_loader: - kv 3: llama.embedding_length u32 = 2048 llama_model_loader: - kv 4: llama.block_count u32 = 22 llama_model_loader: - kv 5: llama.feed_forward_length u32 = 5632 llama_model_loader: - kv 6: llama.rope.dimension_count u32 = 64 llama_model_loader: - kv 7: llama.attention.head_count u32 = 32 llama_model_loader: - kv 8: llama.attention.head_count_kv u32 = 4 llama_model_loader: - kv 9: llama.attention.layer_norm_rms_epsilon f32 = 0.000010 llama_model_loader: - kv 10: llama.rope.freq_base f32 = 10000.000000 llama_model_loader: - kv 11: general.file_type u32 = 15 llama_model_loader: - kv 12: tokenizer.ggml.model str = llama llama_model_loader: - kv 13: tokenizer.ggml.tokens arr[str,32000] = ["<unk>", "<s>", "</s>", "<0x00>", "<... llama_model_loader: - kv 14: tokenizer.ggml.scores arr[f32,32000] = [0.000000, 0.000000, 0.000000, 0.0000... llama_model_loader: - kv 15: tokenizer.ggml.token_type arr[i32,32000] = [2, 3, 3, 6, 6, 6, 6, 6, 6, 6, 6, 6, ... llama_model_loader: - kv 16: tokenizer.ggml.merges arr[str,61249] = ["▁ t", "e r", "i n", "▁ a", "e n... llama_model_loader: - kv 17: tokenizer.ggml.bos_token_id u32 = 1 llama_model_loader: - kv 18: tokenizer.ggml.eos_token_id u32 = 2 llama_model_loader: - kv 19: tokenizer.ggml.unknown_token_id u32 = 0 llama_model_loader: - kv 20: tokenizer.ggml.padding_token_id u32 = 2 llama_model_loader: - kv 21: tokenizer.chat_template str = {% for message in messages %}\n{% if m... llama_model_loader: - kv 22: general.quantization_version u32 = 2 llama_model_loader: - type f32: 45 tensors llama_model_loader: - type q4_K: 135 tensors llama_model_loader: - type q6_K: 21 tensors llm_load_vocab: special tokens definition check successful ( 259/32000 ). llm_load_print_meta: format = GGUF V3 (latest) 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 = 2048 llm_load_print_meta: n_embd = 2048 llm_load_print_meta: n_head = 32 llm_load_print_meta: n_head_kv = 4 llm_load_print_meta: n_layer = 22 llm_load_print_meta: n_rot = 64 llm_load_print_meta: n_embd_head_k = 64 llm_load_print_meta: n_embd_head_v = 64 llm_load_print_meta: n_gqa = 8 llm_load_print_meta: n_embd_k_gqa = 256 llm_load_print_meta: n_embd_v_gqa = 256 llm_load_print_meta: f_norm_eps = 0.0e+00 llm_load_print_meta: f_norm_rms_eps = 1.0e-05 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: n_ff = 5632 llm_load_print_meta: n_expert = 0 llm_load_print_meta: n_expert_used = 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_yarn_orig_ctx = 2048 llm_load_print_meta: rope_finetuned = unknown llm_load_print_meta: model type = 1B llm_load_print_meta: model ftype = Q4_K - Medium llm_load_print_meta: model params = 1.10 B llm_load_print_meta: model size = 636.18 MiB (4.85 BPW) llm_load_print_meta: general.name = tinyllama_tinyllama-1.1b-chat-v1.0 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: PAD token = 2 '</s>' llm_load_print_meta: LF token = 13 '<0x0A>' llm_load_tensors: ggml ctx size = 0.08 MiB llm_load_tensors: CPU buffer size = 636.18 MiB .................................................................................. llama_new_context_with_model: n_ctx = 512 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 = 11.00 MiB llama_new_context_with_model: KV self size = 11.00 MiB, K (f16): 5.50 MiB, V (f16): 5.50 MiB llama_new_context_with_model: CPU input buffer size = 6.01 MiB llama_new_context_with_model: CPU compute buffer size = 66.50 MiB llama_new_context_with_model: graph splits (measure): 1 AVX = 0 | AVX_VNNI = 0 | AVX2 = 0 | AVX512 = 0 | AVX512_VBMI = 0 | AVX512_VNNI = 0 | FMA = 0 | NEON = 1 | ARM_FMA = 1 | F16C = 0 | FP16_VA = 0 | WASM_SIMD = 0 | BLAS = 0 | SSE3 = 0 | SSSE3 = 0 | VSX = 0 | MATMUL_INT8 = 0 | Model metadata: {'tokenizer.chat_template': "{% for message in messages %}\n{% if message['role'] == 'user' %}\n{{ '<|user|>\n' + message['content'] + eos_token }}\n{% elif message['role'] == 'system' %}\n{{ '<|system|>\n' + message['content'] + eos_token }}\n{% elif message['role'] == 'assistant' %}\n{{ '<|assistant|>\n' + message['content'] + eos_token }}\n{% endif %}\n{% if loop.last and add_generation_prompt %}\n{{ '<|assistant|>' }}\n{% endif %}\n{% endfor %}", 'tokenizer.ggml.padding_token_id': '2', 'tokenizer.ggml.unknown_token_id': '0', 'tokenizer.ggml.eos_token_id': '2', 'general.architecture': 'llama', 'llama.rope.freq_base': '10000.000000', 'llama.context_length': '2048', 'general.name': 'tinyllama_tinyllama-1.1b-chat-v1.0', 'llama.embedding_length': '2048', 'llama.feed_forward_length': '5632', 'llama.attention.layer_norm_rms_epsilon': '0.000010', 'llama.rope.dimension_count': '64', 'tokenizer.ggml.bos_token_id': '1', 'llama.attention.head_count': '32', 'llama.block_count': '22', 'llama.attention.head_count_kv': '4', 'general.quantization_version': '2', 'tokenizer.ggml.model': 'llama', 'general.file_type': '15'} Using chat template: {% for message in messages %} {% if message['role'] == 'user' %} {{ '<|user|> ' + message['content'] + eos_token }} {% elif message['role'] == 'system' %} {{ '<|system|> ' + message['content'] + eos_token }} {% elif message['role'] == 'assistant' %} {{ '<|assistant|> ' + message['content'] + eos_token }} {% endif %} {% if loop.last and add_generation_prompt %} {{ '<|assistant|>' }} {% endif %} {% endfor %} Using chat eos_token: Using chat bos_token: llama_print_timings: load time = 1366.39 ms llama_print_timings: sample time = 10.24 ms / 40 runs ( 0.26 ms per token, 3907.78 tokens per second) llama_print_timings: prompt eval time = 1366.32 ms / 17 tokens ( 80.37 ms per token, 12.44 tokens per second) llama_print_timings: eval time = 3326.60 ms / 39 runs ( 85.30 ms per token, 11.72 tokens per second) llama_print_timings: total time = 4850.41 ms / 56 tokens The Empire State Building, also known as the Freedom Tower or simply "the Blob," is a skyscraper located in New York City, USA. It was completed in 19...
色々でますが、一番最後のところの
The Empire State Building, also known as the Freedom Tower or simply "the Blob," is a skyscraper located in New York City, USA. It was completed in 19...
が実行結果。
tokenサイズを40としているので途中で切れている。
40くらいだと数秒で実行結果が出力されましたが、これを400とか10倍にすると数十秒待つ必要がありました。
max_token増やすとすごい時間かかる。あと、ChatGPTと違って最後にバッと出力されるので、体感結構かかる気がした。途中経過を生成している様を見るのは結構体験的にも大事なのかも知れない。そこはllmの出力optionでなんとかできそうな気もしたけど一旦おいとく。
今使ってるモデルは英語オンリーなので、日本語使えるモデルで置き換えてみた。
以下から適当なものを取得。
huggingface.co
サンプルのggufのモデル指定部分を置き換えてみる。
$ vim llama2_jp.py
↓中身はコピペしてモデル部分だけ置き換えた
import sys from llama_cpp import Llama llm = Llama(model_path="/home/pi/llama2/ELYZA-japanese-Llama-2-7b-fast-instruct-q2_K.gguf") print(llm("<user>\n" + sys.argv[1] + "\n<assistant>\n", max_tokens=40)['choices'][0]["text"] + "...")
実行してみる。その時、親子丼食べたかったんで親子丼について聞いてみた。
(llama2) pi@raspberrypi:~/llama2 $ python llama2_jp.py "親子丼のこと教えて" llama_model_loader: loaded meta data with 21 key-value pairs and 291 tensors from /home/pi/llama2/ELYZA-japanese-Llama-2-7b-fast-instruct-q2_K.gguf (version GGUF V3 (latest)) 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 = ELYZA-japanese-Llama-2-7b-fast-instruct llama_model_loader: - kv 2: general.source.huggingface.repository str = elyza/ELYZA-japanese-Llama-2-7b-fast-... llama_model_loader: - kv 3: llama.tensor_data_layout str = Meta AI original pth llama_model_loader: - kv 4: llama.context_length u32 = 4096 llama_model_loader: - kv 5: llama.embedding_length u32 = 4096 llama_model_loader: - kv 6: llama.block_count u32 = 32 llama_model_loader: - kv 7: llama.feed_forward_length u32 = 11008 llama_model_loader: - kv 8: llama.rope.dimension_count u32 = 128 llama_model_loader: - kv 9: llama.attention.head_count u32 = 32 llama_model_loader: - kv 10: llama.attention.head_count_kv u32 = 32 llama_model_loader: - kv 11: llama.attention.layer_norm_rms_epsilon f32 = 0.000001 llama_model_loader: - kv 12: tokenizer.ggml.model str = llama llama_model_loader: - kv 13: tokenizer.ggml.tokens arr[str,45043] = ["<unk>", "<s>", "</s>", "<0x00>", "<... llama_model_loader: - kv 14: tokenizer.ggml.scores arr[f32,45043] = [0.000000, 0.000000, 0.000000, 0.0000... llama_model_loader: - kv 15: tokenizer.ggml.token_type arr[i32,45043] = [2, 3, 3, 6, 6, 6, 6, 6, 6, 6, 6, 6, ... llama_model_loader: - kv 16: tokenizer.ggml.bos_token_id u32 = 1 llama_model_loader: - kv 17: tokenizer.ggml.eos_token_id u32 = 2 llama_model_loader: - kv 18: tokenizer.ggml.unknown_token_id u32 = 0 llama_model_loader: - kv 19: general.quantization_version u32 = 2 llama_model_loader: - kv 20: general.file_type u32 = 10 llama_model_loader: - type f32: 65 tensors llama_model_loader: - type q2_K: 65 tensors llama_model_loader: - type q3_K: 160 tensors llama_model_loader: - type q6_K: 1 tensors llm_load_vocab: mismatch in special tokens definition ( 304/45043 vs 264/45043 ). llm_load_print_meta: format = GGUF V3 (latest) llm_load_print_meta: arch = llama llm_load_print_meta: vocab type = SPM llm_load_print_meta: n_vocab = 45043 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: n_ff = 11008 llm_load_print_meta: n_expert = 0 llm_load_print_meta: n_expert_used = 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_yarn_orig_ctx = 4096 llm_load_print_meta: rope_finetuned = unknown llm_load_print_meta: model type = 7B llm_load_print_meta: model ftype = Q2_K - Medium llm_load_print_meta: model params = 6.85 B llm_load_print_meta: model size = 2.69 GiB (3.37 BPW) llm_load_print_meta: general.name = ELYZA-japanese-Llama-2-7b-fast-instruct 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.11 MiB llm_load_tensors: CPU buffer size = 2752.83 MiB .............................................................................................. llama_new_context_with_model: n_ctx = 512 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 input buffer size = 10.01 MiB llama_new_context_with_model: CPU compute buffer size = 95.97 MiB llama_new_context_with_model: graph splits (measure): 1 AVX = 0 | AVX_VNNI = 0 | AVX2 = 0 | AVX512 = 0 | AVX512_VBMI = 0 | AVX512_VNNI = 0 | FMA = 0 | NEON = 1 | ARM_FMA = 1 | F16C = 0 | FP16_VA = 0 | WASM_SIMD = 0 | BLAS = 0 | SSE3 = 0 | SSSE3 = 0 | VSX = 0 | MATMUL_INT8 = 0 | Model metadata: {'general.file_type': '10', 'tokenizer.ggml.unknown_token_id': '0', 'tokenizer.ggml.eos_token_id': '2', 'general.architecture': 'llama', 'llama.context_length': '4096', 'general.name': 'ELYZA-japanese-Llama-2-7b-fast-instruct', 'general.source.huggingface.repository': 'elyza/ELYZA-japanese-Llama-2-7b-fast-instruct', 'llama.embedding_length': '4096', 'llama.tensor_data_layout': 'Meta AI original pth', 'llama.feed_forward_length': '11008', 'llama.attention.layer_norm_rms_epsilon': '0.000001', 'llama.rope.dimension_count': '128', 'tokenizer.ggml.bos_token_id': '1', 'llama.attention.head_count': '32', 'llama.block_count': '32', 'llama.attention.head_count_kv': '32', 'general.quantization_version': '2', 'tokenizer.ggml.model': 'llama'} llama_print_timings: load time = 12159.14 ms llama_print_timings: sample time = 13.89 ms / 40 runs ( 0.35 ms per token, 2880.18 tokens per second) llama_print_timings: prompt eval time = 12159.06 ms / 16 tokens ( 759.94 ms per token, 1.32 tokens per second) llama_print_timings: eval time = 30298.97 ms / 39 runs ( 776.90 ms per token, 1.29 tokens per second) llama_print_timings: total time = 42718.35 ms / 55 tokens ごちそうが好きなので親子丼を食べたことがあります。 赤ちゃんがまだ小学生のときに食べて、とても美味しかったですね。 子供の頃は今ほど親に...
一番下部に回答がありますね。
ちなみにはじめ動かしていたモデルの tinyllama-1.1b-chat-v1.0.Q4_K_M.gguf は638MBだったのですが、持ってきた日本語モデル ELYZA-japanese-Llama-2-7b-fast-instruct-q2_K.gguf は2.7GBもあるせいか、読み込みがめっちゃ遅い。SDのI/O遅いというのもあるかも。。
その分実行結果もめちゃくちゃ遅いです。メモリ8GBだともう少し頑張れたのかも。
ちなみに参考にした記事をベースにNode-RED使ったchatサーバーまでやりました。
同じようにやってみたい方はパス設定で動かなかったりしたのでそこだけ注意してください。
結果はまぁ… 笑
さいごに
ラズパイ5セットアップついでにLLM動かせたのはなんか良かった。でも4からの比較にはならなかったです。そこはやってる人多いからもういいよね…?
ラズパイ5が技適が通ってちょうど各方面、手に入れられているようで色々と記事が増えてきて面白いです。
個人的にはSSD周りも簡単にできるようになっているので高速化のためにやってみたいですね。
note.com
Interfaceも特集記事上げるみたいなのでちょっと祭り的に盛り上がるの良いですね。業界盛り上げるようなデバイスが最近なかった気がして嬉しい。
ラズベリーパイ5の技適申請が通ったようです.
— コンピュータ技術実験雑誌「Interface」(毎月25日発売,CQ出版社) (@If_CQ) 2024年2月4日
Interface誌では3/25発売の5月号にてラズベリーパイ5を特集します.
詳細は近日中に公開します.ご期待ください. pic.twitter.com/x91xLpT5td
宣伝
最近、当社で顧問としても入っていただいているくずさんとの対談記事が公開されたので、あわせて見ていただけるとhacomonoのIoTが結構頑張ってるところがわかると思います笑
スイッチサイエンス企画の対談記事に掲載して頂きました!hacomonoのIoT事業の立ち上げでPoCは出来たものの、量産ステップで悩んでいた時に全方位助けて頂きました。
— 岩貞 智 / hacomono (@iwasada_s) 2024年2月16日
同じように悩んでいる方は是非くずさん @qzuryu に相談してみてください〜!🙆♂️#hacomono #スイッチサイエンス #量産 https://t.co/GsloxU76Kg
株式会社hacomonoでは一緒に働く仲間を募集しています。
採用情報や採用ウィッシュリストもぜひご覧ください!