数ヶ月前、私は話し合いをしました。 モバイルアプリが言語モデルの使用量の増加から利益を得ることができる会話をすることができ、歓迎され、リフレッシュされました。 デバイス SLMs in Apps 会話の後、参加者から「RAGで動作するSLMを使用するアプリの例を見ることは良いことだ」という部分のフィードバックを受けました。 これらは公平なコメントです。ここで困難なことは、モデルをセットアップし、限られた時間で開発者に意味のあるものを示すのにかかる時間です。 あなたがあなたのAndroidアプリにRAG駆動言語モデルを追加するためのステップを探している場合は、これはあなたのための投稿です! RAGって何? もしあなたがこの用語を知らないなら、RAGは これは、言語モデルがトレーニング後にデータセットに含まれていない外部情報にアクセスするためのテクニックで、モデルがプロンプトに対するより正確な回答を提供するために使用できる最新の情報を認識することを可能にします。 Retrieval Augmented Generation 簡単な例を挙げてみましょう. あなたは2つの言語モデルを持っていると想像してください. あるモデルはRAGを使用して、ヨーロッパの首都に関する外部情報を収集し、他のモデルは独自の知識にのみ依存しています. あなたは両方のモデルに次の手順を与えることを決定します: You are an expert on the geography of Europe. Give me the name of 3 capital cities. Also give me an interesting fact for each of them. 結果は、こういうものに見えるかもしれません: 図が示すように、RAGを使用する言語モデルから有用な答えを得る可能性が高く、モデルが独自の知識に依存するのではなく、RAG駆動モデルからの幻覚が少なくなることもあります。 次に、より深く進み、アプリ内で独自のRAG駆動言語モデルを有効にするコードを書こう! あなたのAndroidアプリの設定 Simon Says ゲームをプレイするために言語モデルを使用してアプリを作成したいとします。あなたはモデルが Simon であることを望んでおり、RAG を使用してタスクのデータソースにアクセスして、ユーザーに何を尋ねるかを決定するのに役立ちます。 Androidでこれを行う最も簡単な方法は、 , あなたのアプリがAIと機械学習技術を使用するのを助けるツールのコレクション. To begin, add the MediaPipe dependency to your app. : メディア build.gradle dependencies { implementation("com.google.mediapipe:tasks-genai:0.10.27") implementation("com.google.ai.edge.localagents:localagents-rag:0.3.0") } 次に、テストデバイスに言語モデルをコンピュータ経由で追加する必要があります。 軽量な言語モデルで、10億の情報パラメータを保有する。 メルマガ3-1B サイドノート:モデルをダウンロードする前に、Kaggleにサインアップし、GoogleのAI規約に同意する必要があります。 Side Note: Before downloading the model, you may have to sign upto Kaggle and agree to Google’s AI Terms and Conditions. モデルをダウンロードすると、デバイスにモデルを追加する時間です. You can do this via adb: $ adb shell mkdir -p /data/local/tmp/slm/ # Create a folder to store the model $ adb push output_path /data/local/tmp/slm/gemma3-1B-it-int4.task # Copy the model over to the file 代替として、あなたは使用することができます。 Android Studio を使用して、自分でフォルダを作成し、モデルをデバイスにドラッグします。 ファイルエクスプレーヤー モデルを追加すると、GEMMAに情報を提供するためにRAGパイプラインを構築し続けることができます。 Embeddingsの作成 RAG を実行するために依存する情報言語モデルは、あなたがそれに送信する情報と同じではありません。 . Embeddings これらはテキストのセマンティックな意味を表す数学的文字列です。モデルがプロンプトを受け取ると、それはそれに匹敵する最も関連する情報を検索するためにそれを使用し、それ自身の情報とともにそれを使用して答えを提供します。 . Embedder 埋め込みは単独で全体的なテーマであり、あなたはそれらについて読むことを奨励されます。この投稿のために、あなたはそれらを作成する方法を知る必要があります。 . Gecko Embedder まず、ダウンロード 「Tokenizer and the embedder モデル ファイルをコンピュータにインストールします. Then push them to your device: sentencepiece.model Gecko_256_f32.tflite $ adb push sentencepiece.model /data/local/tmp/slm/sentencepiece.model # Push the tokenizer to the device $ adb push Gecko_256_f32.tflite /data/local/tmp/slm/Gecko_256_f32.tflite # Push the embedder model to the device あなたのデバイスにインストールされたインベーダーで、インベーダーを作成するサンプルファイルを提供する時間です。 folder, create a file called ファイルを作成する 次に、ファイルに次のテキストを追加します。 assets simon_says_responses.txt <chunk_splitter> Go for a walk <chunk_splitter> Jump and down 10 times <chunk_splitter> Sing your favourite song! <chunk_splitter> Text your best friend a funny meme <chunk_splitter> Do 10 press ups! <chunk_splitter> このファイルには、サイモン・セイズのゲームで与えることができるいくつかの異なる答えが含まれています。 This gives the embedder a signal to know how to separate each response when splitting the text into embeddings. これは、テキストを埋め込みに分割するときに、それぞれの応答を分離する方法を知るためのシグナルを与えます。 <chunk_splitter> このプロセスは呼ばれる 言語モデルを通じてRAGがどれだけうまく機能するかに大きな影響を及ぼすことができます。異なるサイズのブロックと応答で実験して、あなたのニーズにぴったりの組み合わせを見つけることを奨励します! chunking 考えるべきことは、アプリストレージです。すでに言語モデルとインベーダーをデバイスにインストールしていることを覚えておいてください。これらはギガバイトのスペースを占めていますので、デバイスをあまりにも大きいテキストファイルを使用してさらに膨らませないようにしてください! サンプルファイルをクラウドに保存し、ネットワーク経由でダウンロードしてストレージの問題を減らすことを検討する必要があります。 テキストファイルが置かれると、インベーダーを初期化する時間です。 private const val GeckoEmbedderPath = "/data/local/tmp/slm/gecko_256_f32.tflite" private const val TokenizerModelPath = "/data/local/tmp/slm/sentencepiece.model" private const val UseGpuForEmbeddings = true val embedder: Embedder<String> = GeckoEmbeddingModel( GeckoEmbedderPath, TokenizerModelPath, UseGpuForEmbeddings, ) インベーダーは、インベーダーのパスとデバイス上のトークネイザーの3つのパラメータを取っており、最終パラメータは、インベーダーがインベーダーを作成する際にデバイスGPUを使用できるかどうかを設定します。 これを真に設定すると、GPUが利用可能な場合に組み込みの作成が加速されます。この値を有効にすることを決定する前に、デバイスの機能を確認してください。 次に、言語モデルのインスタンスを作成します。 private const val GemmaModelPath = "/data/local/tmp/slm/gemma3-1B-it-int4.task" val llmInferenceOptions = LlmInferenceOptions.builder() .setModelPath(GemmaModelPath) .setPreferredBackend(LlmInference.Backend.CPU) // Change to GPU if you have a GPU powered device. .setMaxTokens(1200) .build() val llmInferenceSessionOptions = LlmInferenceSessionOptions.builder() .setTemperature(0.6f) .setTopK(5000) .setTopP(1f) .build() val languageModel = MediaPipeLlmBackend( context, // This is the application context languageModelOptions, languageModelSessionOptions) languageModel.initialize().get() 上記のパラメータが多く含まれていますが、これらのことを今すぐ心配しないでください。 作成された言語モデルを使用すると、埋め込みに再度焦点を当てることができます。 モデルは、リクエストを受信するたびに埋め込みを取得する場所が必要です。 MediaPipe は SQLite を提供します。 , which is a common tool to store embeddings. Let's create one: 組み込みをストレージするための一般的なツールです。 Vector Store private const val PromptTemplate: String = """ You are Simon in a game of Simon Says. Your task is to ask the player to perform a task from the following list: {0}. Your response must only contain the task that the player must do. Your response must be based on the players request: {1}. Do not ask the player to do the same thing twice. You must not ask the player to do anything that is dangerous, unethical or unlawful. """ val chainConfig = ChainConfig.create( languageModel, PromptBuilder(PromptTemplate), DefaultSemanticTextMemory( SqliteVectorStore(768), embedder ) ) ここでは、言語モデルとインベーダーが ChainConfig に両方転送されると、すべてがつながり始めます。 データベースが保存できる各「ベクトル」のサイズです。 RAGプロセスを駆動するのに役立つプロンプトを提供するために使用されます。 786 PromptBuilder 最後に、資産 フォルダで作成したテキスト ファイルを embedder にロードしましょう. First, load the file from the device and split the text into a list of strings: // This is an extension function to read the file from disk. val gameResponses: List<String> = context.getTextFromFile("simon_says_responses.txt") 次に、以前作成した chainConfig に回答をロードします。 chainConfig.semanticMemory.getOrNull() ?.recordBatchedMemoryItems(ImmutableList.copyOf(gameResponses)) ?.get() あなたのデバイスとテキストファイルのサイズに応じて、これが完了するまでに数秒から数分かかることがあります。 良い指のルールは、このことが起こるときに言語モデルが使用されないようにすることです。 これで、あなたはテキストファイルをベクトルストアに保管されている組み込みのセットに変換したばかりです。 次のセクションでは、言語モデルを提示することにより、これらの埋め込みを使用する方法を示します。 あなたのRAG Poweredモデルにプロンプトを渡す 複雑な設定のほとんどが完了した場合、言語モデルにプロンプトを送信することは驚くほど簡単です。 chain config を使用して呼び出します。 RetrievalAndInferenceChain val retrievalAndInferenceChain = RetrievalAndInferenceChain(chainConfig) 次に、リクエストを作成し、チェーンに送信します。 val prompt = "Tell me something to do Simon involving jumping!" val retrievalRequest = RetrievalRequest.create( prompt, RetrievalConfig.create( 50, // topK 0.1f, // minSimilarityScore RetrievalConfig.TaskType.RETRIEVAL_QUERY ) ) val response = retrievalAndInferenceChain!!.invoke(retrievalRequest).get().text 処理中に、ベクターストアの埋め込みに参照して、それが最も正確な答えだと考えているものを提供します。 あなたが予想するように、あなたがモデルに尋ねるものは異なる答えをもたらします. 埋め込みにはシモンが言っている答えの範囲が含まれているので、あなたは良い答えを得る可能性があります! あなたが予期せぬ結果を受け取るケースはどうですか? この時点で、あなたは前回のセクションからあなたのオブジェクトのパラメータを戻して細かく調節する必要があります。 それを次のセクションで行います。 「Fine-Tuning Your RAG Powered Language Model」 Generative AI から長年にわたり学んだことがあるなら、それは正確な科学ではないということです。 あなたは間違いなく言語モデルが「間違った行動」し、望ましい結果を生み出し、企業に恥ずかしさと評判の損害を引き起こしたことがあるでしょう。 これは、モデルからの回答が期待されるように十分なテストが行われていないことの結果です. テストは恥ずかしさを避けるのに役立ちますだけでなく、言語モデル出力で実験するのに役立ちます! 私たちは、RAG駆動言語モデルを精密に調節するためにどのようなリバウンドを調整できるかを見てみましょう。 : LLmInferenceOptions.Builder LlmInferenceOptions.builder() .setModelPath(GemmaModelPath) .setPreferredBackend(LlmInference.Backend.CPU) // Change to GPU if you have a GPU powered device. .setMaxTokens(1200) .build() 変更できる最初のパラメータは、 パラメータ これは、言語モデルが処理できる「入力」または「出力」の量です. 値が大きいほど、言語モデルが一度に処理できるデータが多くなります. より良い答えが得られます. setMaxTokens() 私たちの例では、これはモデルがテキスト入力を処理し、1200トークンからなる出力を生成することができることを意味します。 より小さい価値を MaxTokens この値には気をつけてください、あなたのモデルが予想以上に多くのトークンを意外に扱うことがありますので、アプリの崩壊を引き起こします。 引っ越しましょう♪ : LlmInferenceSessionOptions.Builder() llmInferenceSessionOptions = LlmInferenceSessionOptions.builder() .setTemperature(0.6f) .setTopK(5000) .setTopP(1f) .build() ここでは、いくつかの異なるパラメータを設定することができます。 で、 そして、 それらをそれぞれに掘り下げていきましょう。 .setTemperature() .setTopK() .setTopP() 言語モデルからの応答がどれほど「ランダム」なのかを考えることができます。値が低ければ低ければ低ければ低ければ低ければ低ければ低ければ低ければ低いので、モデルからの応答が「予測可能」になります。 .setTemperature() この例では、設定は つまり、モデルは半創造的な反応を提供するが、想像できないものではない。 0.6 温度は、あなたがあなたの使用ケースに応じてより良い反応を提供する異なる値を見つけるかもしれないので、実験するための良い価値です。 言語モデルは、プロンプトを処理しながら、数多くの応答を生成し、潜在的に数千を生成します! .setTopK() これらの答えのそれぞれに、どれだけ正しい答えになる可能性があるかという確率が与えられています。 値は、モデルを焦点にするのに役立つように設定できます. あなたは、不確実な回答が考慮されることに満足している場合は、この値を高く設定したいと思います。 topK 温度属性と同様に、これは実験するのに良い属性です. あなたはあなたのニーズに応じて、より少ないまたはより多くの応答でモデルがよりよく機能していることがわかるかもしれません。 サイモン・セイズのゲームでは、モデルがゲームを新鮮に保つためにさまざまな反応を考えていることを望んでいます。 いい値段のようだ。 5000 設定された限界に基づく 言語モデルは、以前述べたように、「P の確率を持つ結果だけを考慮する」と言い、それが生成する各応答にどれほどの確率があるかについて確率を割り当てます。 つまり、モデルは必要な最小確率を持たないすべての応答を簡単に排除することができます。 .setTopP() topK topP 例を挙げると、モデルが セット 2 次のような回答を検討していた。 topP 0.4 Simons Says clap 5 times! = 0.7 // This response has a probablility of 0.7 of being correct Simons Says jump up and down! = 0.5 Find a car and drive it. = 0.3 最初の2つの答えは、確率がより高いので考慮されます。 車についての答えは、その可能性があるため、排除されるだろう。 . 0.4 0.3 似たもの そして で、 言語モデルがどれほどクリエイティブであるかを定義することを可能にします. If you want to consider less likely responses to prompts, setting a low P value will help. 言語モデルがどれほどクリエイティブであるかを定義することを可能にします。 temperature topK topP 私の例では、それは設定されています。 それは、モデルがその反応について絶対に確信していることを望んでいるからだ! 結局は子供の遊びだ! 1.0 これらの値を実験すると、あなたの言語モデルとは非常に異なる結果が生成されます。 次はどこに行こうか。 私はあなたがあなたのAndroidアプリにRAG駆動言語モデルを追加する方法のこのウォークアウトを楽しんだことを願っています! RAGとAndroidについてもっと知りたい場合は、ここに私が推奨するいくつかのリンクがあります: Clone and run the for this blog post to see it in action. It shows how to setup the RAG pipeline with Gemma using Android architecture best practices. Simons Says App: sample code : Check out the RAG section on the MediaPipe - Highly recommended reading. MediaPipe RAG Google Developer docs : Learn more about how setting the Temperature, TopK, and TopP values can the results from language models. Another highly recommended article. Setting Temperature, TopK, and TopP in LLMs help control