Slide 1

Slide 1 text

モバイル端末で動く LLM は どこまで実用的なのか FlutterKaigi 2025

Slide 2

Slide 2 text

About Me 伊藤 恭平 Github: KyoheiG3 X: @KyoheiG3

Slide 3

Slide 3 text

なぜ今「オンデバイス LLM」なのか?

Slide 4

Slide 4 text

クラウド API とオンデバイス LLM の比較 比較項目 クラウド API オンデバイス LLM 応答速度 △ (通信時間が発生) ◎ (通信不要) 利用環境 ✕ (インターネット必須) ◎ (どこでも使える) プライバシー △ (データを外部に送信) ◎ (端末内で完結) コスト △ (API 利用料・サーバー) ◯ (API 利用料ゼロ) モデル性能/機能 ◎ (常に最新・巨大モデル) △ (小型モデル・機能制限)

Slide 5

Slide 5 text

市場動向 Apple Intelligence iOS 18.1 以降でオンデバイス AI 機能を提供 Private Cloud Compute でハ イブリッド処理を実現 Writing Tools、Genmoji、 Image Playground を搭載 Gemini Nano Android 14 以降の対応端末で 利用可能 AICore 経由でシステムレベル の AI を提供 Pixel 8 以降、Galaxy S24 シ リーズなどで動作

Slide 6

Slide 6 text

技術の進化 ハードウェア モバイルチップの性能向上 省電力化 モデル アーキテクチャの進化 ファインチューニング 軽量化

Slide 7

Slide 7 text

これからはオンデバイス LLM が主流に?

Slide 8

Slide 8 text

Agenda 1. オンデバイス LLM の基礎知識 2. Flutter で LLM を動かす選択肢 3. オンデバイス LLM の応用機能 4. パフォーマンスと実践的な考慮点

Slide 9

Slide 9 text

Agenda 1. オンデバイス LLM の基礎知識 2. Flutter で LLM を動かす選択肢 3. オンデバイス LLM の応用機能 4. パフォーマンスと実践的な考慮点

Slide 10

Slide 10 text

オンデバイス LLM の基礎知識 注目のモデル パラメータ数 量子化

Slide 11

Slide 11 text

モバイルで注目の LLM モデル 特徴 Google Gemma Google AI Edge に最適化 Meta Llama オープンソース、豊富なコミュニティ Microsoft Phi-3 小規模ながら高性能 DeepSeek 高い推論・コーディング能力 Qwen Alibaba の多言語対応モデル

Slide 12

Slide 12 text

パラメータ数とは? LLM が学習によって得た知識を保持するための変数の総数で、主に 「重み」と「バイアス」という 2 種類の数値で構成されている 4B = 40 億個のパラメータ 7B = 70 億個のパラメータ

Slide 13

Slide 13 text

パラメータ数は多いほど良いのか? 表現力が高く、より高品質な応答 モデルのファイルサイズが巨大になり、動作させるために大量のメモリ と高い計算能力が必要になる

Slide 14

Slide 14 text

量子化(Quantization) 量子化とは、モデルの「パラメータ数」を変えずに、各パラメータが 使用するデータのサイズを小さくする技術で、モデルのサイズと計算 速度が大幅に改善される モデルサイズが劇的に縮小(例: 14GB → 3.5GB) 計算速度が大幅に向上 トレードオフとしてわずかな精度低下

Slide 15

Slide 15 text

量子化のイメージ 32 ビット浮動小数点(FP32) [0.17384529, -1.40821743, 0.98712456, -0.02941837, ...] ↓ 8 倍圧縮 8 ビット整数(INT8) 範囲: -128 ~ 127 [14, -115, 80, -2, ...] ↓ さらに 2 倍圧縮 4 ビット整数(INT4) 範囲: -8 ~ 7 [7, -8, 4, -1, ...]

Slide 16

Slide 16 text

パラメータ数と量子化の関係性の具体例 量子化前 量子化後 パラメータ数 7B (70 億) 7B (70 億) ビット幅 32 ビット (FP32) 4 ビット (INT4) サイズ(概算) 約 28GB 約 3.5GB ※量子化前サイズ概算: 70 億 ×4 バイト = 約 280 億バイト = 約 28GB ※量子化後サイズ概算: 70 億 ×0.5 バイト = 約 35 億バイト = 約 3.5GB

Slide 17

Slide 17 text

ファイル名から見る量子化の種類 gemma-3n-E4B-it-int4.task 指標 意味 E4B 40 億パラメータモデル it 指示チューニング(ファインチューニング)済み int4 4 ビット量子化

Slide 18

Slide 18 text

Agenda 2. Flutter で LLM を動かす選択肢 1. オンデバイス LLM の基礎知識 3. オンデバイス LLM の応用機能 4. パフォーマンスと実践的な考慮点

Slide 19

Slide 19 text

Flutter で LLM を動かす選択肢 どのフォーマットのモデルを使うかで、利用するライブラリが変わる モデルフォーマット ライブラリ例 GGUF llama.cpp ベース Task(LiteRT-LM) MediaPipe ベース Cactus Cactus Compute ベース

Slide 20

Slide 20 text

GGUF フォーマット 基本的にはマルチモーダル未対応のテキストベースモデルで、CPU で の高速推論に最適化されている モデルとトークナイザが一体化されている 量子化済みモデルのサポートが充実 一部のモデルでマルチモーダル対応が可能 llama_cpp_dart などのパッケージで利用可能

Slide 21

Slide 21 text

llama_cpp_dart(テキスト) Llama.libraryPath = 'bin/MAC_ARM64/libllama.dylib'; final llama = Llama( '/path/to/model.gguf', ); llama.setPrompt('2 + 2 = ?'); while (true) { var (token, done) = llama.getNext(); print(token); if (done) break; } llama.dispose();

Slide 22

Slide 22 text

llama_cpp_dart(画像 + テキスト) Llama.libraryPath = 'bin/MAC_ARM64/libmtmd.dylib'; final llama = Llama( '/path/to/model.gguf', ... '/path/to/mmproj-model.gguf', ); final image = LlamaImage.fromFile(File('/path/to/image.png')); final prompt = """ user 画像について説明してください。 model """; final stream = llama.generateWithMedia(prompt, inputs: [image]); await for (final token in stream) { print(token); } llama.dispose();

Slide 23

Slide 23 text

Task(LiteRT-LM)フォーマット マルチモーダル対応で GPU や NPU を活用した高速推論に最適化さ れていて、ファイル自体は unzip 可能 マルチモーダル対応(テキスト、画像など) GPU や NPU を活用した高速推論 ファイルは unzip 可能で中身を確認できる flutter_gemma や ai_edge などのパッケージで利用可能

Slide 24

Slide 24 text

flutter_gemma(テキスト) await FlutterGemma.installModel( modelType: ModelType.gemmaIt, ).fromNetwork( 'url_to_model', ).install(); final model = await FlutterGemma.getActiveModel(); final chat = await model.createChat(); await chat.addQueryChunk(Message.text( text: '2 + 2 = ?', isUser: true, )); await for (final response in chat.generateChatResponseAsync()) { if (response is TextResponse) { print(response.token); } } await chat.close(); await model.close();

Slide 25

Slide 25 text

flutter_gemma(画像 + テキスト) final model = await FlutterGemma.getActiveModel( preferredBackend: PreferredBackend.gpu, supportImage: true, ); final chat = await model.createChat(supportImage: true); await chat.addQueryChunk(Message.withImage( text: ' 画像について説明してください。', imageBytes: imageBytes, isUser: true, )); await for (final response in chat.generateChatResponseAsync()) { if (response is TextResponse) { print(response.token); } } await chat.close(); await model.close();

Slide 26

Slide 26 text

Cactus フォーマット オンデバイスでの利用に特化しており、 ARM CPU アーキテクチャ に最適化されている ARM CPU アーキテクチャに最適化 FFI を利用したクロスプラットフォーム対応 Flutter、React Native、KMP で利用可能 cactus-flutter パッケージで利用可能

Slide 27

Slide 27 text

cactus-flutter(テキスト) final lm = CactusLM(); await lm.downloadModel(model: 'qwen3-0.6'); await lm.initializeModel(); final streamedResult = await lm.generateCompletionStream( messages: [ChatMessage(content: '2 + 2 = ?', role: 'user')], ); await for (final chunk in streamedResult.stream) { print(chunk); } lm.unload();

Slide 28

Slide 28 text

cactus-flutter(音声) final stt = CactusSTT(); await stt.download(model: 'whisper-tiny'); await stt.init(model: 'whisper-tiny'); final result = await stt.transcribe(); print(result.text); stt.dispose();

Slide 29

Slide 29 text

ai_edge MediaPipe ベース モデルのダウンロード マルチモーダル対応

Slide 30

Slide 30 text

ai_edge での実行例 final downloader = ModelDownloader(); final result = await downloader.downloadModel( Uri.parse('url_to_model'), ); await AiEdge.instance.initialize( modelPath: result.filePath, ); final stream = AiEdge.instance.generateResponseAsync('2 + 2 = ?'); await for (final event in stream) { print(event.partialResult); }

Slide 31

Slide 31 text

設定可能なパラメータ例 await aiEdge.initialize( modelPath: '/path/to/model.task', // 必須: モデルファイルのパス maxTokens: 2048, // 最大生成トークン数 preferredBackend: PreferredBackend.gpu, // ハードウェアバックエンド maxNumImages: 3, // マルチモーダル入力時の最大画像数 temperature: 0.7, // ランダム性の制御(0.0-1.0 ) randomSeed: 42, // 再現性のための乱数シード topK: 50, // Top-K サンプリング topP: 0.95, // Top-P (nucleus )サンプリング supportedLoraRanks: [4, 8], // LoRA アダプターのランク(ファインチューニング用) loraPath: '/path/to/lora_adapter.bin', // LoRA アダプターのパス enableVisionModality: true, // Vision 機能の有効化 );

Slide 32

Slide 32 text

実行時の注意点 メモリはモデルサイズに応じて 4-8GB 以上必要 モデルサイズが大きい場合はアプリの設定変更が必要 巨大なモデルのアプリへのバンドルは不可能 com.apple.developer.kernel.increased-memory-limit

Slide 33

Slide 33 text

HuggingFace AI モデルを共有・公開するプラットフォームで、アカウント登録を行 うことで数万種類の学習済みモデルが無料で利用可能 モデルによってはライセンス同意が必要(Llama、Gemma など) huggingface_hub(Python) や Web からダウンロード可能 商用利用時はモデルごとのライセンス条項を必ず確認

Slide 34

Slide 34 text

Agenda 3. オンデバイス LLM の応用機能 1. オンデバイス LLM の基礎知識 2. Flutter で LLM を動かす選択肢 4. パフォーマンスと実践的な考慮点

Slide 35

Slide 35 text

オンデバイス LLM の応用機能 モデルが学習した時点までの知識しかない 今何時? → リアルタイムの時刻がわからない 今日の東京の天気は? → 最新の天気情報を取得できない 2025 年の出来事は? → 学習時点以降の情報を持たない

Slide 36

Slide 36 text

Function Calling (Tool)

Slide 37

Slide 37 text

Function Calling(Tool) LLM に外部の関数や API を呼び出させる仕組みで、モデルの知識を 補完し、最新情報や特定の機能を利用可能にする 天気情報の取得 計算 データベース検索 etc.

Slide 38

Slide 38 text

Function Calling(Tool) の流れ 1. 利用可能な関数のスキーマ(名前・引数・説明)を定義 2. ユーザーのリクエストと関数スキーマを LLM に渡す 3. LLM が必要な関数と引数を JSON 形式で返答 4. アプリ側で実際の関数を実行 5. 関数の実行結果を LLM に渡す 6. LLM が結果を踏まえてユーザーに最終回答を生成

Slide 39

Slide 39 text

ai_edge での関数設定例 final getWeather = FunctionDeclaration( name: 'get_weather', description: 'Get current weather for a location', properties: [ FunctionProperty( name: 'location', description: 'City name', type: PropertyType.string, required: true, ), ], ); await aiEdge.setFunctions([getWeather]);

Slide 40

Slide 40 text

ai_edge での関数実行例 final response = await aiEdge.sendMessage( Message(role: 'user', text: ' 今日の東京の天気を教えてください。'), ); if (response.functionCall != null) { final call = response.functionCall!; switch (call.name) { case 'get_weather': final location = call.args.fields['location'] as String; final weather = await getWeather(location); final functionResponse = FunctionResponse( functionCall: call, response: {'result': weather}, ); final finalResponse = await aiEdge.sendFunctionResponse(functionResponse); print(finalResponse.text); } }

Slide 41

Slide 41 text

実装時の注意点 LLM が意図しない値を JSON で返す可能性がある 実行する関数を制限・検証してセキュリティの対策が必要

Slide 42

Slide 42 text

ai_edge を利用した Function Calling の制約 Android でしか利用できません(現状では)

Slide 43

Slide 43 text

iOS での実装に挑戦しましたが... FST 制約の実装がなく、Android 用バイナリの提供しかされていない 別のライブラリを組み込む必要がある

Slide 44

Slide 44 text

FST 制約とは? LLM の出力を特定の形式やルールに従うよう制約する技術 JSON などの構造化フォーマットの厳密な遵守を強制 出力の一貫性と信頼性が向上 意図しない「内容」は返される可能性がある

Slide 45

Slide 45 text

プロンプトエンジニアリングによる FST の代替 プロンプト設計により関数呼び出しの形式を模倣可能 FST 制約なしで実装できるが、フォーマット遵守の信頼性は低下 システム: あなたは天気情報を提供する関数 get_weather(location: "location") を持っています。 天気に関する質問には以下のような json 形式で必要な関数を呼び出してください `{ "function": "get_weather", "args": { "location": " 東京" } }` ユーザー: 今日の大阪の天気を教えてください。

Slide 46

Slide 46 text

プロンプトエンジニアリングの注意点 関数呼び出しのフォーマットを明確に定義する Few-shot 例を示して LLM にフォーマットを学習させる 小型モデルでは指示に従わないケースがある JSON パースエラーへの対応が必須

Slide 47

Slide 47 text

Function Calling(Tool)まとめ 関数呼び出しと SaaS API などの組み合わせで最新情報を取得可能 プロンプトエンジニアリングという代替手段もある

Slide 48

Slide 48 text

Retrieval Augmented Generation (RAG)

Slide 49

Slide 49 text

Retrieval Augmented Generation(RAG) 膨大なドキュメントから関連性の高い情報を検索し、LLM に参考情 報として渡すことで、モデルの知識を補完する仕組み 社内 FAQ システム 技術ドキュメント検索 製品マニュアル対応

Slide 50

Slide 50 text

RAG の流れ 1. テキストのクリーニングとチャンク分割 2. ベクトル化(Embedding) 3. Vector DB の構築 4. クエリのベクトル化と検索 5. 検索結果を基に回答を生成

Slide 51

Slide 51 text

RAG システムの構成要素 要素 説明 Tokenizer テキストをトークンに分割 Embedder トークンをベクトルに変換 Vector DB ベクトルとテキストを保存・検索 Retriever 検索プロセス全体を統括

Slide 52

Slide 52 text

ai_edge での実行例 await aiEdge.createEmbeddingModel( tokenizerModelPath: '/path/to/tokenizer.model', embeddingModelPath: '/path/to/embedding.tflite', modelType: EmbeddingModelType.gemma, vectorStore: VectorStore.sqlite, preferredBackend: PreferredBackend.gpu, ); await aiEdge.memorizeChunks([ 'Flutter は UI フレームワーク', 'Python で機械学習を実装', 'Dart 言語でアプリ開発', ]);

Slide 53

Slide 53 text

ai_edge での検索例 final stream = aiEdge.generateResponseAsync( 'Flutter の特徴を教えてください。', ); await for (final event in stream) { print(event.partialResult); }

Slide 54

Slide 54 text

RAG 実装時の注意点 利用するドキュメントの前処理とチャンク分割を適切に行う 検索結果とプロンプトのトークン上限を管理

Slide 55

Slide 55 text

ai_edge を利用した RAG の制約 Android でしか利用できません(現状では)

Slide 56

Slide 56 text

iOS での実装に挑戦しましたが... Android 用のバイナリしか提供されていない Text Chunking や Embedding、Vector DB の実装が必要 完全模倣するには Function Calling より実装ハードルが高いかも

Slide 57

Slide 57 text

Text Chunking とは? テキストを意味的な単位に分割するプロセスで、検索精度や処理効率 を向上させる 自然な文の境界で分割(例: 文、段落、セクション) チャンクサイズの最適化(例: 500 トークン程度) 長いドキュメント(例: 技術文書、3000 トークン) ↓ Text Chunking チャンク1: "LLM とは大規模言語モデルのことで..." (500 トークン) チャンク2: " 量子化はモデルサイズを削減する技術で..." (500 トークン) チャンク3: "RAG は外部知識を活用する手法で..." (500 トークン) チャンク4: "Flutter での実装方法として..." (500 トークン) ...

Slide 58

Slide 58 text

Embedding とは? テキストデータを数値ベクトルに変換する技術で、意味的な類似性を 計算可能にする 事前学習済みの軽量モデルを利用 ベクトル空間での類似度計算 " 猫が好き" ↓ Tokenizer [" 猫", " が", " 好き"] → [1234, 5, 6789] ↓ Embedder [0.23, -0.41, 0.87, 0.15, ...] ← 文全体のベクトル(例: 384 次元)

Slide 59

Slide 59 text

Vector DB とは? ベクトル化されたドキュメントを保存し、高速な類似度検索を可能に するデータベース インメモリ実装または永続化実装を選択可能 FAISS、ObjectBox、SQLite + ベクトル拡張などが利用可能 Vector DB: ID | 元の文章  | ベクトル 1 | "Flutter は UI フレームワーク" | [0.23, -0.41, 0.87, 0.15, ...] 2 | "Python で機械学習を実装" | [0.11, 0.34, -0.56, 0.78, ...] 3 | "Dart 言語でアプリ開発" | [-0.45, 0.67, 0.12, -0.34, ...] ... 検索結果: ID 1 ( 類似度 0.98), ID 3 ( 類似度 0.85)

Slide 60

Slide 60 text

RAG まとめ 公開できない社内文書や機密情報を含めた回答生成 ベクトル検索と推論の両方の処理の負荷に注意

Slide 61

Slide 61 text

Agenda 4. パフォーマンスと実践的な考慮点 1. オンデバイス LLM の基礎知識 2. Flutter で LLM を動かす選択肢 3. オンデバイス LLM の応用機能

Slide 62

Slide 62 text

モデル(ライブラリ)の選択について どれくらい複雑なタスクの処理が必要か マルチモーダルに対応する必要があるか 日本語の入出力や応答速度の要件

Slide 63

Slide 63 text

デバイスについて メモリはモデルに応じて 4-8GB 以上を推奨 実機での推論速度は 1B-4B モデルで 5-20 トークン/秒程度

Slide 64

Slide 64 text

アプリへの組み込みについて モデルのサイズが大きいためアプリにバンドル は困難 初回起動時などに別途ダウンロードする仕組み を用意する 大きいモデルを利用する場合はアプリの設定変 更が必要

Slide 65

Slide 65 text

アプリの UX について 定型の文字入力や音声入力など、入力体験を工 夫する必要がある トークンの上限が小さいので、長い会話の履歴 の保持が難しい 文字出力の体験としてはストリーミングが好ま しいが、 Function Calling などと相性が悪い 場合もある

Slide 66

Slide 66 text

Function Calling の推論回数 final response = await aiEdge.sendMessage( Message(role: 'user', text: ' 今日の東京の天気を教えてください。'), ); if (response.functionCall != null) { final call = response.functionCall!; switch (call.name) { case 'get_weather': final location = call.args.fields['location'] as String; final weather = await getWeather(location); // 再度推論は行わずに、関数の実行結果をユーザーに返すように工夫する } }

Slide 67

Slide 67 text

応答速度と回答品質の改善 パラメータ数が多いほど高品質だが、軽量なモデルの方が速い 日本語より英語の方が、コスト、速度、品質の面で有利 システムプロンプトや Few-shot 学習を活用して回答の品質を向上

Slide 68

Slide 68 text

オンデバイス LLM で利用できるライブラリ ライブラリ 画像入力 音声入力 Function Calling RAG llama_cpp_dart (プロンプト) flutter_gemma (プロンプト) ai_edge (未実装) (Android) (Android) cactus-flutter

Slide 69

Slide 69 text

まとめ Flutter での実装も徐々に現実的になってきている モデル選択、パフォーマンス最適化、UX デザインが成功の鍵 一般ユーザー向けのアプリではまだまだ課題が多い

Slide 70

Slide 70 text

参考 URL ai_edge: https://github.com/KyoheiG3/ai_edge llama_cpp_dart: https://github.com/netdur/llama_cpp_dart flutter_gemma: https://github.com/DenisovAV/flutter_gemma cactus-flutter: https://github.com/cactus-compute/cactus- flutter HuggingFace: https://huggingface.co/

Slide 71

Slide 71 text

Thanks! Github: KyoheiG3 X: @KyoheiG3