Embeddings API का इस्तेमाल करके सिमैंटिक सर्च करना

इस गाइड में, उपयोगकर्ता के डिवाइस पर सिमैंटिक सर्च की सुविधा को लागू करने का तरीका बताया गया है. यह खोज की एक ऐसी तकनीक है जो सिर्फ़ कीवर्ड के बजाय, क्वेरी के मतलब के आधार पर काम का कॉन्टेंट ढूंढती है. सिमैंटिक सर्च की मदद से, समानार्थी शब्दों की कस्टम टेबल या टैगिंग टैक्सोनॉमी को मैन्युअल तरीके से बनाने और मैनेज करने की ज़रूरत नहीं पड़ती. उपयोगकर्ता के डिवाइस पर सेमैंटिक सर्च की सुविधा लागू करने के लिए, इन लाइब्रेरी का इस्तेमाल करें:

  • वेक्टर एंबेडिंग जनरेट करने के लिए, ML Kit SDK टूल, खास तौर पर Embeddings API
  • वेक्टर एंबेडिंग को स्टोर करने के लिए AndroidX AppSearch का इस्तेमाल किया जाता है. साथ ही, कोसाइन डिस्टेंस मेट्रिक का इस्तेमाल करके स्ट्रक्चर्ड डेटा को खोजा जाता है, ताकि कॉन्टेंट को उसके सिमैंटिक के हिसाब से ढूंढा जा सके

काम करने वाला उदाहरण देखने के लिए, GitHub पर मौजूद ML Kit GenAI का सैंपल ऐप्लिकेशन देखें.

ज़रूरी शर्तें

उपयोगकर्ता के डिवाइस पर सेमैंटिक सर्च की सुविधा का इस्तेमाल करने के लिए, आपको डिवाइस और खाते से जुड़ी ये ज़रूरी शर्तें पूरी करनी होंगी:

  • आपके डिवाइस में Android AICore काम करता हो. AICore की सुविधा वाले ज़्यादातर डिवाइसों में, एनपीयू की मदद से तेज़ी से काम करने वाले मॉडल इस्तेमाल किए जाते हैं. वहीं, इस सुविधा वाले बाकी डिवाइसों में सीपीयू मॉडल (XNNPACK की मदद से तेज़ी से काम करने वाले मॉडल) का इस्तेमाल किया जाता है.
  • आपके डिवाइस पर मौजूद Google खाता, AICore डेवलपर के लिए झलक प्रोग्राम का हिस्सा है.
  • आपके डिवाइस पर, AICore डेवलपर के लिए झलक APK का नया वर्शन मौजूद हो: अपने डिवाइस पर, Android AICore Play Store पेज खोलें और APK का नया वर्शन इंस्टॉल करने के लिए, अपडेट करें पर टैप करें. इसके अलावा, अपने डिवाइस की सेटिंग पर जाएं. इसके बाद, सिस्टम की सेवाएं या Google की सेवाएं खोजें. फिर, Android AICore (बीटा वर्शन) पर टैप करें. अगर कोई अपडेट उपलब्ध है, तो अपडेट करें पर टैप करें.
  • आपके डिवाइस पर Private Compute Services ऐप्लिकेशन का नया वर्शन होना चाहिए: अपने डिवाइस पर, Private Compute Services का Play Store पेज खोलें और ऐप्लिकेशन का नया वर्शन इंस्टॉल करने के लिए, अपडेट करें पर टैप करें. इसके अलावा, Play Store > ऐप्लिकेशन और डिवाइस मैनेज करें > मैनेज करें > Private Compute Services पर जाएं. अगर कोई अपडेट उपलब्ध है, तो अपडेट करें पर टैप करें.

इस्तेमाल किए जा सकने वाले मॉडल और कॉन्फ़िगरेशन

ML Kit Embeddings API, Google के EmbeddingGemma मॉडल फ़ैमिली का इस्तेमाल करता है. इसे Android AICore मैनेज करता है. खास तौर पर, Embeddings API, टेक्स्ट और इमेज इनपुट के लिए एंबेडिंग जनरेट करने के लिए, EmbeddingGemma 2 (embedding_gemma_v2) का इस्तेमाल करता है.

इनपुट की खास जानकारी और सीमाएं यहां दी गई हैं:

इनपुट प्रॉपर्टी EmbeddingGemma 2 इस्तेमाल की जानकारी
इस्तेमाल किए जा सकने वाले मोड टेक्स्ट और इमेज (Modality.TEXT, Modality.IMAGE) यह पुष्टि करने के लिए कि इमेज इनपुट की सुविधा काम करती है, रनटाइम के दौरान देखें कि model.getMetadata()?.supportedModalities.contains(Modality.IMAGE), true दिखाता है या नहीं.
टोकन की सीमा 256 टोकन model.getTokenLimit() तरीके का इस्तेमाल करके जांच करें. हर इमेज को 140 सॉफ़्ट-टोकन में एन्कोड किया जाता है. इससे, इमेज के साथ मौजूद टेक्स्ट के लिए 116 टोकन बचते हैं.
इमेज रिज़ॉल्यूशन छोटी साइड पर 768 पिक्सल तक ImagePart क्लास के लिए कंस्ट्रक्टर, Bitmap, Uri या ByteArray आर्ग्युमेंट टाइप स्वीकार करता है. अगर किसी इमेज का छोटा डाइमेंशन 768 पिक्सल से ज़्यादा है, तो एसडीके उसे अपने-आप छोटा कर देता है. हालांकि, इससे इमेज के आसपेक्ट रेशियो (लंबाई-चौड़ाई का अनुपात) पर कोई असर नहीं पड़ता. बेहतर परफ़ॉर्मेंस और मेमोरी के इस्तेमाल के लिए, बहुत ज़्यादा आसपेक्ट रेशियो (लंबाई-चौड़ाई का अनुपात) का इस्तेमाल करने से बचें.

आउटपुट वेक्टर का डाइमेंशन 768 है (32-बिट फ़्लोट का FloatArray ऑब्जेक्ट). model.getMetadata()?.dimension तरीके का इस्तेमाल करके, रनटाइम के दौरान आउटपुट वेक्टर डाइमेंशन की जांच की जा सकती है.

अपनी प्रोफ़ाइल बनाना शुरू करें

सेमैंटिक सर्च का इस्तेमाल करने के लिए, अपने प्रोजेक्ट को कॉन्फ़िगर करें. इसके लिए, मॉड्यूल-लेवल की build.gradle.kts (या build.gradle) फ़ाइल में ये डिपेंडेंसी जोड़ें.

Kotlin

dependencies {
    // ...
    implementation("com.google.mlkit:genai-embedding:1.0.0-alpha1")
    implementation("androidx.appsearch:appsearch:1.2.0-alpha01")
    implementation("androidx.appsearch:appsearch-local-storage:1.2.0-alpha01")
    implementation("androidx.concurrent:concurrent-futures-ktx:1.3.0")
    // ...
}

ग्रूवी

dependencies {
    // ...
    implementation 'com.google.mlkit:genai-embedding:1.0.0-alpha1'
    implementation 'androidx.appsearch:appsearch:1.2.0-alpha01'
    implementation 'androidx.appsearch:appsearch-local-storage:1.2.0-alpha01'
    implementation 'androidx.concurrent:concurrent-futures-ktx:1.3.0'
    // ...
}

एम्बेडिंग मॉडल पाना

डिवाइस पर उपलब्ध एंबेडिंग मॉडल की सूची देखने के लिए, EmbeddingGeneration.getEmbeddingModels() का इस्तेमाल करें. इसके बाद, मॉडल के सिग्नेचर के हिसाब से कोई मॉडल चुनें. उन सभी मॉडल को बंद कर दें जिनका इस्तेमाल नहीं किया जा रहा है.

// Prefer EmbeddingGemma 2 (text and image), then EmbeddingGemma 1 (text only).
val preferredSignatures = listOf("embedding_gemma_v2", "embedding_gemma_v1")

suspend fun selectEmbeddingModel(): EmbeddingModel? {
    val models = EmbeddingGeneration.getEmbeddingModels()
    val selected = preferredSignatures.firstNotNullOfOrNull { signature ->
        models.firstOrNull { signature in it.getModelSignature() }
    } ?: models.firstOrNull()

    models.filter { it !== selected }.forEach { it.close() }
    return selected
}

कोई खास मॉडल पाने के लिए, उसका सिग्नेचर पास करें: EmbeddingGeneration.getEmbeddingModels("embedding_gemma_v2").

एम्बेडिंग जनरेट करने से पहले, ज़रूरत पड़ने पर मॉडल डाउनलोड करें और उसे वॉर्म अप करें:

suspend fun prepareModel(model: EmbeddingModel): Boolean {
    when (model.checkStatus()) {
        FeatureStatus.UNAVAILABLE -> return false
        FeatureStatus.DOWNLOADABLE, FeatureStatus.DOWNLOADING -> model.download().collect()
    }
    model.warmup()
    return true
}

जब आपको मॉडल की ज़रूरत न हो, तब model.close() को कॉल करें.

AppSearch स्कीमा तय करना

सेमैंटिक सिमिलैरिटी सर्च की सुविधा चालू करने के लिए, आपको AppSearch स्कीमा तय करना होगा. इसमें वेक्टर प्रॉपर्टी को खास तौर पर, सिमिलैरिटी इंडेक्सिंग के लिए कॉन्फ़िगर किया गया हो.

val documentSchema = AppSearchSchema.Builder("my_schema_type")
    .addProperty(
        AppSearchSchema.StringPropertyConfig.Builder("text")
            .setIndexingType(AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_PREFIXES)
            .setTokenizerType(AppSearchSchema.StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
            .build()
    )
    .addProperty(AppSearchSchema.StringPropertyConfig.Builder("imageUri").build())
    .addProperty(
        AppSearchSchema.EmbeddingPropertyConfig.Builder("embedding")
            .setIndexingType(AppSearchSchema.EmbeddingPropertyConfig.INDEXING_TYPE_SIMILARITY)
            .setQuantizationType(AppSearchSchema.EmbeddingPropertyConfig.QUANTIZATION_TYPE_8_BIT)
            .build()
    )
    .build()

दस्तावेज़ के कॉन्टेंट को इंडेक्स करना

दस्तावेज़ के कॉन्टेंट को इंडेक्स करने के लिए, आपको हर Content ऑब्जेक्ट के लिए एक वेक्टर एंबेडिंग जनरेट करनी होगी. इसके बाद, उसे GenericDocument इंस्टेंस में रैप करके, अपने AppSearch डेटाबेस में सेव करना होगा.

EmbeddingVector ऑब्जेक्ट बनाते समय, आपको चालू modelSignature वैल्यू भी पास करनी होगी. AppSearch, इस modelSignature स्ट्रिंग का इस्तेमाल करके, खोज क्वेरी को गलत नतीजे दिखाने से रोकता है.

suspend fun syncDatabase(
    model: EmbeddingModel,
    session: AppSearchSession,
    documents: List<Pair<String, Uri?>>,
) {
    val modelSignature = model.getModelSignature()
    val isImageSupported =
        model.getMetadata()?.supportedModalities?.contains(Modality.IMAGE) == true
    val docsWithEmbedding = mutableListOf<GenericDocument>()

    for ((index, document) in documents.withIndex()) {
        val (text, imageUri) = document

        // Generate the embedding from the text and/or image.
        val imagePart = if (imageUri != null && isImageSupported) ImagePart(imageUri) else null
        val vector = try {
            val parts = listOfNotNull(if (text.isNotBlank()) TextPart(text) else null, imagePart)
            if (parts.isEmpty()) continue
            model.generateEmbedding(EmbeddingRequest(Content(parts))).embedding.vector
        } finally {
            imagePart?.close()
        }

        // Associate the vector with the signature of the model that generated it.
        val docBuilder = GenericDocument.Builder<GenericDocument.Builder<*>>(
            "my_namespace",
            "doc_$index",
            "my_schema_type"
        )
            .setPropertyString("text", text)
            .setPropertyEmbedding("embedding", EmbeddingVector(vector, modelSignature))
        if (imageUri != null) {
            docBuilder.setPropertyString("imageUri", imageUri.toString())
        }
        docsWithEmbedding.add(docBuilder.build())
    }

    // Clear the old database by force-overriding the schema, then set the schema again.
    session.setSchemaAsync(SetSchemaRequest.Builder().setForceOverride(true).build()).await()
    session.setSchemaAsync(SetSchemaRequest.Builder().addSchemas(documentSchema).build()).await()

    val putRequest = PutDocumentsRequest.Builder().addGenericDocuments(docsWithEmbedding).build()
    session.putAsync(putRequest).await()
}

सिमैंटिक क्वेरी करने के लिए, सर्च क्वेरी (टेक्स्ट और/या इमेज) को एंबेडिंग वेक्टर में बदलें. इसके बाद, वेक्टर-सर्च पैरामीटर कॉन्फ़िगर करें और अपने AppSearch डेटाबेस में क्वेरी करें.

सुझाव:

  • AppSearch, डिफ़ॉल्ट रूप से कोसाइन मेट्रिक (SearchSpec.EMBEDDING_SEARCH_METRIC_TYPE_COSINE) का इस्तेमाल करता है. इसलिए, आपको इसे सेट करने की ज़रूरत नहीं है.
  • "sum(this.matchedSemanticScores(getEmbeddingParameter(0)))" फ़ॉर्मूला के हिसाब से नतीजों को रैंक करें.
  • नतीजों को फ़िल्टर करने के लिए, लॉजिकल "semanticSearch(getEmbeddingParameter(0), $threshold)" स्ट्रिंग का इस्तेमाल करें. इससे आपको ऐसे नतीजे मिलेंगे जो आपकी थ्रेशोल्ड वैल्यू (आम तौर पर 0.5 या इससे ज़्यादा) से मेल खाते हैं.
data class Match(val text: String, val imageUri: Uri?, val score: Double)

suspend fun search(
    model: EmbeddingModel,
    session: AppSearchSession,
    queryText: String,
    imageUri: Uri? = null,
    threshold: Double = 0.5,
): List<Match> {
    val modelSignature = model.getModelSignature()
    val isImageSupported =
        model.getMetadata()?.supportedModalities?.contains(Modality.IMAGE) == true

    // Generate the query embedding from the text and/or image.
    val imagePart = if (imageUri != null && isImageSupported) ImagePart(imageUri) else null
    val queryVector = try {
        val parts = listOfNotNull(if (queryText.isNotBlank()) TextPart(queryText) else null, imagePart)
        if (parts.isEmpty()) return emptyList()
        model.generateEmbedding(EmbeddingRequest(Content(parts))).embedding.vector
    } finally {
        imagePart?.close()
    }

    val searchSpec = SearchSpec.Builder()
        .addFilterSchemas("my_schema_type")
        .setRankingStrategy("sum(this.matchedSemanticScores(getEmbeddingParameter(0)))")
        .setListFilterQueryLanguageEnabled(true)
        .setResultCountPerPage(5)
        // Add the query vector as parameter 0.
        .addEmbeddingParameters(EmbeddingVector(queryVector, modelSignature))
        .build()

    val queryExpression = "semanticSearch(getEmbeddingParameter(0), $threshold)"
    val results = session.search(queryExpression, searchSpec)
    return try {
        results.nextPageAsync.await().map {
            Match(
                text = it.genericDocument.getPropertyString("text") ?: "",
                imageUri = it.genericDocument.getPropertyString("imageUri")?.let(Uri::parse),
                // rankingSignal contains the computed similarity score.
                score = it.rankingSignal,
            )
        }
    } finally {
        results.close()
    }
}

हाइब्रिड सर्च इंडेक्स का इस्तेमाल करना

बेहतरीन उपयोगकर्ता अनुभव के लिए, हमारा सुझाव है कि आप हाइब्रिड रणनीति का इस्तेमाल करें. इसमें सिमैंटिक और कीवर्ड सर्च, दोनों शामिल हैं. जब उपयोगकर्ता खोज बार में कुछ टाइप कर रहा हो, तब कीवर्ड सर्च का इस्तेमाल करके, उसे तुरंत टाइप-अहेड सुझाव दिखाएं. जब वह पूरी क्वेरी डाल दे या शब्दों के बीच में स्पेस दे, तब सिमैंटिक सर्च का इस्तेमाल करके, क्वेरी को पढ़ें, समझें, और सबसे सही मैच दिखाएं.

अपने वेक्टर डेटाबेस को फिर से इंडेक्स करना

मॉडल के अलग-अलग वर्शन से जनरेट किए गए एंबेडिंग, अलग-अलग वेक्टर स्पेस में मौजूद होते हैं. इसलिए, उनकी तुलना सीधे तौर पर नहीं की जा सकती. एम्बेडिंग मॉडल को अपडेट करने पर, आपको अपने AppSearch डेटाबेस को फिर से इंडेक्स करना होगा.

हमारा सुझाव है कि इस्तेमाल किए जा रहे मॉडल के बंद होने की अवधि शुरू होते ही, एंबेडिंग को फिर से इंडेक्स करना शुरू कर दें. Embeddings API का इस्तेमाल करके, किसी मॉडल के बंद होने का शेड्यूल देखा जा सकता है. EmbeddingMetadata ऑब्जेक्ट को वापस पाने के लिए, model.getMetadata() तरीके को कॉल करें. साथ ही, लाइफ़साइकल की तारीख की इन प्रॉपर्टी को देखें. ये यूटीसी में LocalDate ऑब्जेक्ट के तौर पर दिखती हैं:

  • deprecationDate वैल्यू से, मॉडल के बंद होने की अवधि की शुरुआत का पता चलता है. आपको फिर से इंडेक्स करने की प्रोसेस शुरू कर देनी चाहिए. इस दौरान, मॉडल काम करता रहेगा. साथ ही, मॉडल के आधिकारिक तौर पर बंद होने से पहले, फिर से इंडेक्स करने की प्रोसेस पूरी कर लें.
  • shutdownDate की वैल्यू से पता चलता है कि मॉडल को कब बंद किया जाएगा और कब इसका इस्तेमाल नहीं किया जा सकेगा. इस तारीख के बाद, अनुमान लगाने के लिए मॉडल को ऐक्सेस नहीं किया जा सकेगा. साथ ही, इस मॉडल की मौजूदा एम्बेडिंग को उपलब्ध नए मॉडल के साथ फिर से इंडेक्स करना होगा.

हम ML Kit के रिलीज़ नोट और डेवलपर के दस्तावेज़ के अपडेट के ज़रिए, मॉडल के बंद होने, माइग्रेशन की टाइमलाइन, और बंद होने की तारीखों के बारे में भी बताते हैं.

सबसे सही तरीके

उपयोगकर्ता को बेहतर अनुभव देने के लिए, अपने Android ऐप्लिकेशन में वेक्टर एंबेडिंग को इंटिग्रेट करते समय इन तरीकों को अपनाएं:

  • इंडेक्स करने के वर्कलोड शेड्यूल करना: बैकग्राउंड में इंडेक्स करने के वर्कलोड को हमेशा WorkManager लाइब्रेरी का इस्तेमाल करके शेड्यूल करें. बैकग्राउंड में चलने वाले टास्क को इस तरह कॉन्फ़िगर करें कि वे सिर्फ़ तब चलें, जब डिवाइस चार्ज हो रहा हो और इस्तेमाल में न हो. एमएल किट, मॉडल के इस्तेमाल के लिए कोटा की सीमाएं लागू करता है. ये सीमाएं, एलएलएम एपीआई की तरह ही होती हैं. हालांकि, डिवाइस के चार्ज होने पर ये सीमाएं ज़्यादा उदार होती हैं. डेवलपर के लिए झलक मोड में, टेस्टिंग के लिए कोटा की सीमाओं को बायपास किया जा सकता है.
  • स्टोरेज और क्वेरी ऑप्टिमाइज़ेशन: क्वांटाइज़ेशन और डाइमेंशनलिटी ट्रंकेशन का इस्तेमाल करके, अपने डेटाबेस फ़ुटप्रिंट और क्वेरी के इंतज़ार के समय को ऑप्टिमाइज़ करें:
    • QUANTIZATION_TYPE_8_BIT कॉन्सटैंट का इस्तेमाल करने के लिए, अपने AppSearchSchema स्कीमा को कॉन्फ़िगर करें. इसके बारे में AppSearch स्कीमा तय करें में बताया गया है. इससे मेमोरी का इस्तेमाल कम होता है. साथ ही, हर कोऑर्डिनेट की सटीक जानकारी को 8-बिट के पूर्णांक में छोटा करके, क्वालिटी पर कम से कम असर पड़ता है.
    • एम्बेडिंग वेक्टर को छोटा करने के लिए, Matryoshka Representation Learning (एमआरएल) का इस्तेमाल करें. यह EmbeddingGemma 2 मॉडल के साथ काम करता है. ज़्यादा जानने के लिए, HuggingFace पर EmbeddingGemma 2 देखें.
  • हस्ताक्षर की एकरूपता: किसी वेक्टर स्पेस को क्वेरी करने के लिए, ऐसे हस्ताक्षर का इस्तेमाल न करें जो मेल न खाता हो. अगर आपने एंबेडिंग मॉडल के वर्शन को अपग्रेड किया है, तो आपको AppSearch डेटाबेस को वाइप करना होगा. साथ ही, नए मॉडल सिग्नेचर का इस्तेमाल करके, सभी दस्तावेज़ों के लिए एंबेडिंग को फिर से जनरेट करना होगा.
  • सुरक्षा और कॉन्टेंट मॉडरेट करना: अपने ऐप्लिकेशन में कॉन्टेंट मॉडरेट करने और सुरक्षा फ़िल्टर लागू करने की ज़िम्मेदारी आपकी है. जनरेटिव टेक्स्ट या इमेज एपीआई के उलट, ML Kit Embeddings API में सुरक्षा फ़िल्टर पहले से मौजूद नहीं होते हैं. मॉडल, कॉलर से मिले इनपुट के लिए, नया टेक्स्ट या मीडिया जनरेट करने के बजाय, संख्या वाले वेक्टर रिप्रज़ेंटेशन (FloatArray कोऑर्डिनेट) आउटपुट करता है. इसलिए, संख्या वाले वेक्टर से जनरेटिव कॉन्टेंट की सुरक्षा से जुड़े जोखिम नहीं होते. डिवाइस पर सुरक्षा से जुड़ी फ़िल्टरिंग की सुविधा को हटाने से, कम इंतज़ार के समय वाली इंडेक्सिंग और खोज की परफ़ॉर्मेंस भी बेहतर बनी रहती है. ऐसा इसलिए होता है, क्योंकि हाई थ्रूपुट वाले दस्तावेज़ की इंडेक्सिंग और रीयल-टाइम क्वेरी के दौरान, सीपीयू के लिए संसाधनों की कमी नहीं होती. सुरक्षा को बेहतर बनाने के लिए, इन दिशा-निर्देशों का पालन करें:
    • गैर-भरोसेमंद इनपुट और इंडेक्स किए गए कॉन्टेंट को मॉडरेट करना: अगर आपका ऐप्लिकेशन, यूज़र जनरेटेड कॉन्टेंट या बाहरी डेटा को इंडेक्स करता है या खोजता है, तो इंडेक्स करने या खोज के नतीजे दिखाने से पहले, सोर्स टेक्स्ट या इमेज पर कॉन्टेंट मॉडरेट करने की सुविधा और नीति के फ़िल्टर लागू करें.
    • डाउनस्ट्रीम जनरेटिव एआई पाइपलाइन को सुरक्षित रखना: अगर खोजे गए दस्तावेज़ों या पैसेज को डाउनस्ट्रीम जनरेटिव एआई मॉडल के लिए कॉन्टेक्स्ट के तौर पर पास किया जाता है, तो पुष्टि करें कि उस डाउनस्ट्रीम मॉडल और उसके इस्तेमाल करने वाले लोगों के लिए उपलब्ध आउटपुट पर, सुरक्षा फ़िल्टर और कॉन्टेंट से जुड़ी नीतियां चालू हों.

अन्य संसाधन