Delta Lake: High-Per无需重写formance ACID Table Storage over Cloud Object Stores

什么是向量?

向量是数学、物理学和工程科学等多个自然科学中的基本概念,它是一个具有方向和长度的量,用于描述问题,如空间几何、力学、信号处理等。在计算机科学中,向量被用于表示数据,如文本、图像或音频。此外,向量还代表AI模型对文本、图像、音频、视频等非结构化数据的印象。

向量相似度搜索的基本原理

向量相似度搜索的基本原理是通过将数据集中的每个元素映射为向量,并使用特定相似度计算算法,如基于余弦相似度的、基于欧氏相似度或基于Jaccard相似度等算法,找到与查询向量最相似的向量。

Redis实现向量相似度搜索

了解原理后,我们开始来实现如何使用Redis实现向量相似度搜索。Redis允许我们在FT.SEARCH命令中使用向量相似度查询。使我们可以加载、索引和查询作为Redis哈希或JSON文档中字段存储的向量。

//相关文档地址

Vector similarity | Redis

1、Redis Search安装

关于Redis Search的安装和使用,此处不再赘述,如果您对此不熟悉,可以参考上一篇文章:

C#+Redis Search:如何用Redis实现高性能全文搜索

2、创建向量索引库

这里我们使用NRedisStack和StackExchange.Redis两个库来与Redis进行交互操作。

复制代码

//创建一个Redis连接
static ConnectionMultiplexer mux = ConnectionMultiplexer.Connect("localhost");
//获取一个Redis数据库
static IDatabase db = mux.GetDatabase();
//创建一个RediSearch客户端
static SearchCommands ft = new SearchCommands(db, null);

复制代码

在进行向量搜索之前,首先需要定义并创建索引,并指定相似性算法。

复制代码

public static async Task CreateIndexAsync()
{
    await ft.CreateAsync(indexName,
        new FTCreateParams()
                    .On(IndexDataType.HASH)
                    .Prefix(prefix),
        new Schema()
                    .AddTagField("tag")
                    .AddTextField("content")
                    .AddVectorField("vector",
                        VectorField.VectorAlgo.HNSW,
                        new Dictionary<string, object>()
                        {
                            ["TYPE"] = "FLOAT32",
                            ["DIM"] = 2,
                            ["DISTANCE_METRIC"] = "COSINE"
                        }));
}

复制代码

这段代码的意思是:

  • 使用了一个异步方法 ft.CreateAsync 来创建索引。它接受三个参数:索引名称 indexName,一个 FTCreateParams 对象和一个 Schema 对象;
  • FTCreateParams 类提供了一些参数选项,用于指定索引的参数。这里使用 .On(IndexDataType.HASH)  方法来指定索引数据类型为哈希,并使用 .Prefix(prefix)  方法来指定索引数据的前缀;
  • Schema 类用于定义索引中的字段和字段类型。这里定义了一个标签字段(tag field)用于区分过虑数据。定义了一个文本字段(text field)用于存储原始数据,以及一个向量字段(vector field)用于存储经原始数据转化后的向量数据;
  • 使用了 VectorField.VectorAlgo.HNSW 来指定向量算法为 HNSW(Hierarchical Navigable Small World)。还传递了一个字典对象,用于设置向量字段的参数。其中,键为字符串类型,值为对象类型。
目前Redis支持两种相似度算法:

HNSW分层导航小世界算法,使用小世界网络构建索引,具有快速查询速度和小内存占用,时间复杂度为O(logn),适用于大规模索引。

FLAT暴力算法,它对所有的键值对进行扫描,然后根据键值对的距离计算出最短路径,时间复杂度为O(n),其中n是键值对的数量。这种算法时间复杂度非常高,只适用于小规模的索引。

3、添加向量到索引库

索引创建后,我们将数据添加到索引中。

复制代码

public async Task SetAsync(string docId, string prefix, string tag, string content, float[] vector)
{
    await db.HashSetAsync($"{prefix}{docId}", new HashEntry[] {
        new HashEntry ("tag", tag),
        new HashEntry ("content", content),
        new HashEntry ("vector", vector.SelectMany(BitConverter.GetBytes).ToArray())
    });
}

复制代码

SetAsync方法用于将一个具有指定文档ID、前缀、标签、内容及内容的向量存储到索引库中。并使用SelectMany()方法和BitConverter.GetBytes()方法将向量转换为一个字节数组。

4、向量搜索

Redis 支持两种类型的向量查询:KNN查询和Range查询,也可以将两种查询混合使用。

KNN 查询

KNN 查询用于在给定查询向量的情况下查找前 N 个最相似的向量。

复制代码

public async IAsyncEnumerable<(string Content, double Score)> SearchAsync(float[] vector, int limit)
{
    var query = new Query($"*=>[KNN {limit} @vector $vector AS score]")
                .AddParam("vector", vector.SelectMany(BitConverter.GetBytes).ToArray())
                .SetSortBy("score")
                .ReturnFields("content", "score")
                .Limit(0, limit)
                .Dialect(2);

    var result = await ft.SearchAsync(indexName, query).ConfigureAwait(false);
    foreach (var document in result.Documents)
    {
        yield return (document["content"],Convert.ToDouble(document["score"]));
    }
}

复制代码

这段代码的意思是:

  • 创建一个查询对象 query,并设置查询条件。查询条件包括:
    1. "*=>[KNN {limit} @vector $vector AS score]":使用KNN算法进行向量相似度搜索,限制结果数量为limit,使用给定的向量vector作为查询向量,将查询结果按照相似度得分进行排序;
    2. AddParam("vector", vector.SelectMany(BitConverter.GetBytes).ToArray()):将浮点数数组转换为字节数组,并将其作为查询参数传递给查询;
    3. SetSortBy("score"):按照相似度得分对结果进行排序;
    4. ReturnFields("content", "score"):将content和score两个字段从结果集中返回;
    5. Limit(0, limit):限制结果集的起始位置为0,结果数量为limit;
    6. Dialect(2):设置查询方言为2,即Redis默认的查询语言Redis Protocol;
  • 调用异步搜索方法 ft.SearchAsync(indexName, query),并等待搜索结果;
  • 遍历搜索结果集 result.Documents,将每个文档转换为 (string Content, double Score) 元组,并通过 yield 语句进行迭代返回。
Range 查询:

Range查询提供了一种根据 Redis 中的向量字段与基于某些预定义阈值(半径)的查询向量之间的距离来过滤结果的方法。类似于 NUMERIC 和 GEO 子句,可以在查询中多次出现,特别是可以和 KNN 进行混合搜索。

复制代码

public static async IAsyncEnumerable<(string Tag, string Content, double Score)> SearchAsync(string tag, float[] vector, int limit)
{
    var query = new Query($"(@tag:{tag})=>[KNN {limit} @vector $vector AS score]")
                .AddParam("vector", vector.SelectMany(BitConverter.GetBytes).ToArray())
                .SetSortBy("score")
                .ReturnFields("tag", "content", "score")
                .Limit(0, limit)
                .Dialect(2);

    var result = await ft.SearchAsync(indexName, query).ConfigureAwait(false);
    foreach (var document in result.Documents)
    {
        yield return (document["tag"], document["content"], Convert.ToDouble(document["score"]));
    }
}

复制代码

这段代码使用了KNN和Range混合查询,与上一段代码相比,新增了@tag参数,将限制结果仅包含给定标签的内容。这样做可以增加查询的准确性,提高查询效率。

5、从索引库中删除向量

public async Task DeleteAsync(string docId, string prefix)
{
    await db.KeyDeleteAsync($"{prefix}{docId}");
}

这个方法通过删除与指定向量相关联的哈希缓存键,来实现从索引库中删除指定向量数据。

6、删除向量索引库

public async Task DropIndexAsync()
{
    await ft.DropIndexAsync(indexName, true);
}

这个方法 await ft.DropIndexAsync接受两个参数: indexName 和 true 。indexName 表示索引库的名称, true 表示在删除索引时是否删除索引文件。

7、查询索引库信息

public async Task<InfoResult> InfoAsync()
{
    return await ft.InfoAsync(indexName);
}

通过 await ft.InfoAsync(indexName) 方法,我们可以获取到指定索引库的大小,文档数量等相关索引库信息。

完整 Demo 如下:

复制代码

using NRedisStack;
using NRedisStack.Search;
using NRedisStack.Search.DataTypes;
using NRedisStack.Search.Literals.Enums;
using StackExchange.Redis;
using static NRedisStack.Search.Schema;

namespace RedisVectorExample
{
    class Program
    {
        //创建一个Redis连接
        static ConnectionMultiplexer mux = ConnectionMultiplexer.Connect("localhost");
        //获取一个Redis数据库
        static IDatabase db = mux.GetDatabase();
        //创建一个RediSearch客户端
        static SearchCommands ft = new SearchCommands(db, null);
        //索引名称
        static string indexName = "test:index";
        //索引前缀
        static string prefix = "test:data";
        static async Task Main(string[] args)
        {  
            //创建一个向量的索引
            await CreateIndexAsync();

            //添加一些向量到索引中
            await SetAsync("1", "A", "测试数据A1", new float[] { 0.1f, 0.2f });
            await SetAsync("2", "A", "测试数据A2", new float[] { 0.3f, 0.4f });
            await SetAsync("3", "B", "测试数据B1", new float[] { 0.5f, 0.6f });
            await SetAsync("4", "C", "测试数据C1", new float[] { 0.7f, 0.8f });

            //删除一个向量
            await DeleteAsync("4");

            //KUN搜索 
            await foreach (var (Content, Score) in SearchAsync(new float[] { 0.1f, 0.2f }, 2))
            {
                Console.WriteLine($"内容:{Content},相似度得分:{Score}");
            }

            //混合
            await foreach (var (Tag, Content, Score) in SearchAsync("A", new float[] { 0.1f, 0.2f }, 2))
            {
                Console.WriteLine($"标签:{Tag},内容:{Content},相似度得分:{Score}");
            }
            
            //检查索引是否存在
            var info = await InfoAsync();
            if (info != null)
                await DropIndexAsync(); //存在则删除索引
        }

        public static async Task CreateIndexAsync()
        {
            await ft.CreateAsync(indexName,
                new FTCreateParams()
                            .On(IndexDataType.HASH)
                            .Prefix(prefix),
                new Schema()
                            .AddTagField("tag")
                            .AddTextField("content")
                            .AddVectorField("vector",
                                VectorField.VectorAlgo.HNSW,
                                new Dictionary<string, object>()
                                {
                                    ["TYPE"] = "FLOAT32",
                                    ["DIM"] = 2,
                                    ["DISTANCE_METRIC"] = "COSINE"
                                }));
        }

        public static async Task SetAsync(string docId, string tag, string content, float[] vector)
        {
            await db.HashSetAsync($"{prefix}{docId}", new HashEntry[] {
                new HashEntry ("tag", tag),
                new HashEntry ("content", content),
                new HashEntry ("vector", vector.SelectMany(BitConverter.GetBytes).ToArray())
            });
        }

        public static async Task DeleteAsync(string docId)
        {
            await db.KeyDeleteAsync($"{prefix}{docId}");
        }

        public static async Task DropIndexAsync()
        {
            await ft.DropIndexAsync(indexName, true);
        }

        public static async Task<InfoResult> InfoAsync()
        {
            return await ft.InfoAsync(indexName);
        }

        public static async IAsyncEnumerable<(string Content, double Score)> SearchAsync(float[] vector, int limit)
        {
            var query = new Query($"*=>[KNN {limit} @vector $vector AS score]")
                        .AddParam("vector", vector.SelectMany(BitConverter.GetBytes).ToArray())
                        .SetSortBy("score")
                        .ReturnFields("content", "score")
                        .Limit(0, limit)
                        .Dialect(2);

            var result = await ft.SearchAsync(indexName, query).ConfigureAwait(false);
            foreach (var document in result.Documents)
            {
                yield return (document["content"], Convert.ToDouble(document["score"]));
            }
        }

        public static async IAsyncEnumerable<(string Tag, string Content, double Score)> SearchAsync(string tag, float[] vector, int limit)
        {
            var query = new Query($"(@tag:{tag})=>[KNN {limit} @vector $vector AS score]")
                        .AddParam("vector", vector.SelectMany(BitConverter.GetBytes).ToArray())
                        .SetSortBy("score")
                        .ReturnFields("tag", "content", "score")
                        .Limit(0, limit)
                        .Dialect(2);

            var result = await ft.SearchAsync(indexName, query).ConfigureAwait(false);
            foreach (var document in result.Documents)
            {
                yield return (document["tag"], document["content"], Convert.ToDouble(document["score"]));
            }
        }
    }
}

复制代码

Unlike the classical encryption schemes,keys are dispensable in certain PLS technigues, known as the keyless secure strat egy. Sophisticated signal processing techniques such as arti- ficial noise, beamforming,and diversitycan be developed to ensure the secrecy of the MC networks.In the Alice-Bob-Eve model, Alice is the legitimate transmitter, whose intended target is the legitimate receiver Bob,while Eve is the eavesdropper that intercepts the information from Alice to Bob.The secrecy performance is quantified via information leakagei.ethe dif ference of the mutual information between the Alice-Bob and Alice-Eve links. The upper bound of the information leakage is called secrecy capacity realized by a specific distribution of the input symbols, namely,capacity-achieving distribution.The secrecy performance of the diffusion-based MC system with concentration shift keying(CSK)is analyzed from an informa- tion-theoretical point of view,providing two paramount secrecy metrics, i.e., secrecy capacity and secure distance[13].How ever, only the estimation of lower bound secrecy capacity is derived as both links attain their channel capacity.The secrecy capacity highly depends on the system parameters such as the average signal energy,diffusion coefficientand reception duration. Moreover, the distance between the transmitter and the eavesdropper is also an important aspect of secrecy per- formance. For both amplitude and energy detection schemes secure distance is proposed as a secret metricover which the eavesdropper is incapable of signal recovery. Despite the case with CSK,the results of the secure metrics vary with the modulation type(e.g.pulse position,spacetype) and reception mechanism(e.g.passive,partially absorbingper fectly absorbing).For ease of understanding,Figure 3 depicts the modulation types and the corresponding CIRs with different reception mechanisms. Novel signa processing techniques and the biochemical channel properties can further assist the secrecy enhancement in the MC system.The molecular beam forming that avoids information disclosure can be realized via the flow generated in the channel.Besidesnew dimensions of diversity, such as the aforementioned molecular diversity of ionic compounds, can beexploited. Note that the feasibility of these methods can be validated by the derived secrecy metrics.
06-13
该论文主要介绍了在某些物理层安全技术中,与传统加密技术不同的是,密钥并不是必须的,这种技术被称为“无密钥安全策略”。通过使用先进的信号处理技术,如人工噪声、波束成形和多样性等,可以确保分子通信网络的安全性。文章还介绍了Alice-Bob-Eve模型,其中Alice是合法的发射器,其目标是合法的接收器Bob,而Eve是窃听者,从Alice到Bob的信息进行拦截。通过信息泄漏的度量,即Alice-Bob和Alice-Eve之间的互信息差异,来量化保密性能。信息泄漏的上界称为特定输入符号分布实现的保密容量。从信息论的角度分析了基于扩散的浓度偏移键控分子通信系统的保密性能,提供了两个重要的保密度量,即保密容量和安全距离。然而,只得到了保密容量的下界估计,因为两条链路都达到了其信道容量。保密容量高度依赖于系统参数,如平均信号能量、扩散系数和接收持续时间。此外,发射器与窃听者之间的距离也是保密性能的重要方面。对于幅度和能量检测方案,提出了安全距离作为一种保密度量,超过这个距离窃听者无法恢复信号。尽管使用CSK的情况,保密度量的结果会随着调制类型(如脉冲位置、空间类型)和接收机制(如被动、部分吸收、完全吸收)而有所不同。为了方便理解,图3描述了不同接收机制下的调制类型及其相应的通道冲激响应。此外,新的信号处理技术和生物化学通道特性可以进一步提高分子通信系统的保密性。通过通道中产生的流可以实现分子波束成形,避免信息泄露。此外,还可以利用分子离子化合物等新的多样性维度。需要注意的是,这些方法的可行性可以通过推导的保密度量进行验证。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值