日本熟妇hd丰满老熟妇,中文字幕一区二区三区在线不卡 ,亚洲成片在线观看,免费女同在线一区二区

如何優(yōu)化表格存儲(chǔ)的向量檢索效果

更新時(shí)間:

表格存儲(chǔ)基于多元索引提供了向量檢索的能力,可以在大規(guī)模數(shù)據(jù)集中找到最相似的數(shù)據(jù)項(xiàng)。如果您在使用向量檢索進(jìn)行語(yǔ)義搜索時(shí)的檢索效果不符合預(yù)期,請(qǐng)按照本文的排查思路進(jìn)行向量檢索優(yōu)化。

向量檢索評(píng)分公式

說(shuō)明

表格存儲(chǔ)向量檢索(KnnVectorQuery)使用數(shù)值向量進(jìn)行近似最近鄰查詢,適用于檢索增強(qiáng)生成(RAG)、推薦系統(tǒng)、相似性檢測(cè)、自然語(yǔ)言處理與語(yǔ)義搜索等場(chǎng)景。如何使用向量檢索,請(qǐng)參見(jiàn)向量檢索介紹與使用。

向量檢索支持的距離度量算法包括歐氏距離(euclidean)、余弦相似度(cosine)、點(diǎn)積(dot_product)。不同距離度量算法的評(píng)分公式不同,表格存儲(chǔ)內(nèi)部通過(guò)距離度量算法的評(píng)分公式來(lái)評(píng)估向量之間的相似度。具體評(píng)分公式請(qǐng)參見(jiàn)下表。

MetricType

評(píng)分公式

歐氏距離(euclidean)

點(diǎn)積(dot_product)

余弦相似度(cosine)

排查分析

1. 檢查排序方式

在使用向量檢索時(shí),請(qǐng)您手動(dòng)設(shè)置排序規(guī)則為按照分?jǐn)?shù)排序,即使用ScoreSort。默認(rèn)情況下按照主鍵排序。

2. 調(diào)整與BoolQuery的組合使用方式

如果您在組合使用KnnVectorQuery(向量檢索)與BoolQuery(多條件組合查詢),建議將多元索引的查詢類型設(shè)置為KnnVectorQuery。BoolQuery的查詢條件設(shè)置到Filter(向量檢索過(guò)濾器)中,不影響評(píng)分分?jǐn)?shù)計(jì)算。

重要

如果您將查詢類型設(shè)置為BoolQuery,KnnVectorQuery作為BoolQuery中的子條件,則BoolQuery中的其他查詢條件可能影響評(píng)分分?jǐn)?shù)的計(jì)算。更多信息,請(qǐng)參見(jiàn)BoolQuery組合使用說(shuō)明

以下為向量檢索的Java示例代碼。

private static void knnVectorQuery(SyncClient client) {
    SearchQuery searchQuery = new SearchQuery();
    KnnVectorQuery query = new KnnVectorQuery();
    query.setFieldName("Col_Vector");
    query.setTopK(10); // 返回最鄰近的topK。
    query.setFloat32QueryVector(new float[]{0.1f, 0.2f, 0.3f, 0.4f});
    // 最鄰近的向量需要滿足Col_Keyword=hangzhou && Col_Long<4條件。
    query.setFilter(QueryBuilders.bool()
            .must(QueryBuilders.term("Col_Keyword", "hangzhou"))
            .must(QueryBuilders.range("Col_Long").lessThan(4))
    );
    searchQuery.setQuery(query);
    searchQuery.setLimit(10);
    // 按照分?jǐn)?shù)排序。
    searchQuery.setSort(new Sort(Collections.singletonList(new ScoreSort())));
    SearchRequest searchRequest = new SearchRequest("<TABLE_NAME>", "<SEARCH_INDEX_NAME>", searchQuery);
    SearchRequest.ColumnsToGet columnsToGet = new SearchRequest.ColumnsToGet();
    columnsToGet.setColumns(Arrays.asList("Col_Keyword", "Col_Long"));
    searchRequest.setColumnsToGet(columnsToGet);
    // 訪問(wèn)Search接口。
    SearchResponse resp = client.search(searchRequest);
    for (SearchHit hit : resp.getSearchHits()) {
        // 打印分?jǐn)?shù)。
        System.out.println(hit.getScore());
        // 打印數(shù)據(jù)。
        System.out.println(hit.getRow());
    }
}

3. 檢查向量的生成效果

表格存儲(chǔ)僅對(duì)向量數(shù)據(jù)進(jìn)行相似度的計(jì)算,并不涉及向量生成的效果是否最佳的問(wèn)題。數(shù)據(jù)庫(kù)中的向量和查詢的向量均由外部Embedding模型生成寫入,因此在針對(duì)一些專業(yè)性特別強(qiáng)的場(chǎng)景,生成的向量可能效果不佳。接下來(lái)針對(duì)此問(wèn)題進(jìn)行排查。

  1. 使用外圍(不使用表格存儲(chǔ))直接計(jì)算分?jǐn)?shù)。

    1. 將查詢的向量命名為向量a,將希望召回的表格存儲(chǔ)表中的向量命名為向量b。

      說(shuō)明

      您可以通過(guò)多元索引、二級(jí)索引或?qū)挶頂?shù)據(jù)讀取接口獲取向量b數(shù)據(jù)。

    2. 根據(jù)附錄:向量檢索評(píng)分公式的演示代碼MetricFunction.COSINE.compare(a, b)方法,計(jì)算出分?jǐn)?shù)a。

  2. 使用表格存儲(chǔ)計(jì)算分?jǐn)?shù)。

    使用表格存儲(chǔ)的向量檢索功能查詢向量a,然后查看返回結(jié)果中每行數(shù)據(jù)的分?jǐn)?shù)b。

  3. 對(duì)比分析

    如果表格存儲(chǔ)的向量檢索中未查詢到向量b所在的行數(shù)據(jù),則理論上返回結(jié)果中每行數(shù)據(jù)的分?jǐn)?shù)b均高于分?jǐn)?shù)a

    此時(shí)可驗(yàn)證,Embedding模型生成效果不佳導(dǎo)致向量檢索效果不理想。由于在召回結(jié)果中僅存在高于用戶實(shí)際期望分?jǐn)?shù)的向量數(shù)據(jù),因此無(wú)法返回用戶所期望的較低分?jǐn)?shù)的向量數(shù)據(jù)。

  4. 建議方案

    該問(wèn)題一般發(fā)生在專業(yè)場(chǎng)景下,例如生物醫(yī)療中特殊的名詞在通用的Embedding模型下表現(xiàn)不佳,在專業(yè)場(chǎng)景下語(yǔ)義相近但是在模型中語(yǔ)義不相近,此時(shí)候您可考慮以下方案:

    • 尋找專業(yè)領(lǐng)域的Embedding模型。

      魔搭社區(qū)提供了大量現(xiàn)成的Embedding模型。您可以選擇政務(wù)、電商、醫(yī)療、法律、金融等專業(yè)領(lǐng)域的模型。更多信息,請(qǐng)參見(jiàn)Embedding模型列表。

    • 通過(guò)合法途徑收集大量的專業(yè)語(yǔ)料,以此訓(xùn)練一個(gè)合適的Embedding模型。

附錄:向量檢索評(píng)分公式的演示代碼

以下通過(guò)Java代碼演示距離度量算法的評(píng)分公式。

import java.util.concurrent.ThreadLocalRandom;

public class CompareVector {

    public static void main(String[] args) {
        // a 是查詢的向量
        float[] a = randomVector(512);
        // b 是索引中期望返回的那一行向量
        float[] b = randomVector(512);
        // 這里選擇自己多元索引中自己設(shè)置的相似度量算法,輸出評(píng)分
        System.out.println(MetricFunction.COSINE.compare(a, b));
    }

    public static float[] randomVector(int dim) {
        float[] vec = new float[dim];
        for (int i = 0; i < dim; i++) {
            vec[i] = ThreadLocalRandom.current().nextFloat();
            if (ThreadLocalRandom.current().nextBoolean()) {
                vec[i] = -vec[i];
            }
        }
        return l2normalize(vec, true);
    }

    public static float[] l2normalize(float[] v, boolean throwOnZero) {
        double squareSum = 0.0f;
        int dim = v.length;
        for (float x : v) {
            squareSum += x * x;
        }
        if (squareSum == 0) {
            if (throwOnZero) {
                throw new IllegalArgumentException("normalize a zero-length vector");
            } else {
                return v;
            }
        }
        double length = Math.sqrt(squareSum);
        for (int i = 0; i < dim; i++) {
            v[i] /= length;
        }
        return v;
    }

    public enum MetricFunction {
        /**
         * Euclidean distance.
         */
        EUCLIDEAN {
            @Override
            public float compare(float[] v1, float[] v2) {
                return 1 / (1 + VectorUtil.squareDistance(v1, v2));
            }
        },

        /**
         * Dot product.
         */
        DOT_PRODUCT {
            @Override
            public float compare(float[] v1, float[] v2) {
                return (1 + VectorUtil.dotProduct(v1, v2)) / 2;
            }
        },

        /**
         * Cosine.
         */
        COSINE {
            @Override
            public float compare(float[] v1, float[] v2) {
                return (1 + VectorUtil.cosine(v1, v2)) / 2;
            }
        };

        public abstract float compare(float[] v1, float[] v2);
    }


    static final class VectorUtil {

        private static void checkParam(float[] a, float[] b) {
            if (a.length != b.length) {
                throw new IllegalArgumentException("vector dimensions differ: " + a.length + "!=" + b.length);
            }
        }

        public static float dotProduct(float[] a, float[] b) {
            checkParam(a, b);
            float res = 0f;
            for (int i = 0; i < a.length; i++) {
                res += b[i] * a[i];
            }
            return res;
        }

        public static float cosine(float[] a, float[] b) {
            checkParam(a, b);
            float sum = 0.0f;
            float norm1 = 0.0f;
            float norm2 = 0.0f;
            for (int i = 0; i < a.length; i++) {
                float elem1 = a[i];
                float elem2 = b[i];
                sum += elem1 * elem2;
                norm1 += elem1 * elem1;
                norm2 += elem2 * elem2;
            }
            return (float) (sum / Math.sqrt((double) norm1 * (double) norm2));
        }

        public static float squareDistance(float[] a, float[] b) {
            checkParam(a, b);
            float sum = 0.0f;
            for (int i = 0; i < a.length; i++) {
                float difference = a[i] - b[i];
                sum += difference * difference;
            }
            return sum;
        }
    }
}