Vài tháng trước, tôi đã nói về Nó đã được chào đón tốt và làm mới để có thể đưa ra một cuộc trò chuyện nơi các ứng dụng di động có thể có được lợi thế từ sự gia tăng sử dụng mô hình ngôn ngữ. chạy trên thiết bị SLMs trong ứng dụng Sau khi nói chuyện, tôi đã có một vài phản hồi từ những người tham gia dọc theo dòng "Sẽ tốt khi thấy một ví dụ về một ứng dụng sử dụng SLM được cung cấp bởi RAG." Đây là những nhận xét công bằng. điều khó khăn ở đây là thời gian dẫn nó mất để thiết lập một mô hình và hiển thị một cái gì đó có ý nghĩa cho các nhà phát triển trong một thời gian giới hạn. May mắn thay, nó làm cho một bài đăng blog tuyệt vời! Vì vậy, đây là nó! Nếu bạn đang tìm kiếm các bước để thêm một mô hình ngôn ngữ được cung cấp bởi RAG vào ứng dụng Android của bạn, đây là bài viết dành cho bạn! RAG là gì? Nếu bạn không quen thuộc với thuật ngữ, RAG có nghĩa là Đây là một kỹ thuật cho các mô hình ngôn ngữ để truy cập thông tin bên ngoài không có sẵn trong tập dữ liệu của họ sau khi đào tạo. Điều này cho phép các mô hình nhận thức được thông tin cập nhật mà họ có thể sử dụng để cung cấp câu trả lời chính xác hơn cho lời nhắc. Retrieval Augmented Generation Hãy xem xét một ví dụ nhanh. Hãy tưởng tượng bạn có hai mô hình ngôn ngữ, một mô hình đang sử dụng RAG để lấy thông tin bên ngoài về các thủ đô ở châu Âu, trong khi người kia chỉ dựa vào kiến thức của riêng mình. 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. Kết quả có thể trông như thế này: Như biểu đồ minh họa, bạn có nhiều khả năng nhận được câu trả lời hữu ích từ mô hình ngôn ngữ sử dụng RAG, thay vì mô hình dựa vào kiến thức của chính nó. Đó thực sự là tất cả những gì bạn cần biết về RAG ở cấp độ cao.Sau đó, hãy đi sâu hơn và viết một số mã để kích hoạt mô hình ngôn ngữ RAG của riêng bạn trong ứng dụng của bạn! Cài đặt ứng dụng Android của bạn Giả sử bạn muốn tạo một ứng dụng bằng cách sử dụng một mô hình ngôn ngữ để chơi trò chơi Simon Says. Bạn muốn mô hình là Simon, và sử dụng RAG để truy cập một nguồn dữ liệu nhiệm vụ để giúp quyết định những gì để hỏi người dùng. Cách đơn giản nhất để làm điều đó trên Android là với , một bộ sưu tập các công cụ để giúp ứng dụng của bạn sử dụng AI và kỹ thuật học máy. Để bắt đầu, hãy thêm sự phụ thuộc MediaPipe vào ứng dụng của bạn : phương tiện truyền thông build.gradle dependencies { implementation("com.google.mediapipe:tasks-genai:0.10.27") implementation("com.google.ai.edge.localagents:localagents-rag:0.3.0") } Tiếp theo, bạn cần thêm một mô hình ngôn ngữ vào thiết bị kiểm tra của bạn thông qua máy tính của bạn. , một mô hình ngôn ngữ nhẹ có chứa 1 tỷ thông số có giá trị thông tin. Đồ chơi Gemma3-1B Lưu ý: Trước khi tải xuống mô hình, bạn có thể phải đăng nhập vào Kaggle và đồng ý với Điều khoản và Điều kiện AI của Google. Side Note: Before downloading the model, you may have to sign upto Kaggle and agree to Google’s AI Terms and Conditions. Một khi mô hình được tải xuống, đã đến lúc thêm mô hình vào thiết bị của bạn. Bạn có thể làm điều này thông qua 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 Ngoài ra, bạn có thể sử dụng các sử dụng Android Studio để tự tạo các thư mục và kéo mô hình vào thiết bị của bạn. File Explorer Đánh giá Với mô hình được thêm vào, bạn có thể tiếp tục xây dựng đường ống dẫn RAG để cung cấp thông tin cho Gemma. Tạo Embeddings Các mô hình ngôn ngữ thông tin dựa vào để thực hiện RAG không phải là cùng một thông tin bạn truyền vào nó. . Embeddings Đây là các chuỗi toán học đại diện cho ý nghĩa ngữ nghĩa của văn bản. Khi mô hình nhận được một lời nhắc, nó sẽ sử dụng nó để tìm kiếm thông tin có liên quan nhất phù hợp với nó và sử dụng nó cùng với thông tin của chính nó để cung cấp một câu trả lời. . Embedder Embeddings là một chủ đề riêng; bạn được khuyến khích đọc về chúng. Đối với bài đăng này, bạn chỉ cần biết làm thế nào để tạo ra chúng. . Gecko Embedder Đầu tiên, download Tokenizer và các embedder mô hình tập tin vào máy tính của bạn. Sau đó nhấn chúng vào thiết bị của bạn: 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 Với embedder được cài đặt trên thiết bị của bạn, đã đến lúc cung cấp một tệp mẫu để tạo các embeddings từ. folder, tạo một file được gọi là Sau đó, vào file, thêm văn bản sau: 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> Tệp chứa một vài câu trả lời khác nhau mà người ta có thể đưa ra trong một trò chơi của Simon Says, mỗi câu được chia ra với một câu trả lời khác nhau. Điều này cung cấp cho người nhúng một tín hiệu để biết làm thế nào để tách từng câu trả lời khi chia văn bản thành nhúng. <chunk_splitter> Quá trình này được gọi và có thể có tác động lớn đến việc RAG hoạt động tốt như thế nào thông qua mô hình ngôn ngữ.Thử nghiệm với các mảnh và phản ứng có kích thước khác nhau được khuyến khích để tìm sự kết hợp phù hợp với nhu cầu của bạn! chunking Một điều cần xem xét là lưu trữ ứng dụng. Hãy nhớ, bạn đã cài đặt một mô hình ngôn ngữ và một bộ nhúng trên thiết bị của bạn. Những thứ này chiếm nhiều gigabyte không gian, vì vậy hãy chắc chắn rằng bạn không thêm sưng lên một thiết bị bằng cách sử dụng một tệp văn bản quá lớn! Bạn muốn xem xét lưu trữ tệp mẫu trong đám mây và tải xuống nó qua mạng để giảm các vấn đề với lưu trữ. Với tệp văn bản ở vị trí, đã đến lúc khởi tạo 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 có ba tham số: con đường của embedder và tokenizer trên thiết bị, với một tham số cuối cùng để thiết lập xem embedder có thể sử dụng GPU thiết bị khi tạo embeddings hay không. Đặt điều này thành đúng sẽ tăng tốc tạo các nhúng nếu có GPU. Hãy chắc chắn kiểm tra khả năng của thiết bị trước khi quyết định kích hoạt giá trị này. Tiếp theo, tạo một trường hợp của mô hình ngôn ngữ: 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() Có rất nhiều thông số liên quan ở trên; đừng lo lắng về những điều này ngay bây giờ. Với mô hình ngôn ngữ được tạo, bạn có thể tập trung trở lại vào các nhúng. Mô hình cần một nơi để lấy nhúng mỗi khi nó nhận được một lời nhắc. MediaPipe cung cấp một SQLite , đó là một công cụ phổ biến để lưu trữ nhúng. Hãy tạo một: 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 ) ) Ở đây, mọi thứ bắt đầu liên kết với nhau khi cả mô hình ngôn ngữ và embedder đều được truyền vào ChainConfig. là kích thước của mỗi "vector" cơ sở dữ liệu có thể lưu trữ. được sử dụng để cung cấp một lời nhắc để giúp thúc đẩy quá trình RAG. 786 PromptBuilder Cuối cùng, chúng ta hãy tải tệp văn bản mà chúng ta đã tạo trước đó trong thư mục tài sản vào embedder.First, tải tệp từ thiết bị và chia văn bản thành một danh sách các chuỗi: // This is an extension function to read the file from disk. val gameResponses: List<String> = context.getTextFromFile("simon_says_responses.txt") Tiếp theo, tải các câu trả lời vào chainConfig được tạo trước đó. chainConfig.semanticMemory.getOrNull() ?.recordBatchedMemoryItems(ImmutableList.copyOf(gameResponses)) ?.get() Tùy thuộc vào thiết bị của bạn và kích thước của tệp văn bản, điều này có thể mất vài giây đến vài phút để hoàn thành. Một quy tắc tốt là ngăn chặn mô hình ngôn ngữ được sử dụng trong khi điều này xảy ra. Bạn cũng có thể chạy thao tác này trên một chủ đề IO để tránh chặn chủ đề chính. Với điều đó, bạn vừa chuyển đổi tệp văn bản của mình thành một tập hợp các nhúng được lưu trữ trong một cửa hàng vector. bạn cũng đã liên kết mô hình ngôn ngữ của mình với cửa hàng để nó bây giờ có thể lấy thông tin! Phần tiếp theo sẽ cho bạn thấy làm thế nào để sử dụng các nhúng đó bằng cách thông qua mô hình ngôn ngữ yêu cầu của bạn. Thông qua Prompts cho RAG Powered Model của bạn Với hầu hết các cài đặt phức tạp hoàn thành, truyền một lời nhắc vào mô hình ngôn ngữ là đáng ngạc nhiên dễ dàng. sử dụng chuỗi config và gọi nó. RetrievalAndInferenceChain val retrievalAndInferenceChain = RetrievalAndInferenceChain(chainConfig) Tiếp theo, tạo một yêu cầu và truyền nó vào chuỗi. 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 Trong quá trình xử lý, nó sẽ tham khảo các nhúng trong cửa hàng vector của bạn để cung cấp những gì nó nghĩ là câu trả lời chính xác nhất. Như bạn mong đợi, những gì bạn hỏi mô hình sẽ dẫn đến các câu trả lời khác nhau.Khi các nhúng chứa một loạt các câu trả lời Simon nói, rất có thể bạn sẽ nhận được một câu trả lời tốt! Điều gì về các trường hợp bạn nhận được một kết quả bất ngờ?Vào thời điểm này, bạn cần phải quay trở lại và điều chỉnh tốt các thông số của các đối tượng của bạn từ phần trước. Chúng ta hãy làm điều đó trong phần tiếp theo. Tùy chỉnh tốt mô hình ngôn ngữ RAG của bạn Nếu có một điều chúng ta đã học được trong những năm qua từ Generative AI, đó là nó không phải là một khoa học chính xác. Bạn chắc chắn đã thấy những câu chuyện mà các mô hình ngôn ngữ đã “hành xử sai” và tạo ra kết quả ít hơn mong muốn, gây ra sự xấu hổ và thiệt hại danh tiếng cho các công ty. Đây là kết quả của không đủ thử nghiệm được thực hiện để đảm bảo các phản hồi từ một mô hình được mong đợi. thử nghiệm không chỉ giúp tránh sự xấu hổ, nó cũng có thể giúp thử nghiệm với đầu ra mô hình ngôn ngữ của bạn để nó hoạt động thậm chí tốt hơn! Chúng ta hãy xem những đòn bẩy nào chúng ta có thể điều chỉnh để tinh chỉnh mô hình ngôn ngữ RAG của chúng ta. : LLmInferenceOptions.Builder LlmInferenceOptions.builder() .setModelPath(GemmaModelPath) .setPreferredBackend(LlmInference.Backend.CPU) // Change to GPU if you have a GPU powered device. .setMaxTokens(1200) .build() Các thông số đầu tiên có thể được thay đổi là Đây là số lượng “input” hoặc “output” mà mô hình ngôn ngữ có thể xử lý. Giá trị càng lớn, mô hình ngôn ngữ có thể xử lý nhiều dữ liệu cùng một lúc. setMaxTokens() Trong ví dụ của chúng tôi, điều này có nghĩa là mô hình có thể xử lý một đầu vào văn bản và tạo ra một đầu ra bao gồm 1200 token. với giá trị nhỏ hơn. MaxTokens Hãy cẩn thận với giá trị này, vì bạn có thể thấy mô hình của bạn bất ngờ xử lý nhiều token hơn mong đợi. Chúng ta hãy di chuyển đến : LlmInferenceSessionOptions.Builder() llmInferenceSessionOptions = LlmInferenceSessionOptions.builder() .setTemperature(0.6f) .setTopK(5000) .setTopP(1f) .build() Ở đây, bạn có thể đặt một vài tham số khác nhau. , và Chúng ta hãy lặn chúng vào từng người trong số họ. .setTemperature() .setTopK() .setTopP() có thể suy nghĩ về mức độ “ngẫu nhiên” các câu trả lời từ mô hình ngôn ngữ có thể là. giá trị càng thấp, các câu trả lời từ mô hình có thể là “đáng đoán” hơn. giá trị càng cao, các câu trả lời sẽ càng “sáng tạo”, dẫn đến nhiều câu trả lời bất ngờ hơn. .setTemperature() Đối với ví dụ này, nó được thiết lập để , có nghĩa là mô hình sẽ cung cấp các phản ứng bán sáng tạo, nhưng không phải là không thể tưởng tượng. 0.6 Nhiệt độ là một giá trị tốt để thử nghiệm với, vì bạn có thể thấy các giá trị khác nhau cung cấp phản ứng tốt hơn tùy thuộc vào trường hợp sử dụng của bạn. là một cách để nói “chỉ xem xét các kết quả K hàng đầu để trở lại cho người dùng.” mô hình ngôn ngữ, trong khi xử lý một lời nhắc, tạo ra một số câu trả lời, có khả năng hàng ngàn! .setTopK() Mỗi câu trả lời này được đưa ra một xác suất về khả năng họ sẽ là câu trả lời đúng. giá trị có thể được đặt để giúp tập trung vào mô hình.Nếu bạn hài lòng với các câu trả lời ít có khả năng được xem xét, bạn sẽ muốn đặt giá trị này cao. topK Bạn có thể thấy rằng mô hình hoạt động tốt hơn với ít hoặc nhiều phản ứng để xem xét, tùy thuộc vào nhu cầu của bạn. Đối với một trò chơi của Simon Says, chúng tôi muốn mô hình đang suy nghĩ về rất nhiều phản ứng khác nhau để giữ cho trò chơi tươi. Có vẻ như một giá trị tốt. 5000 xây dựng trên giới hạn được thiết lập bởi bằng cách nói “chỉ xem xét kết quả có xác suất P”. như đã đề cập trước đó, các mô hình ngôn ngữ gán một xác suất cho mỗi phản ứng mà nó tạo ra về xác suất có thể. có nghĩa là mô hình có thể dễ dàng loại bỏ bất kỳ phản ứng nào không có xác suất tối thiểu cần thiết. .setTopP() topK topP Để chỉ một ví dụ, nếu mô hình có một Đặt 2 và đang xem xét các câu trả lời sau: 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 Hai câu trả lời đầu tiên sẽ được xem xét bởi vì xác suất cao hơn Câu trả lời về chiếc xe sẽ bị loại bỏ, vì nó chỉ có khả năng . 0.4 0.3 tương tự và , cho phép bạn xác định mô hình ngôn ngữ của bạn có thể sáng tạo như thế nào.Nếu bạn muốn xem xét phản hồi ít có khả năng hơn cho lời nhắc, thiết lập một giá trị P thấp sẽ giúp. temperature topK topP Trong ví dụ này, nó được thiết lập để Đó là bởi vì chúng tôi muốn mô hình hoàn toàn chắc chắn về phản ứng của nó. nó đang chơi một trò chơi trẻ con sau tất cả! 1.0 Thử nghiệm với các giá trị này sẽ tạo ra kết quả rất khác nhau từ các mô hình ngôn ngữ của bạn. Đi đâu tiếp theo? Tôi hy vọng bạn đã thích trải nghiệm này về cách thêm một mô hình ngôn ngữ hỗ trợ RAG vào Ứng dụng Android của bạn! Nếu bạn đang tìm kiếm để tìm hiểu thêm về RAG và Android, đây là một vài liên kết tôi khuyên bạn nên: 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