সিম্যান্টিক সার্চ

এই নির্দেশিকা আপনাকে অন-ডিভাইস সিম্যান্টিক সার্চ কীভাবে প্রয়োগ করতে হয় তা শেখায়, এটি এমন একটি সার্চ টেকনিক যা শুধুমাত্র কীওয়ার্ডের পরিবর্তে কোয়েরির অর্থের উপর ভিত্তি করে প্রাসঙ্গিক কন্টেন্ট খোঁজে। সিম্যান্টিক সার্চের ফলে ম্যানুয়ালি কাস্টম সমার্থক শব্দ সারণী বা ট্যাগিং ট্যাক্সোনমি তৈরি ও মেনে চলার প্রয়োজন হয় না। ডিভাইসে সিম্যান্টিক সার্চ প্রয়োগ করতে, নিম্নলিখিত লাইব্রেরি ব্যবহার করুন:

  • ভেক্টর এম্বেডিং জেনারেট করার জন্য ML Kit SDK, বিশেষ করে Embedding API
  • ভেক্টর এম্বেডিং স্টোর করতে এবং কোসাইন ডিসট্যান্স মেট্রিক্স ব্যবহার করে স্ট্রাকচার্ড ডেটা সার্চ করতে AndroidX AppSearch ব্যবহার করা হয় যাতে সেম্যান্টিক অনুযায়ী কন্টেন্ট খুঁজে পাওয়া যায়

পূর্বশর্ত

অন-ডিভাইস সিম্যান্টিক সার্চ ব্যবহার করতে, আপনাকে নিম্নলিখিত ডিভাইস ও অ্যাকাউন্ট সম্পর্কিত প্রয়োজনীয়তা পূরণ করতে হবে:

  • আপনার ডিভাইসে Android AICore কাজ করে। AICore-সমর্থিত বেশিরভাগ ডিভাইস NPU-অ্যাক্সিলারেটেড মডেল ব্যবহার করে, যেখানে বাকি সমর্থিত ডিভাইসগুলি CPU মডেলে (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 Embedding API, Google-এর EmbeddingGemma মডেল ফ্যামিলি ব্যবহার করে, যা Android AICore দ্বারা ম্যানেজ করা হয়। বিশেষ করে, Embedding API টেক্সট ও ছবি ইনপুটের জন্য এম্বেডিং তৈরি করতে EmbeddingGemma 2 (embedding_gemma_v2) ব্যবহার করে।

ইনপুট স্পেসিফিকেশন ও সীমাবদ্ধতা এখানে দেওয়া হল:

ইনপুট প্রপার্টি EmbeddingGemma 2 ব্যবহার সংক্রান্ত নোট
যেসব মোডালিটি কাজ করে টেক্সট ও ছবি (Modality.TEXT, Modality.IMAGE) ইমেজ ইনপুট কাজ করে কিনা তা কনফার্ম করতে, রানটাইমে চেক করে দেখুন যে model.getMetadata()?.supportedModalities.contains(Modality.IMAGE)-এর রিটার্ন ভ্যালু true কিনা।
টোকেনের সীমা ২৫৬টি টোকেন model.getTokenLimit() পদ্ধতি ব্যবহার করে পরীক্ষা করুন। প্রতিটি ছবি ৭০টি টোকেনে এনকোড করা হয়, যার ফলে সাথে থাকা টেক্সটের জন্য ১৮৬টি টোকেন বাকি থাকে।
ছবির রেজোলিউশন ছোট সাইডে সর্বাধিক ৭৬৮ পিক্সেল ImagePart ক্লাসের কনস্ট্রাক্টর Bitmap, Uri বা ByteArray আর্গুমেন্টের ধরন গ্রহণ করে। ৭৬৮ পিক্সেলের থেকে ছোট ডাইমেনশন সহ ছবি SDK-এর মাধ্যমে অটোমেটিক ডাউনস্কেল করা হয়, তবে অ্যাস্পেক্ট রেশিও একই থাকে। সর্বোত্তম পারফর্ম্যান্স ও মেমরির ব্যবহারের জন্য, এক্সট্রিম অ্যাস্পেক্ট রেশিও এড়িয়ে চলুন।

আউটপুট ভেক্টর ডাইমেনশন হল ৭৬৮ (৩২-বিট ফ্লোটের 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")
    // ...
}

Groovy

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(EmbeddingOptions()) পদ্ধতি ব্যবহার করুন।

AppSearch স্কিমা নির্দিষ্ট করুন

সিম্যান্টিক সিমিলারিটি সার্চ চালু করতে, আপনাকে অবশ্যই একটি AppSearch স্কিমা সংজ্ঞায়িত করতে হবে যেখানে ভেক্টর প্রপার্টি স্পষ্টভাবে সিমিলারিটি ইন্ডেক্সিংয়ের জন্য কনফিগার করা হয়েছে।

// Define the document schema
val documentSchema = AppSearchSchema.Builder("my_schema_type")
    .addProperty(
        AppSearchSchema.StringPropertyConfig.Builder("text")
            .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
            .setIndexingType(AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_PREFIXES)
            .setTokenizerType(AppSearchSchema.StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
            .build()
    )
    .addProperty(
        AppSearchSchema.StringPropertyConfig.Builder("imageUri")
            .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
            .setIndexingType(AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_NONE)
            .setTokenizerType(AppSearchSchema.StringPropertyConfig.TOKENIZER_TYPE_NONE)
            .build()
    )
    .addProperty(
        AppSearchSchema.EmbeddingPropertyConfig.Builder("embedding")
            .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
            .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(documents: List<Pair<String, Uri?>>) {

    val modelSignature = model?.getModelSignature() ?: return
    val docsWithEmbedding = mutableListOf<GenericDocument>()

    for ((index, item) in documents.withIndex()) {
        val (content, imageUri) = item
        // 1. Generate embedding (text and/or image)
        val parts = mutableListOf<Part>()
        if (content.isNotBlank()) {
            parts.add(TextPart(content))
        }
        var imagePart: ImagePart? = null
        val vector = try {
            if (imageUri != null && isImageSupported) {
                imagePart = ImagePart(imageUri)
                parts.add(imagePart)
            }
            if (parts.isEmpty()) continue
            val request = EmbeddingRequest(
                Content(parts)
            )
            model?.generateEmbedding(request)?.embedding?.vector
                ?: continue
        } finally {
            imagePart?.close()
        }

        // 2. Build the AppSearch document
        val docBuilder = GenericDocument.Builder<GenericDocument.Builder<*>>(
            "my_namespace",
            "doc_$index",
            "my_schema_type"
        )
        .setPropertyString("text", content)
        // Associate the vector with the signature that generated it
        .setPropertyEmbedding("embedding", EmbeddingVector(vector, modelSignature))

        if (imageUri != null) {
            docBuilder.setPropertyString("imageUri", imageUri.toString())
        }

        docsWithEmbedding.add(docBuilder.build())
    }

    // 3. Clear the old database by force-overriding the schema
    val clearRequest = SetSchemaRequest.Builder().setForceOverride(true).build()
    session?.setSchemaAsync(clearRequest)?.await()

    val schemaRequest = SetSchemaRequest.Builder().addSchemas(documentSchema).build()
    session?.setSchemaAsync(schemaRequest)?.await()

    // 4. Index documents
    val putRequest = PutDocumentsRequest.Builder().addGenericDocuments(docsWithEmbedding).build()
    session?.putAsync(putRequest)?.await()
}

সেম্যান্টিক কোয়েরি করতে, সার্চ কোয়েরি (টেক্সট এবং/অথবা ছবি) একটি এম্বেডিং ভেক্টরে কনভার্ট করুন, ভেক্টর-সার্চ প্যারামিটার কনফিগার করুন এবং আপনার AppSearch ডেটাবেসে কোয়েরি করুন।

প্রস্তাবনাগুলি:

  • ডিফল্ট মেট্রিককে SearchSpec.EMBEDDING_SEARCH_METRIC_TYPE_COSINE কনস্ট্যান্টে সেট করুন।
  • "sum(this.matchedSemanticScores(getEmbeddingParameter(0)))" ফর্মুলা অনুযায়ী ফলাফল র‍্যাঙ্ক করুন।
  • ফলাফল ফিল্টার করতে লজিক্যাল "semanticSearch(getEmbeddingParameter(0), $threshold)" স্ট্রিং ব্যবহার করুন যেখানে মিল আপনার থ্রেশহোল্ড ভ্যালুর সাথে মেলে (সাধারণত ০.৫ বা তার বেশি)।
suspend fun search(
    queryText: String,
    imageUri: Uri? = null,
    threshold: Double = 0.5
): List<Triple<String, Uri?, Double>> {
    val modelSignature = model?.getModelSignature() ?: return emptyList()

    // 1. Generate query embedding (text and/or image)
    val parts = mutableListOf<Part>()
    if (queryText.isNotBlank()) {
        parts.add(TextPart(queryText))
    }
    var imagePart: ImagePart? = null
    val queryVector = try {
        if (imageUri != null && isImageSupported) {
            imagePart = ImagePart(imageUri)
            parts.add(imagePart)
        }
        if (parts.isEmpty()) return emptyList()
        val request = EmbeddingRequest(
            content = Content(parts)
        )
        model?.generateEmbedding(request)?.embedding?.vector
            ?: return emptyList()
    } finally {
        imagePart?.close()
    }

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

    // 3. Execute search
    val queryExpression = "semanticSearch(getEmbeddingParameter(0), $threshold)"
    val results = session?.search(queryExpression, searchSpec) ?: return emptyList()

    return try {
        val page = results.nextPageAsync.await()
        page.map {
            val text = it.genericDocument.getPropertyString("text") ?: ""
            val matchedImageUri = it.genericDocument.getPropertyString("imageUri")?.let(Uri::parse)
            // rankingSignal contains the computed similarity score
            Triple(text, matchedImageUri, it.rankingSignal)
        }
    } finally {
        results.close()
    }
}

হাইব্রিড সার্চ ইন্ডেক্স ব্যবহার করা

ব্যবহারকারীর সেরা অভিজ্ঞতার জন্য, আমরা সিম্যান্টিক এবং কীওয়ার্ড সার্চ উভয়ই জড়িত একটি হাইব্রিড কৌশল ব্যবহার করার পরামর্শ দিই। ব্যবহারকারী যখন সার্চ বারে অ্যাক্টিভভাবে টাইপ করছেন, তখন ইনস্ট্যান্ট টাইপ-অ্যাহেড সাজেশন দেখাতে কীওয়ার্ড সার্চ ব্যবহার করুন; যখন তারা সম্পূর্ণ কোয়েরি লিখবেন, এমনকি শব্দ বিরতির সময়ও, সেরা ধারণাগত মিলগুলি পড়তে, বুঝতে এবং আনতে শব্দার্থিক সার্চ ব্যবহার করুন।

আপনার ভেক্টর ডেটাবেস আবার ইন্ডেক্স করুন

বিভিন্ন মডেল ভার্সনের জেনারেট করা এম্বেডিং পরস্পরের সাথে মানানসই নয় এমন ভেক্টর স্পেসে থাকে এবং সরাসরি তুলনা করা যায় না। আপনি যখনই এম্বেডিং মডেল আপডেট করবেন, তখনই আপনাকে অবশ্যই AppSearch ডেটাবেস আবার ইন্ডেক্স করতে হবে।

আপনি যে মডেল ব্যবহার করছেন সেটির অবচিতির সময়সীমা শুরু হওয়ার সাথে সাথেই এম্বেডিং আবার ইন্ডেক্স করা শুরু করার সাজেশন আমরা দিই। আপনি Embedding API ব্যবহার করে কোনও মডেলের বন্ধ হয়ে যাওয়ার শিডিউল চেক করতে পারেন। EmbeddingMetadata অবজেক্ট ফিরিয়ে আনতে model.getMetadata() মেথড কল করুন এবং এইসব লাইফসাইকেল ডেট প্রপার্টি চেক করুন (UTC-তে LocalDate অবজেক্ট হিসেবে ফিরিয়ে আনা হয়েছে):

  • deprecationDate ভ্যালু মডেলের অবচিতির সময়সীমার শুরুকে চিহ্নিত করে। মডেল কাজ করা চালিয়ে যাওয়ার সময় আপনার আবার ইন্ডেক্সিং শুরু করা উচিত এবং এটি আনুষ্ঠানিকভাবে বন্ধ হওয়ার আগে আবার ইন্ডেক্সিং শেষ করা উচিত।
  • shutdownDate মানটি হল সেই সময় যখন মডেলটি ডিকমিশন করা হবে এবং আর পরিষেবা দেবে না। এই তারিখের পরে, ইনফারেন্সের জন্য মডেলটি অ্যাক্সেস করা যাবে না এবং এই মডেলের বর্তমান এম্বেডিং লেটেস্ট উপলভ্য মডেলের সাথে বাধ্যতামূলকভাবে আবার ইন্ডেক্স করা হবে।

এছাড়াও, আমরা ML Kit রিলিজ নোট এবং ডেভেলপার ডকুমেন্টেশন আপডেটের মাধ্যমে আসন্ন মডেলের অবচয়, মাইগ্রেশন টাইমলাইন এবং টার্গেট শাটডাউন তারিখগুলি জানাই।

পেশাদার পদ্ধতি

ব্যবহারকারীকে ভালো অভিজ্ঞতা প্রদান করতে, আপনার Android অ্যাপে ভেক্টর এম্বেডিং ইন্টিগ্রেট করার সময় এইসব পদ্ধতি অনুসরণ করুন:

  • ইনডেক্সিং ওয়ার্কলোড শিডিউল করা: সবসময় WorkManager লাইব্রেরি ব্যবহার করে ব্যাকগ্রাউন্ড ইনডেক্সিং ওয়ার্কলোড শিডিউল করুন। আপনার ব্যাকগ্রাউন্ড টাস্ক এমনভাবে কনফিগার করুন যাতে ডিভাইস চার্জিং ও আইডল মোডে থাকাকালীনই শুধু তা রান করে। ML Kit, LLM API-এর মতো মডেল ব্যবহারের জন্য কোটার সীমা আরোপ করে, ডিভাইস চার্জ করার সময় আরও উদার সীমা সহ। ডেভেলপার প্রিভিউ মোডে, আপনি পরীক্ষার উদ্দেশ্যে কোটার সীমাবদ্ধতা বাইপাস করতে পারবেন।
  • স্টোরেজ ও কোয়েরি অপ্টিমাইজেশন: কোয়ান্টাইজেশন ও ডাইমেনশনালিটি ট্রাঙ্কেশন ব্যবহার করে আপনার ডেটাবেস ফুটপ্রিন্ট ও কোয়েরি লেটেন্সি অপ্টিমাইজ করুন:
    • QUANTIZATION_TYPE_8_BIT কনস্ট্যান্ট ব্যবহার করতে আপনার AppSearchSchema স্কিমা কনফিগার করুন, যেমন AppSearch স্কিমা সংজ্ঞায়িত করুন-এ দেখানো হয়েছে। এটি প্রতিটি কোঅর্ডিনেটের প্রিসিশন ৮-বিট ইন্টিজারে সঙ্কুচিত করে কোয়ালিটির উপর ন্যূনতম প্রভাব ফেলে মেমরির ব্যবহার কমায়।
    • এম্বেডিং ভেক্টর ট্রাঙ্কেট করতে, EmbeddingGemma 2 মডেল দ্বারা সমর্থিত Matryoshka Representation Learning (MRL) ব্যবহার করুন। আরও জানতে, HuggingFace-এ EmbeddingGemma 2 দেখুন।
  • স্বাক্ষরের সামঞ্জস্য: অমিল আছে এমন স্বাক্ষর ব্যবহার করে ভেক্টর স্পেস কোয়েরি করবেন না। আপনি এম্বেডিং মডেল ভার্সন আপগ্রেড করলে, আপনাকে অবশ্যই AppSearch ডেটাবেস ওয়াইপ করতে হবে এবং নতুন মডেল সিগনেচার ব্যবহার করে সব ডকুমেন্টের জন্য এম্বেডিং আবার তৈরি করতে হবে।
  • নিরাপত্তা ও কন্টেন্ট মডারেশন: আপনার অ্যাপে কন্টেন্ট মডারেশন ও নিরাপত্তা ফিল্টার প্রয়োগ করার দায়িত্ব আপনার। জেনারেটিভ টেক্সট বা ইমেজ API-এর মতো ML Kit Embedding API-তে বিল্ট-ইন নিরাপত্তা ফিল্টার থাকে না। কারণ, মডেলটি নতুন টেক্সট বা মিডিয়া তৈরি করার পরিবর্তে কলারের দেওয়া ইনপুটের সংখ্যাসূচক ভেক্টর উপস্থাপনা (FloatArray কোঅর্ডিনেট) আউটপুট করে, সংখ্যাসূচক ভেক্টরগুলি সহজাতভাবে জেনারেটিভ কন্টেন্ট নিরাপত্তা ঝুঁকি উপস্থাপন করে না। অন-ডিভাইস নিরাপত্তা ফিল্টারিং বাদ দিলে, হাই-থ্রুপুট ডকুমেন্ট ইন্ডেক্সিং ও রিয়েল-টাইম কোয়েরির সময় সিপিইউ কন্টেনশন এড়িয়ে লো-লেটেন্সি ইন্ডেক্সিং ও সার্চ পারফর্ম্যান্সও বজায় থাকে। নিরাপত্তা উন্নত করতে, এইসব নির্দেশিকা অনুসরণ করুন:
    • অবিশ্বস্ত ইনপুট ও ইন্ডেক্স করা কন্টেন্ট মডারেট করা: আপনার অ্যাপ যদি অবিশ্বস্ত ব্যবহারকারীর তৈরি করা কন্টেন্ট বা এক্সটার্নাল ডেটা ইন্ডেক্স বা সার্চ করে, তাহলে ইন্ডেক্স করা বা সার্চ ফলাফল দেখানোর আগে সোর্স টেক্সট বা ছবিতে কন্টেন্ট মডারেশন ও নীতি ফিল্টার প্রয়োগ করুন।
    • ডাউনস্ট্রিম জেনারেটিভ AI পাইপলাইন সুরক্ষিত করা: যদি উদ্ধার করা ডকুমেন্ট বা প্যাসেজ ডাউনস্ট্রিম জেনারেটিভ AI মডেলে প্রসঙ্গ হিসেবে পাস করা হয়, তাহলে যাচাই করুন যে সেই ডাউনস্ট্রিম মডেল এবং ব্যবহারকারী নিজে ইন্টার‌্যাক্ট করতে পারেন এমন আউটপুটে নিরাপত্তা ফিল্টার এবং কন্টেন্ট নীতি চালু আছে কিনা।

অতিরিক্ত রিসোর্স