nanoLLaVAを最適化して動かす

2024年7月10日

はじめに

EdgeTech+ West 2024では画像解説AI(nanoLLaVA)を搭載したラズパイ5によるチャットデモを展示します。
そこで今回はデモを作成するにあたって研究したことについて紹介したいと思います。

動作環境

使用する環境は以下の通りです。

  • Raspberry Pi5
    • CPU: BCM2712 2.4GHz quad-core 64-bit Arm Cortex-A76 CPU, with cryptography extensions, 512KB per-core L2 caches and a 2MB shared L3 cache
    • GPU: VideoCore VII GPU, supporting OpenGL ES 3.1, Vulkan 1.2
    • Memory: 8GB SKUs available at launch
    • OS: Raspberry Pi OS with desktop
    • System: 32-bit
    • Kernel version: 6.6
  • llama.cpp
    • tag: b3358
  • llama-cpp-python==0.2.79

nanoLLaVA とは

nanoLLaVAはエッジデバイス上で効率的に動作するように設計された1Bパラメータの視覚言語モデル(VLM)です。
VLMはChatGPTのようなAIモデルとは異なり、マルチモーダル入力に対応したモデルです。
画像とテキストを入力することが可能で、画像に写っているものについて説明してもらうことや質問に答えてもらうことなどが可能です。
nanoLLaVAは2024年7月現在、公開されているVLMの中で最軽量なモデルだと思われます。



一般にVLMは大きく分けてvision encoderとvision projectorとLLMの3要素から構成されており、nanoLLaVAではそれぞれ以下のようなモデルが使用されています。

  • vision enconder
      google/siglip-so400m-patch14-384
  • vision projector
      線形層
  • LLM
      Quyen-SE-v0.1 (Qwen1.5-0.5B)

nanoLLaVAのラズパイ5上への最適化

LLMやVLMを高速に動作させるフレームワークとしてC/C++で実装されているllama.cppというオープンソースがあります。モデルをGGML/GGUFと呼ばれる形式に変換することにより、様々なデバイス上での動作が可能な設計となっており、モデルを軽量化するためのツール等も用意されています。
今回はllama.cppとllama.cppのpythonバインディングライブラリであるllama-cpp-pythonを使用してnanoLLaVAをラズパイ5に実装します。

gguf形式への変換

事前準備としてnanoLLaVAをcloneしておきます。


git clone https://huggingface.co/qnguyen3/nanoLLaVA
								

cloneしたnanollavaのhuggingfaceモデルをgguf形式に変換するスクリプトを実装しました。以下のリンクで公開しています。
https://huggingface.co/bee-kake/nanollava-1.5-gguf/tree/main
nanollava_mmproj_convert.pyと./nanollava_llm_convert.pyを以下のコマンドで実行します。


# vision projectorのgguf modelへの変換
python ./nanollava_mmproj_convert.py --model <"nanollava repository">
# llmのgguf modelへの変換
python ./nanollava_llm_convert.py <"nanollava repository">
								

nanollavaディレクトリ内にvision projector用のggufモデルとllm用のggufモデルの2つが作成されます。
このggufモデルをllama.cppまたはllama-cpp-pythonで読み込むことで推論の実行が可能になります。

量子化による軽量化

AIモデルのモデルサイズを削減するテクニックの一つに量子化があります。
量子化はモデルを構成するパラメータを表現するためのビット数を、より低ビットの離散値に値をマッピングすることにより軽量化するアルゴリズムです
作成したgguf形式のモデルを使用して量子化してみます。
事前準備としてllama.cppをcloneして、量子化ツールをビルドしておきます。


git clone https://github.com/ggerganov/llama.cpp
cd llama.cpp
make llama-llava-cli
								

2024年7月現在は以下のようなコマンドで量子化が可能です。
最後に量子化オプションを追加することで指定の量子化ビットでの量子化が可能となります。


./llama-quantize <"llm用のggufモデルへのパス"> <"量子化後のggufモデルへの出力パス"> Q8_0
								

変換が成功すれば以下のようなログが出力されます。


llama_model_quantize_internal: model size  =  1181.97 MB
llama_model_quantize_internal: quant size  =   628.14 MB
								

元の16bitモデルから8bitに量子化を行ったため、単純にモデルサイズが半分程度になっていることを確認できます。これにより推論時のメモリ使用量も削減されます。
量子化前と量子化後で生成されたテキストの精度を比較してみます。
まず量子化前のモデルを使って以下のコマンドで推論してみます。テスト画像としては以下の画像を使用します。(以下画像名をsample.jpgとする)


./llama-llava-cli -m <"llm用ggufモデルへのパス"> --mmproj <"vision projector用ggufモデルへのパス"> -c 2048 -p "What is in this picture?" --image sample.jpg
								

生成されたテキストは以下のようになりました。


A cat is sitting on a laptop computer. There are also some plants on the laptop.
								

猫の位置はノートパソコンの横ですが、概ね正しいテキストが返されています。
また、8bit量子化済みモデルを使って同様に以下のコマンドで推論してみます。


./llama-llava-cli -m <"llm用8bit量子化済みggufモデルへのパス"> --mmproj <"vision projector用ggufモデルへのパス"> -c 2048 -p "What is in this picture?" --image sample.jpg
								

生成されたテキストは以下のようになりました。


A cat is laying on a laptop computer.
								

量子化前と比較して情報量が減っているように見えますが、大幅な精度劣化は見られませんでした。

GPU(Vulkan)を使用した高速化

次にllama-cpp-pythonでVulkanオプションを有効にしてラズパイ5のGPUを使用した高速化が可能か試してみます。
llama-cpp-pythonをビルドする際のオプションとして以下のようにオプションを追加しました。


CMAKE_ARGS="-DLLAMA_VULKAN=on" python3 -m pip install --no-cache-dir --upgrade --force-reinstall --verbose llama-cpp-python
								

このパッケージで再度推論を実行してみましたが、推論結果が表示され始めるまでに10分以上掛かり、生成されたテキストも”G”の羅列となりました。現状ではllama.cppがラズパイ5のGPUでの推論に対応していないと思われます。

おわりに

視覚言語モデルnanoLLaVAをラズパイ5用に最適化して動作させることに成功しました。今回、GPUへの対応では思ったような結果が得られませんでしたが、引き続きチャレンジしたいと思います。llama.cppも非常に活発なコミュニティとなっているので今後のアップデート情報にも注目したいです。