몇 달 전, 나는 이야기를 나누었다. 모바일 앱이 언어 모델 사용의 상승에서 이익을 얻을 수있는 대화를 할 수있는 것이 잘 받아 들여지고 신선했습니다. 앱에서 장치 SLMs에서 실행 토론 후에 참석자들로부터 몇 조각의 피드백을 받았습니다. "RAG에 의해 구동되는 SLM을 사용하는 앱의 예를 보는 것이 좋을 것입니다." 이들은 공정한 의견입니다. 여기에 어려운 것은 모델을 설정하고 제한된 시간에 개발자에게 의미있는 것을 보여주는 데 걸리는 리드 타임입니다. 당신이 당신의 안드로이드 앱에 RAG-powered 언어 모델을 추가하는 단계를 찾고 있다면, 이것은 당신을위한 포스트입니다! RAG 란 무엇입니까? 당신이 용어에 익숙하지 않은 경우, RAG는 이것은 언어 모델이 훈련 후 데이터 세트에서 사용할 수없는 외부 정보에 액세스하는 기술입니다.This allows models to be aware of up-to-date information they can use to provide more accurate answers to prompts. Retrieval Augmented Generation 한 모델은 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를 사용하는 언어 모델에서 유용한 답변을 얻을 수 있습니다.You may also notice less hallucinations from the RAG-powered model, as it doesn't need to make up information. 다음으로, 더 깊게 가서 앱 내에서 자신의 RAG-powered 언어 모델을 활성화 할 수있는 코드를 작성합시다! 귀하의 Android 앱 설정 Simon Says 게임을 재생하기 위해 언어 모델을 사용하여 앱을 만들고 싶다고 가정 해 봅시다. 모델이 Simon이 되기를 원하고 RAG를 사용하여 작업 데이터 원본에 액세스하여 사용자에게 무엇을 요청해야 하는지 결정하도록 하자. 안드로이드에서 가장 간단한 방법은 , 앱이 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") } 다음으로 컴퓨터를 통해 테스트 장치에 언어 모델을 추가해야 합니다.이 예제에서는 Google의 언어 모델을 사용합니다. 정보 가치가 10 억 개의 매개 변수를 포함하는 가벼운 언어 모델입니다. 엠마3-1B Side Note: 모델을 다운로드하기 전에 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. 모델이 다운로드되면 장치에 모델을 추가 할 시간입니다. 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 대신, 당신은 그것을 사용할 수 있습니다 안드로이드 스튜디오를 사용하여 폴더를 직접 만들고 모델을 장치에 끌어 당기십시오. 파일 탐색기 모델이 추가되면 Gemma에 정보를 공급하기 위해 RAG 파이프라인을 계속 건설할 수 있습니다.다음으로, Gemma가 응답 요청에 의존하는 정보를 추가하는 것을 살펴보자. Embeddings 만들기 RAG를 수행하기 위해 의존하는 정보 언어 모델은 당신이 그것에 전달하는 동일한 정보가 아닙니다.Models require information to be in a specific format called RAG. . Embeddings 이들은 텍스트의 상징적 의미를 나타내는 수학적 문자열입니다.모델이 인스턴스를받을 때, 그것은 그것에 맞는 가장 관련 정보를 찾기 위해 그것을 사용하고 자신의 정보와 함께 그것을 사용하여 대답을 제공합니다.이 수학적 문자열은 도구라고 불리는 도구에 의해 만들어집니다. . Embedder 삽입은 그 자체로 전체 주제입니다; 당신은 그들에 대해 읽는 것이 좋습니다.이 포스트를 위해, 당신은 단지 그들을 만드는 방법을 알아야합니다. . Gecko Embedder에 대한 리뷰 보기 우선, download the Tokenizer 및 The 모델 파일을 컴퓨터에 삽입한 다음 장치에 눌러주세요: 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 장치에 설치된 embedder를 사용하면, 삽입을 만들기 위해 샘플 파일을 제공할 시간입니다.In Android Studio, in your app module 폴더, 이름의 파일을 만들기 그런 다음 파일에 다음 텍스트를 추가하십시오: 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> 이 파일은 Simon Says의 게임에서 제공 할 수있는 몇 가지 다른 응답을 포함하고 있으며, 각각은 하나의 응답으로 나니다. 이것은 삽입자가 텍스트를 삽입물로 분할 할 때 각 응답을 분리하는 방법을 알 수있는 신호를 제공합니다.This gives the embedder a signal to know how to separate each response when splitting the text into embeddings. <chunk_splitter> 이 과정은 소위 언어 모델을 통해 RAG가 얼마나 잘 수행하는지에 큰 영향을 미칠 수 있습니다.다른 크기의 조각과 응답을 실험하여 필요에 맞는 조합을 찾는 것이 좋습니다! chunking 고려해야 할 일 중 하나는 앱 스토리지입니다. 이미 언어 모델과 장치에 삽입기를 설치한 것을 기억하십시오.이 장치는 공간의 기가바이트를 차지하므로 너무 큰 텍스트 파일을 사용하여 장치를 더 팽창시키지 않도록하십시오! 저장 문제를 줄이기 위해 샘플 파일을 클라우드에 저장하고 네트워크를 통해 다운로드하는 것을 고려하십시오.You want to consider storing the sample file in the cloud and downloading it via the network to reduce problems with storage. 텍스트 파일이 위치에 있으면 embedder를 초기화하는 시간입니다.With the text file in place, it is time to initialize the embedder: 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, ) Embedder는 embedder의 경로와 장치의 tokenizer, 마지막 매개 변수를 사용하여 장치 GPU를 사용할 수 있는지 여부를 설정합니다.The embedder takes three parameters: the path of the embedder and the tokenizer on the device, with a final parameter to set whether the embedder can use the device GPU when creating embeddings. 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() 위에 포함 된 많은 매개 변수가 있습니다; 지금 당장 이들에 대해 걱정하지 마십시오. 언어 모델이 생성되면 삽입에 다시 집중할 수 있습니다.모델은 삽입을 수신할 수 있는 장소가 필요합니다.The model needs a place to retrieve the embeddings each time it receives a prompt. MediaPipe는 SQLite를 제공합니다. , 삽입을 저장하는 일반적인 도구입니다.우리는 하나를 만들자: 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 ) ) 여기서 모든 것이 언어 모델과 embedder가 ChainConfig로 전달됨에 따라 연결되기 시작합니다. 데이터베이스가 저장할 수 있는 각 "벡터"의 크기입니다. RAG 프로세스를 구동하는 데 도움이 되는 프롬프트를 제공하는 데 사용됩니다. is used to provide a prompt to help drive the RAG process. 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() 귀하의 장치와 텍스트 파일의 크기에 따라 완료하는 데 몇 초에서 몇 분이 걸릴 수 있습니다. 좋은 규칙은 언어 모델이 발생하는 동안 사용되지 않도록하는 것입니다.당신은 또한 주요 스레드의 차단을 피하기 위해 IO 스레드에서이 작업을 실행할 수 있습니다. 이렇게하면 텍스트 파일을 벡터 스토어에 보관된 삽입 세트로 변환했습니다.당신은 또한 언어 모델을 스토어에 연결하여 이제 정보를 검색할 수 있습니다! 다음 섹션은 언어 모델을 통해 이러한 삽입을 사용하는 방법을 보여줍니다.The next section will show how to use those embeddings by passing the language model your prompts. 당신의 RAG Powered 모델에 Prompts 전달 대부분의 복잡한 설치가 완료되면 언어 모델에 인스턴트를 전달하는 것은 놀랍게도 쉽습니다. 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 처리 중에는 벡터 스토어의 삽입을 참조하여 가장 정확한 답이라고 생각하는 것을 제공합니다.During processing, it will refer to the embeddings in your vector store to provide what it thinks is the most accurate answer. 당신이 예상했듯이, 당신이 모델에게 묻는 것은 다른 응답을 가져올 것입니다. 삽입에는 시몬이 말하는 응답의 범위가 있기 때문에, 당신은 좋은 응답을 얻을 가능성이 있습니다! 예기치 않은 결과를 얻는 경우에 대해 어떻게 생각하십니까?이 시점에서 이전 섹션에서 객체의 매개 변수를 돌려서 정밀하게 조정해야합니다. 다음 섹션에서 그렇게 하자. 귀하의 RAG Powered 언어 모델을 최적화 수년 동안 Generative AI에서 배운 한 가지가 있다면 그것이 정확한 과학이 아니라는 것입니다. 언어 모델이 "잘못 행동"하고 원하는 결과보다 적은 결과를 가져왔으며 회사에 부끄러움과 명성 손상을 유발 한 이야기를 의심 할 여지가 없습니다. 이것은 모델의 응답이 기대되는지 확인하기 위해 충분한 테스트가 수행되지 않는 결과입니다. 테스트는 부끄러움을 피하는 데 도움이 될뿐만 아니라 언어 모델 출력을 실험하여 더 잘 작동하는 데 도움이 될 수 있습니다! 우리가 우리의 RAG-powered 언어 모델을 잘 조정하기 위해 어떤 리버를 조정할 수 있는지 살펴보자. : 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 토큰으로 구성된 출력을 생성할 수 있다는 것을 의미합니다.If we wanted to handle less text and also generate a smaller response, we could set 더 작은 가치가 있습니다. MaxTokens 예기치 않게 모델이 기대하는 더 많은 토큰을 처리 할 수 있기 때문에이 값을 조심하십시오.Causing an app crash. 이곳으로 옮겨가자 : LlmInferenceSessionOptions.Builder() llmInferenceSessionOptions = LlmInferenceSessionOptions.builder() .setTemperature(0.6f) .setTopK(5000) .setTopP(1f) .build() 여기, 당신은 몇 가지 다른 매개 변수를 설정할 수 있습니다. , 그리고 우리는 그들 각각에 몰입하자. .setTemperature() .setTopK() .setTopP() 언어 모델에서 얻은 응답이 얼마나 “연수”할 수 있는지 생각할 수 있다.그 값이 낮을수록 모델에서 얻은 응답이 더 “예측가능하다”고 생각할 수 있다.그 값이 높을수록 그 응답이 더욱 “창조적”일 것이며, 예상치 못한 응답을 더 많이 가져올 수 있다. .setTemperature() 이 예를 들어, 그것은 설정되어 있습니다 즉, 모델은 반 창조적이지만 상상할 수없는 반응을 제공 할 것입니다. 0.6 온도는 사용 사례에 따라 더 나은 응답을 제공하는 다른 값을 찾을 수 있기 때문에 실험하기 좋은 가치입니다. 언어 모델은 요청을 처리하는 동안 수많은 응답, 잠재적으로 수천을 생성합니다! .setTopK() 각각의 응답은 그들이 올바른 대답일 가능성에 대한 확률을 부여받습니다. 덜 가능성이 높은 응답을 고려하는 것으로 만족한다면, 이 값을 높게 설정할 수 있습니다.If you are happy with less likely responses being considered, you would like to set this value high. topK 온도 속성과 마찬가지로, 이것은 실험 할 수있는 좋은 속성입니다.당신은 당신의 필요에 따라 고려해야 할 더 적은 또는 더 많은 응답으로 모델이 더 잘 작동하는 것을 발견 할 수 있습니다. Simon Says의 게임을 위해, 우리는 모델이 게임을 신선하게 유지하기 위해 많은 다른 반응을 생각하고 싶습니다. 좋은 가치가 있는 것 같아요. 5000 설정된 제한에 따라 이전에 언급했듯이, 언어 모델은 그것이 생성하는 각 응답에 얼마나 가능성이 있는지에 대한 확률을 할당합니다. 즉, 모델은 필요한 최소한의 확률이 없는 모든 응답을 쉽게 버릴 수 있습니다. .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 첫 번째 두 가지 응답은 확률이 더 높기 때문에 고려될 것입니다. 차에 대한 답은 버려질 것이며, 그것은 오직 확률을 가지고 있기 때문이다. . 0.4 0.3 비슷한 그리고 , 언어 모델이 얼마나 창조적일 수 있는지 정의할 수 있습니다.Pompts에 대한 응답이 덜 가능하다면 낮은 P 값을 설정하는 것이 도움이 됩니다.If you want to consider less likely responses to prompts, setting a low P value will help. temperature topK topP 우리의 예를 들어, 그것은 설정되어 있습니다 그것은 우리가 모델이 반응에 대해 절대적으로 확신하기를 원하기 때문입니다.그것은 어쨌든 아이들의 게임을하고있다! 1.0 이러한 값을 실험하면 언어 모델과 매우 다른 결과를 얻을 수 있습니다.Try them out and see what happens! 다음은 어디로 가시나요? 나는 당신이 당신의 안드로이드 앱에 RAG 지원 언어 모델을 추가하는 방법에 대한이 도전을 즐겼기를 바랍니다! 당신이 RAG와 안드로이드에 대해 더 많은 것을 배우고자하는 경우, 여기에 몇 가지 링크를 권장합니다 : 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