インターンシップ2024 参加報告 モバイル端末でのSLM利用

はじめに

初めまして. 株式会社Nefrockの2024年度インターンシップに参加した東京科学大学博士課程2年の細木隆豊です. 大学では並列計算の研究室に所属しており,マルチGPU環境下での機械学習モデルの並列学習の高速化や自動化に関する研究を行っています.

この記事では,インターシップとして私が実施した"スマートフォン上でSLMを利用するためのフレームワークの調査"について紹介いたします.

SLMとは

インターンシップでの活動内容について述べる前に,SLMについて説明します.

SLM(Small Language Model; 小規模言語モデル)とは,ざっくり言うと比較的小規模なサイズの言語モデルです. 大規模言語モデル(LLM)と比べると性能は落ちますが,推論が少ない計算量でできること,メモリ使用量が少ないことが特徴です.

ここ数年,ChatGPTやClaudeなどの高性能なテキスト向けAIサービスが提供され,多くの方が利用するようになっています. これらの高性能な言語タスクを可能にしているのが,大規模言語モデルの存在です. LLMはその名の通り非常に大規模なモデルで,一般に百億以上のパラメータを持ちます. そのため,仮に量子化した場合でもモデルのパラメータだけで数十GB以上のメモリが必要になり,さらに計算量も膨大であるため,推論であってもハイエンドなGPUを搭載したサーバでなければ実行するのは難しいです.

このように実行環境が限定されるLLMに対し,より小規模なモデルを使うことでサーバでなくとも気軽に実行できるようにしているのがSLMになります. 言語タスクではパラメータサイズがモデル性能につながるとされているため,精度や汎化性能はLLMに比べ落ちてしまいますが,誰でも手元の環境で試せる利点は非常に大きいもので,注目を浴び始めております. SLMの代表的なモデルとしては,Phi-3やGemma,Mistralが挙げられます.

今回のインターンシップでのテーマ

モバイル端末,特にスマートフォン上でSLMを動かすことができるエッジデバイス用フレームワークについて調査することが,インターンシップで取り組んだテーマになります. エッジデバイス用機械学習ライブラリは現在多種多様なものが公開されておりますが,今回はその中でExecuTorch, llama.cpp, picoLLMの3つを調べていきます. これらのフレームワークは,インターンシップ開始時点で最新のSLMであったLlama3.2モデルをサポートし,android端末でLlamaモデルを動かすためのアプリケーションも用意されています.

それぞれのフレームワークの特徴やバックエンド利用,実行した際の推論速度について,これから述べていきます.

各種フレームワークの概要

ExecuTorch

一つ目のフレームワークは,PyTorchによるエッジデバイス向けプラットフォームであるExecuTorchになります. PyTorchで記述したモデルをtorch.exportによるグラフ表現を介して最適化を施し,エッジデバイス向けプログラム.pteに変換します. ランタイムでは.pteプログラムに従い実行されていきます. ExecuTorchでは現在,量子化やアクティベーションのメモリ使用に関する最適化に加え,XNNPACKやQNNなどのスマホやPC向けのバックエンドがサポートされています.

llama.cpp

二つ目のllama.cppは,言語モデルの推論を純粋なC/C++で実行することを目的としたプロジェクトです. GGUFというC/C++向けのモデル記述形式を利用することが特徴であり,低精度への量子化による推論の高速化を実現します. GGUFの前身である.ggmlやHuggingFaceのモデルをGGUF形式への変換機能も提供されています. また,CUDAやHIP,SYCL,Metalなどのバックエンドをサポートしており,デスクトップやラップトップなどの軽量な環境での高速な推論が可能です.

picoLLM

3つ目のフレームワークはpicoLLMです. 限られたリソース環境で精度を落とさずにLLMを動かすフレームワークとして,カナダのPicovoice社によって開発されました. このフレームワークでは,精度が落ちにくい独自の量子化を採用しています. LlamaだけでなくGemmaやMistralなどの言語モデルを圧縮したものを用意しており,利用者はアクセスキーをコードに打ち込むことでモデルをダウンロード・利用できるようになります.

このフレームワークでは,モデル変換の機能がおそらく提供されていないため,独自のモデルやファインチューニングしたモデルに用いることはできないと思われます.

デモアプリのビルド

まず,それぞれのフレームワークのデモアプリを実行してみました. 今回の実験では,ExecuTorchのデリゲートはXNNPACKとQNNを試しました. ビルド環境は,ExecuTorchのQNNデリゲートではLinuxのサーバマシンを,それ以外では手元のM1 macを利用しました.

これからそれぞれのフレームワークでのビルド手順をみていきます.

ExecuTorch (XNNPACK)

仮想環境にvenvを利用した以外は,以下のドキュメントに通りに実行しました. 一連の操作は,依存ライブラリのインストール,モデルのエクスポート,モデルとトークナイザの転送,AARライブラリのビルド,そしてアプリの実行になります. github.com

モデルのエクスポートでは,変換前のファイルの形式(量子化の種類など),バックエンドや最適化をオプションとして指定できるようになっています. 今回は,BF16, SpinQuant, QAT+LoRAを試しました.

ExecuTorchデモアプリの画面

ExecuTorch (QNN)

QNNは,Qualcomm社のSnapdragon SoC用のAIワークロード向けAPIである"Qualcomm AI Engine Direct"のことを指します. QNNの利用により,GalaxyなどSnapdragonを搭載したAndoroidスマホで,高速な推論が期待されます.

ビルドの工程としては,XNNPACKでの操作に加え,ExecuTorchでのQNNバックエンドのビルドとLlama Runnerのビルドが必要です. また,モデルのエクスポートは数十分程度の時間と相当のメモリが必要となります. 実験では1Bのモデルであっても100GB近くのメモリを消費していました.

実は今回のインターンシップで一番時間を費やしたのが,このQNNバックエンド利用のExecuTorchの準備でした. 発生したエラーがソフトウェアの不足やバージョン違いによるものなのかExecuTorchのバグによるものなのかの判断が難しく,双方向から調査する必要があったため時間がかかってしまいました. 最終的にはたいていのバグがソフトウェアの違いからくるものであることがわかり, CI用のDocker imageテンプレートを見つけそれを利用することで解消できました.

しかしながら,QNN利用のデモアプリは現状Llama3までしかサポートしていないようで,苦労の甲斐虚しくLlama3.2モデルでは動作がかなり遅く,また無意味な文字列しか出力されませんでした.

llama.cpp

llama.cppは,モデルをエクスポートしスマホに転送すればアプリが実行できます. 今回は,convert-hf-to-gguf.pyを使い,HuggingFaceのLlama-3.2-1B-InstructをGGUF形式に変換しました. 今回はモデルパラメータをBF16, INT8にしたものを試しました.

picoLLM

Picovoice Consoleにログインするとアクセスキーが発行されるため,そのキーをアプリコード中にペーストすればアプリでモデルを動かすことができます. 実験では,Llama3.2 1B-Instructのモデルのうちビットレート5.61で量子化されているものを使用しました.

picoLLMのデモアプリは上記のように非常に簡単に実行ができますが,一方で他のフレームワークと比べると目に見えて文章の生成速度が遅かったです.

ベンチマーク測定

文章の処理速度と生成速度を比較しました. プロンプトのトークン数と生成する文章のトークン数を固定し,プロンプト処理速度は最初のトークンが出力されるまでの時間から,トークン生成速度は最初のトークンが生成されてから全てのトークンを出力するまでの時間から算出します. picoLLMやExecuTorch(QNN)は予備実験から明らかに遅いことがわかっていたため計測しておらず,ExecuTorch(XNNPACK)とllama.cppのみ比較します.

使用したデバイスは,Pixel 9 ProとGalaxy S24 Ultraの二種類のスマホです. プロンプトトークン長と生成トークン長はいくつかのバランスで試してみましたが,以下ではそれぞれを512/256とした結果を見ていきます.

まず量子化なしの場合ですが,どちらのスマホでもExecuTorchの方が速い結果となりました. llama.cppと比べ,プロンプト処理速度ではおおよそ7倍,生成速度では3-4倍ほど高速でした. llama.cppでは何もバックエンドを利用しなかったため,XNNPACKによる浮動小数点数演算の最適化が効いているのだと推測されます.

次に量子化ありの場合についてです. ExecuTorchでは元々int4に量子化されているモデルをエクスポートしたものを,llama.cppではエクスポート時にint8へ量子化したものを使いました. 結果としては,やはりExecuTorchの方が速かったです. ただ量子化なしと比べると差は縮まり,比率としてはプロンプト処理速度が3.3倍,生成速度が1.2倍ほどになりました. llama.cppでint4量子化を使えばこの差はさらに縮まると予想されます.

まとめ

今回のインターンシップでは,スマートフォン上でSLMを利用するためのフレームワークとして,ExecuTorch, llama.cpp, picoLLMの調査を実施しました. それぞれのデモアプリを試し,Llama3.2 1BモデルではXNNPACKバックエンドありのExecuTorchとllama.cppが良さそうであると見込みを立てました. これらの二つで速度に関するベンチマーク測定を実施したところ,今回の設定では量子化なし/ありにおいてもXNNPACKバックエンドありのExecuTorchが最も高速な推論をすることがわかりました.

謝辞

今回のインターンに際して,メンターをしていただいたいけかづ氏に感謝いたします. 環境構築で詰まったり(なぜかわからないがデフォルトでx86_64エミュレータを使っていたのが原因),Docker初心者で使い方がわからなかったりした際に大変多くのご助言をいただきました. また、インターンの参加に対して温かく受け入れてくださった株式会社ネフロックの社員の皆様に心から感謝します. 昼ごはんを何度もご馳走していただきました.ごちそうさまです.

研究の忙しさから予定よりもかなり延長するなど,融通を利かせていただき助かりました. 楽しいインターンシップでした,大変ありがとうございました.