向量
向量(或嵌入)是 Qdrant 向量搜索引擎的核心概念。向量定义了向量空间中对象之间的相似性。
如果在向量空间中一对向量相似,则意味着它们代表的对象在某种程度上是相似的。
例如,如果你有一个图片集合,你可以将每张图片表示为一个向量。如果两张图片相似,它们的向量在向量空间中就会彼此接近。
为了获得对象的向量表示,你需要对对象应用向量化算法。通常,这个算法是一个神经网络,它将对象转换为固定大小的向量。
神经网络通常会在相似和不相似对象的对或三元组上进行训练,以便学习识别特定类型的相似性。
利用向量的这个特性,你可以通过多种方式探索你的数据;例如,通过搜索相似对象、聚类对象等。
向量类型
现代神经网络可以输出不同形状和大小的向量,Qdrant 支持其中大部分。让我们来看一下 Qdrant 支持的最常见的向量类型。
密集向量
这是最常见的向量类型。它是一个简单的数字列表,具有固定长度,列表中的每个元素都是浮点数。
它看起来像这样
// A piece of a real-world dense vector
[
-0.013052909,
0.020387933,
-0.007869,
-0.11111383,
-0.030188112,
-0.0053388323,
0.0010654867,
0.072027855,
-0.04167721,
0.014839341,
-0.032948174,
-0.062975034,
-0.024837125,
....
]
大多数神经网络会创建密集向量,因此你可以直接在 Qdrant 中使用它们,无需额外处理。虽然与大多数嵌入模型兼容,Qdrant 已对以下已验证的嵌入提供商进行了测试。
稀疏向量
稀疏向量是一种特殊的向量类型。数学上,它们与密集向量相同,但包含大量零,因此以特殊格式存储。
Qdrant 中的稀疏向量没有固定长度,因为在向量插入过程中会动态分配。稀疏向量中非零值的数量目前限制在 u32 数据类型范围 (4294967295)。
为了定义稀疏向量,你需要提供一个非零元素及其索引的列表。
// A sparse vector with 4 non-zero elements
{
"indexes": [1, 3, 5, 7],
"values": [0.1, 0.2, 0.3, 0.4]
}
Qdrant 中的稀疏向量存储在特殊的存储中,并在单独的索引中建立索引,因此它们的配置与密集向量不同。
创建包含稀疏向量的集合
PUT /collections/{collection_name}
{
"sparse_vectors": {
"text": { }
}
}
curl -X PUT http://localhost:6333/collections/{collection_name} \
-H 'Content-Type: application/json' \
--data-raw '{
"sparse_vectors": {
"text": { }
}
}'
from qdrant_client import QdrantClient, models
client = QdrantClient(url="http://localhost:6333")
client.create_collection(
collection_name="{collection_name}",
vectors_config={},
sparse_vectors_config={
"text": models.SparseVectorParams(),
},
)
import { QdrantClient } from "@qdrant/js-client-rest";
const client = new QdrantClient({ host: "localhost", port: 6333 });
client.createCollection("{collection_name}", {
sparse_vectors: {
text: { },
},
});
use qdrant_client::Qdrant;
use qdrant_client::qdrant::{
CreateCollectionBuilder, SparseVectorParamsBuilder, SparseVectorsConfigBuilder,
};
let client = Qdrant::from_url("http://localhost:6334").build()?;
let mut sparse_vector_config = SparseVectorsConfigBuilder::default();
sparse_vector_config.add_named_vector_params("text", SparseVectorParamsBuilder::default());
client
.create_collection(
CreateCollectionBuilder::new("{collection_name}")
.sparse_vectors_config(sparse_vector_config),
)
.await?;
import io.qdrant.client.QdrantClient;
import io.qdrant.client.QdrantGrpcClient;
import io.qdrant.client.grpc.Collections.CreateCollection;
import io.qdrant.client.grpc.Collections.SparseVectorConfig;
import io.qdrant.client.grpc.Collections.SparseVectorParams;
QdrantClient client =
new QdrantClient(QdrantGrpcClient.newBuilder("localhost", 6334, false).build());
client
.createCollectionAsync(
CreateCollection.newBuilder()
.setCollectionName("{collection_name}")
.setSparseVectorsConfig(
SparseVectorConfig.newBuilder()
.putMap("text", SparseVectorParams.getDefaultInstance()))
.build())
.get();
using Qdrant.Client;
using Qdrant.Client.Grpc;
var client = new QdrantClient("localhost", 6334);
await client.CreateCollectionAsync(
collectionName: "{collection_name}",
sparseVectorsConfig: ("text", new SparseVectorParams())
);
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.CreateCollection(context.Background(), &qdrant.CreateCollection{
CollectionName: "{collection_name}",
SparseVectorsConfig: qdrant.NewSparseVectorsConfig(
map[string]*qdrant.SparseVectorParams{
"text": {},
}),
})
在创建的集合中插入一个包含稀疏向量的点
PUT /collections/{collection_name}/points
{
"points": [
{
"id": 1,
"vector": {
"text": {
"indices": [1, 3, 5, 7],
"values": [0.1, 0.2, 0.3, 0.4]
}
}
}
]
}
from qdrant_client import QdrantClient, models
client = QdrantClient(url="http://localhost:6333")
client.upsert(
collection_name="{collection_name}",
points=[
models.PointStruct(
id=1,
payload={}, # Add any additional payload if necessary
vector={
"text": models.SparseVector(
indices=[1, 3, 5, 7],
values=[0.1, 0.2, 0.3, 0.4]
)
},
)
],
)
import { QdrantClient } from "@qdrant/js-client-rest";
const client = new QdrantClient({ host: "localhost", port: 6333 });
client.upsert("{collection_name}", {
points: [
{
id: 1,
vector: {
text: {
indices: [1, 3, 5, 7],
values: [0.1, 0.2, 0.3, 0.4]
},
},
}
]
});
use qdrant_client::qdrant::{NamedVectors, PointStruct, UpsertPointsBuilder, Vector};
use qdrant_client::{Payload, Qdrant};
let client = Qdrant::from_url("http://localhost:6334").build()?;
let points = vec![PointStruct::new(
1,
NamedVectors::default().add_vector(
"text",
Vector::new_sparse(vec![1, 3, 5, 7], vec![0.1, 0.2, 0.3, 0.4]),
),
Payload::new(),
)];
client
.upsert_points(UpsertPointsBuilder::new("{collection_name}", points))
.await?;
import java.util.List;
import java.util.Map;
import static io.qdrant.client.PointIdFactory.id;
import static io.qdrant.client.VectorFactory.vector;
import static io.qdrant.client.VectorsFactory.namedVectors;
import io.qdrant.client.QdrantClient;
import io.qdrant.client.QdrantGrpcClient;
import io.qdrant.client.grpc.Points.PointStruct;
QdrantClient client =
new QdrantClient(QdrantGrpcClient.newBuilder("localhost", 6334, false).build());
client
.upsertAsync(
"{collection_name}",
List.of(
PointStruct.newBuilder()
.setId(id(1))
.setVectors(
namedVectors(Map.of(
"text", vector(List.of(1.0f, 2.0f), List.of(6, 7))))
)
.build()))
.get();
using Qdrant.Client;
using Qdrant.Client.Grpc;
var client = new QdrantClient("localhost", 6334);
await client.UpsertAsync(
collectionName: "{collection_name}",
points: new List < PointStruct > {
new() {
Id = 1,
Vectors = new Dictionary <string, Vector> {
["text"] = ([0.1f, 0.2f, 0.3f, 0.4f], [1, 3, 5, 7])
}
}
}
);
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.Upsert(context.Background(), &qdrant.UpsertPoints{
CollectionName: "{collection_name}",
Points: []*qdrant.PointStruct{
{
Id: qdrant.NewIDNum(1),
Vectors: qdrant.NewVectorsMap(
map[string]*qdrant.Vector{
"text": qdrant.NewVectorSparse(
[]uint32{1, 3, 5, 7},
[]float32{0.1, 0.2, 0.3, 0.4}),
}),
},
},
})
现在你可以使用稀疏向量进行搜索
POST /collections/{collection_name}/points/query
{
"query": {
"indices": [1, 3, 5, 7],
"values": [0.1, 0.2, 0.3, 0.4]
},
"using": "text"
}
from qdrant_client import QdrantClient, models
client = QdrantClient(url="http://localhost:6333")
result = client.query_points(
collection_name="{collection_name}",
query=models.SparseVector(indices=[1, 3, 5, 7], values=[0.1, 0.2, 0.3, 0.4]),
using="text",
).points
import { QdrantClient } from "@qdrant/js-client-rest";
const client = new QdrantClient({ host: "localhost", port: 6333 });
client.query("{collection_name}", {
query: {
indices: [1, 3, 5, 7],
values: [0.1, 0.2, 0.3, 0.4]
},
using: "text",
limit: 3,
});
use qdrant_client::qdrant::QueryPointsBuilder;
use qdrant_client::Qdrant;
let client = Qdrant::from_url("http://localhost:6334").build()?;
client
.query(
QueryPointsBuilder::new("{collection_name}")
.query(vec![(1, 0.2), (3, 0.1), (5, 0.9), (7, 0.7)])
.limit(10)
.using("text"),
)
.await?;
import java.util.List;
import io.qdrant.client.QdrantClient;
import io.qdrant.client.QdrantGrpcClient;
import io.qdrant.client.grpc.Points.QueryPoints;
import static io.qdrant.client.QueryFactory.nearest;
QdrantClient client =
new QdrantClient(QdrantGrpcClient.newBuilder("localhost", 6334, false).build());
client.queryAsync(
QueryPoints.newBuilder()
.setCollectionName("{collection_name}")
.setUsing("text")
.setQuery(nearest(List.of(0.1f, 0.2f, 0.3f, 0.4f), List.of(1, 3, 5, 7)))
.setLimit(3)
.build())
.get();
using Qdrant.Client;
var client = new QdrantClient("localhost", 6334);
await client.QueryAsync(
collectionName: "{collection_name}",
query: new (float, uint)[] {(0.1f, 1), (0.2f, 3), (0.3f, 5), (0.4f, 7)},
usingVector: "text",
limit: 3
);
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.Query(context.Background(), &qdrant.QueryPoints{
CollectionName: "{collection_name}",
Query: qdrant.NewQuerySparse(
[]uint32{1, 3, 5, 7},
[]float32{0.1, 0.2, 0.3, 0.4}),
Using: qdrant.PtrOf("text"),
})
多向量
自 v1.10.0 版本起可用
Qdrant 支持在单个点中存储可变数量的同形密集向量。这意味着你可以上传一个密集向量矩阵,而不是单个密集向量。
矩阵的长度是固定的,但矩阵中向量的数量对于每个点可以不同。
多向量看起来像这样
// A multivector of size 4
"vector": [
[-0.013, 0.020, -0.007, -0.111],
[-0.030, -0.055, 0.001, 0.072],
[-0.041, 0.014, -0.032, -0.062],
....
]
多向量在以下两种场景中很有用
- 同一对象的多种表示 - 例如,你可以为同一对象从不同角度拍摄的图片存储多个嵌入。这种方法假设所有向量的载荷是相同的。
- 延迟交互嵌入 - 一些文本嵌入模型可以为单个文本输出多个向量。例如,像 ColBERT 这样的模型系列会为文本中的每个 token 输出一个相对较小的向量。
为了使用多向量,我们需要指定一个用于比较向量矩阵的函数
目前,Qdrant 支持 max_sim
函数,其定义为矩阵中每对向量之间最大相似度之和。
$$ score = \sum_{i=1}^{N} \max_{j=1}^{M} \text{Sim}(\text{vectorA}_i, \text{vectorB}_j) $$
其中 $N$ 是第一个矩阵中的向量数量,$M$ 是第二个矩阵中的向量数量,$\text{Sim}$ 是相似度函数,例如余弦相似度。
要使用多向量,请创建具有以下配置的集合
PUT collections/{collection_name}
{
"vectors": {
"size": 128,
"distance": "Cosine",
"multivector_config": {
"comparator": "max_sim"
}
}
}
from qdrant_client import QdrantClient, models
client = QdrantClient(url="http://localhost:6333")
client.create_collection(
collection_name="{collection_name}",
vectors_config=models.VectorParams(
size=128,
distance=models.Distance.COSINE,
multivector_config=models.MultiVectorConfig(
comparator=models.MultiVectorComparator.MAX_SIM
),
),
)
import { QdrantClient } from "@qdrant/js-client-rest";
const client = new QdrantClient({ host: "localhost", port: 6333 });
client.createCollection("{collection_name}", {
vectors: {
size: 128,
distance: "Cosine",
multivector_config: {
comparator: "max_sim"
}
},
});
use qdrant_client::qdrant::{
CreateCollectionBuilder, Distance, VectorParamsBuilder,
MultiVectorComparator, MultiVectorConfigBuilder,
};
use qdrant_client::Qdrant;
let client = Qdrant::from_url("http://localhost:6334").build()?;
client
.create_collection(
CreateCollectionBuilder::new("{collection_name}")
.vectors_config(
VectorParamsBuilder::new(100, Distance::Cosine)
.multivector_config(
MultiVectorConfigBuilder::new(MultiVectorComparator::MaxSim)
),
),
)
.await?;
import io.qdrant.client.QdrantClient;
import io.qdrant.client.QdrantGrpcClient;
import io.qdrant.client.grpc.Collections.Distance;
import io.qdrant.client.grpc.Collections.MultiVectorComparator;
import io.qdrant.client.grpc.Collections.MultiVectorConfig;
import io.qdrant.client.grpc.Collections.VectorParams;
QdrantClient client =
new QdrantClient(QdrantGrpcClient.newBuilder("localhost", 6334, false).build());
client.createCollectionAsync("{collection_name}",
VectorParams.newBuilder().setSize(128)
.setDistance(Distance.Cosine)
.setMultivectorConfig(MultiVectorConfig.newBuilder()
.setComparator(MultiVectorComparator.MaxSim)
.build())
.build()).get();
using Qdrant.Client;
using Qdrant.Client.Grpc;
var client = new QdrantClient("localhost", 6334);
await client.CreateCollectionAsync(
collectionName: "{collection_name}",
vectorsConfig: new VectorParams {
Size = 128,
Distance = Distance.Cosine,
MultivectorConfig = new() {
Comparator = MultiVectorComparator.MaxSim
}
}
);
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.CreateCollection(context.Background(), &qdrant.CreateCollection{
CollectionName: "{collection_name}",
VectorsConfig: qdrant.NewVectorsConfig(&qdrant.VectorParams{
Size: 128,
Distance: qdrant.Distance_Cosine,
MultivectorConfig: &qdrant.MultiVectorConfig{
Comparator: qdrant.MultiVectorComparator_MaxSim,
},
}),
})
插入一个包含多向量的点
PUT collections/{collection_name}/points
{
"points": [
{
"id": 1,
"vector": [
[-0.013, 0.020, -0.007, -0.111, ...],
[-0.030, -0.055, 0.001, 0.072, ...],
[-0.041, 0.014, -0.032, -0.062, ...]
]
}
]
}
from qdrant_client import QdrantClient, models
client = QdrantClient(url="http://localhost:6333")
client.upsert(
collection_name="{collection_name}",
points=[
models.PointStruct(
id=1,
vector=[
[-0.013, 0.020, -0.007, -0.111],
[-0.030, -0.055, 0.001, 0.072],
[-0.041, 0.014, -0.032, -0.062]
],
)
],
)
import { QdrantClient } from "@qdrant/js-client-rest";
const client = new QdrantClient({ host: "localhost", port: 6333 });
client.upsert("{collection_name}", {
points: [
{
id: 1,
vector: [
[-0.013, 0.020, -0.007, -0.111, ...],
[-0.030, -0.055, 0.001, 0.072, ...],
[-0.041, 0.014, -0.032, -0.062, ...]
],
}
]
});
use qdrant_client::qdrant::{PointStruct, UpsertPointsBuilder, Vector};
use qdrant_client::Qdrant;
let client = Qdrant::from_url("http://localhost:6334").build()?;
let points = vec![
PointStruct::new(
1,
Vector::new_multi(vec![
vec![-0.013, 0.020, -0.007, -0.111],
vec![-0.030, -0.055, 0.001, 0.072],
vec![-0.041, 0.014, -0.032, -0.062],
]),
Payload::new()
)
];
client
.upsert_points(
UpsertPointsBuilder::new("{collection_name}", points)
).await?;
import java.util.List;
import static io.qdrant.client.PointIdFactory.id;
import static io.qdrant.client.VectorsFactory.vectors;
import static io.qdrant.client.VectorFactory.multiVector;
import io.qdrant.client.QdrantClient;
import io.qdrant.client.QdrantGrpcClient;
import io.qdrant.client.grpc.Points.PointStruct;
QdrantClient client =
new QdrantClient(QdrantGrpcClient.newBuilder("localhost", 6334, false).build());
client
.upsertAsync(
"{collection_name}",
List.of(
PointStruct.newBuilder()
.setId(id(1))
.setVectors(vectors(multiVector(new float[][] {
{-0.013f, 0.020f, -0.007f, -0.111f},
{-0.030f, -0.055f, 0.001f, 0.072f},
{-0.041f, 0.014f, -0.032f, -0.062f}
})))
.build()
))
.get();
using Qdrant.Client;
using Qdrant.Client.Grpc;
var client = new QdrantClient("localhost", 6334);
await client.UpsertAsync(
collectionName: "{collection_name}",
points: new List <PointStruct> {
new() {
Id = 1,
Vectors = new float[][] {
[-0.013f, 0.020f, -0.007f, -0.111f],
[-0.030f, -0.05f, 0.001f, 0.072f],
[-0.041f, 0.014f, -0.032f, -0.062f ],
},
},
}
);
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.Upsert(context.Background(), &qdrant.UpsertPoints{
CollectionName: "{collection_name}",
Points: []*qdrant.PointStruct{
{
Id: qdrant.NewIDNum(1),
Vectors: qdrant.NewVectorsMulti(
[][]float32{
{-0.013, 0.020, -0.007, -0.111},
{-0.030, -0.055, 0.001, 0.072},
{-0.041, 0.014, -0.032, -0.062}}),
},
},
})
使用多向量进行搜索(在 query
API 中可用)
POST collections/{collection_name}/points/query
{
"query": [
[-0.013, 0.020, -0.007, -0.111, ...],
[-0.030, -0.055, 0.001, 0.072, ...],
[-0.041, 0.014, -0.032, -0.062, ...]
]
}
from qdrant_client import QdrantClient, models
client = QdrantClient(url="http://localhost:6333")
client.query_points(
collection_name="{collection_name}",
query=[
[-0.013, 0.020, -0.007, -0.111],
[-0.030, -0.055, 0.001, 0.072],
[-0.041, 0.014, -0.032, -0.062]
],
)
import { QdrantClient } from "@qdrant/js-client-rest";
const client = new QdrantClient({ host: "localhost", port: 6333 });
client.query("{collection_name}", {
"query": [
[-0.013, 0.020, -0.007, -0.111],
[-0.030, -0.055, 0.001, 0.072],
[-0.041, 0.014, -0.032, -0.062]
]
});
use qdrant_client::Qdrant;
use qdrant_client::qdrant::{ QueryPointsBuilder, VectorInput };
let client = Qdrant::from_url("http://localhost:6334").build()?;
let res = client.query(
QueryPointsBuilder::new("{collection_name}")
.query(VectorInput::new_multi(
vec![
vec![-0.013, 0.020, -0.007, -0.111],
vec![-0.030, -0.055, 0.001, 0.072],
vec![-0.041, 0.014, -0.032, -0.062],
]
))
).await?;
import static io.qdrant.client.QueryFactory.nearest;
import io.qdrant.client.QdrantClient;
import io.qdrant.client.QdrantGrpcClient;
import io.qdrant.client.grpc.Points.QueryPoints;
QdrantClient client =
new QdrantClient(QdrantGrpcClient.newBuilder("localhost", 6334, false).build());
client.queryAsync(QueryPoints.newBuilder()
.setCollectionName("{collection_name}")
.setQuery(nearest(new float[][] {
{-0.013f, 0.020f, -0.007f, -0.111f},
{-0.030f, -0.055f, 0.001f, 0.072f},
{-0.041f, 0.014f, -0.032f, -0.062f}
}))
.build()).get();
using Qdrant.Client;
var client = new QdrantClient("localhost", 6334);
await client.QueryAsync(
collectionName: "{collection_name}",
query: new float[][] {
[-0.013f, 0.020f, -0.007f, -0.111f],
[-0.030f, -0.055f, 0.001 , 0.072f],
[-0.041f, 0.014f, -0.032f, -0.062f],
}
);
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.Query(context.Background(), &qdrant.QueryPoints{
CollectionName: "{collection_name}",
Query: qdrant.NewQueryMulti(
[][]float32{
{-0.013, 0.020, -0.007, -0.111},
{-0.030, -0.055, 0.001, 0.072},
{-0.041, 0.014, -0.032, -0.062},
}),
})
命名向量
在 Qdrant 中,您可以在同一数据点中存储不同大小和类型的多个向量。当您需要使用多个嵌入来表示不同的特征或模态(例如图像、文本或视频)来定义数据时,这非常有用。
为了为每个点存储不同的向量,您需要在集合中创建单独的命名向量空间。您可以在创建集合时定义这些向量空间并独立管理它们。
要创建具有命名向量的集合,您需要为每个向量指定配置
PUT /collections/{collection_name}
{
"vectors": {
"image": {
"size": 4,
"distance": "Dot"
},
"text": {
"size": 5,
"distance": "Cosine"
}
},
"sparse_vectors": {
"text-sparse": {}
}
}
from qdrant_client import QdrantClient, models
client = QdrantClient(url="http://localhost:6333")
client.create_collection(
collection_name="{collection_name}",
vectors_config={
"image": models.VectorParams(size=4, distance=models.Distance.DOT),
"text": models.VectorParams(size=5, distance=models.Distance.COSINE),
},
sparse_vectors_config={"text-sparse": models.SparseVectorParams()},
)
import { QdrantClient } from "@qdrant/js-client-rest";
const client = new QdrantClient({ host: "localhost", port: 6333 });
client.createCollection("{collection_name}", {
vectors: {
image: { size: 4, distance: "Dot" },
text: { size: 5, distance: "Cosine" },
},
sparse_vectors: {
text_sparse: {}
}
});
use qdrant_client::qdrant::{
CreateCollectionBuilder, Distance, SparseVectorParamsBuilder, SparseVectorsConfigBuilder,
VectorParamsBuilder, VectorsConfigBuilder,
};
use qdrant_client::Qdrant;
let client = Qdrant::from_url("http://localhost:6334").build()?;
let mut vector_config = VectorsConfigBuilder::default();
vector_config.add_named_vector_params("text", VectorParamsBuilder::new(5, Distance::Dot));
vector_config.add_named_vector_params("image", VectorParamsBuilder::new(4, Distance::Cosine));
let mut sparse_vectors_config = SparseVectorsConfigBuilder::default();
sparse_vectors_config
.add_named_vector_params("text-sparse", SparseVectorParamsBuilder::default());
client
.create_collection(
CreateCollectionBuilder::new("{collection_name}")
.vectors_config(vector_config)
.sparse_vectors_config(sparse_vectors_config),
)
.await?;
import java.util.Map;
import io.qdrant.client.QdrantClient;
import io.qdrant.client.QdrantGrpcClient;
import io.qdrant.client.grpc.Collections.CreateCollection;
import io.qdrant.client.grpc.Collections.Distance;
import io.qdrant.client.grpc.Collections.SparseVectorConfig;
import io.qdrant.client.grpc.Collections.SparseVectorParams;
import io.qdrant.client.grpc.Collections.VectorParams;
import io.qdrant.client.grpc.Collections.VectorParamsMap;
import io.qdrant.client.grpc.Collections.VectorsConfig;
QdrantClient client =
new QdrantClient(QdrantGrpcClient.newBuilder("localhost", 6334, false).build());
client
.createCollectionAsync(
CreateCollection.newBuilder()
.setCollectionName("{collection_name}")
.setVectorsConfig(VectorsConfig.newBuilder().setParamsMap(
VectorParamsMap.newBuilder().putAllMap(Map.of("image",
VectorParams.newBuilder()
.setSize(4)
.setDistance(Distance.Dot)
.build(),
"text",
VectorParams.newBuilder()
.setSize(5)
.setDistance(Distance.Cosine)
.build()))))
.setSparseVectorsConfig(SparseVectorConfig.newBuilder().putMap(
"text-sparse", SparseVectorParams.getDefaultInstance()))
.build())
.get();
using Qdrant.Client;
using Qdrant.Client.Grpc;
var client = new QdrantClient("localhost", 6334);
await client.CreateCollectionAsync(
collectionName: "{collection_name}",
vectorsConfig: new VectorParamsMap
{
Map = {
["image"] = new VectorParams {
Size = 4, Distance = Distance.Dot
},
["text"] = new VectorParams {
Size = 5, Distance = Distance.Cosine
},
}
},
sparseVectorsConfig: new SparseVectorConfig
{
Map = {
["text-sparse"] = new()
}
}
);
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.CreateCollection(context.Background(), &qdrant.CreateCollection{
CollectionName: "{collection_name}",
VectorsConfig: qdrant.NewVectorsConfigMap(
map[string]*qdrant.VectorParams{
"image": {
Size: 4,
Distance: qdrant.Distance_Dot,
},
"text": {
Size: 5,
Distance: qdrant.Distance_Cosine,
},
}),
SparseVectorsConfig: qdrant.NewSparseVectorsConfig(
map[string]*qdrant.SparseVectorParams{
"text-sparse": {},
},
),
})
插入一个包含命名向量的点
PUT /collections/{collection_name}/points?wait=true
{
"points": [
{
"id": 1,
"vector": {
"image": [0.9, 0.1, 0.1, 0.2],
"text": [0.4, 0.7, 0.1, 0.8, 0.1],
"text-sparse": {
"indices": [1, 3, 5, 7],
"values": [0.1, 0.2, 0.3, 0.4]
}
}
}
]
}
client.upsert(
collection_name="{collection_name}",
points=[
models.PointStruct(
id=1,
vector={
"image": [0.9, 0.1, 0.1, 0.2],
"text": [0.4, 0.7, 0.1, 0.8, 0.1],
"text-sparse": {
"indices": [1, 3, 5, 7],
"values": [0.1, 0.2, 0.3, 0.4],
},
},
),
],
)
client.upsert("{collection_name}", {
points: [
{
id: 1,
vector: {
image: [0.9, 0.1, 0.1, 0.2],
text: [0.4, 0.7, 0.1, 0.8, 0.1],
text_sparse: {
indices: [1, 3, 5, 7],
values: [0.1, 0.2, 0.3, 0.4]
}
},
},
],
});
use qdrant_client::qdrant::{
NamedVectors, PointStruct, UpsertPointsBuilder, Vector,
};
use qdrant_client::Payload;
client
.upsert_points(
UpsertPointsBuilder::new(
"{collection_name}",
vec![PointStruct::new(
1,
NamedVectors::default()
.add_vector("text", Vector::new_dense(vec![0.4, 0.7, 0.1, 0.8, 0.1]))
.add_vector("image", Vector::new_dense(vec![0.9, 0.1, 0.1, 0.2]))
.add_vector(
"text-sparse",
Vector::new_sparse(vec![1, 3, 5, 7], vec![0.1, 0.2, 0.3, 0.4]),
),
Payload::default(),
)],
)
.wait(true),
)
.await?;
import java.util.List;
import java.util.Map;
import static io.qdrant.client.PointIdFactory.id;
import static io.qdrant.client.VectorFactory.vector;
import static io.qdrant.client.VectorsFactory.namedVectors;
import io.qdrant.client.grpc.Points.PointStruct;
client
.upsertAsync(
"{collection_name}",
List.of(
PointStruct.newBuilder()
.setId(id(1))
.setVectors(
namedVectors(
Map.of(
"image",
vector(List.of(0.9f, 0.1f, 0.1f, 0.2f)),
"text",
vector(List.of(0.4f, 0.7f, 0.1f, 0.8f, 0.1f)),
"text-sparse",
vector(List.of(0.1f, 0.2f, 0.3f, 0.4f), List.of(1, 3, 5, 7)))))
.build()))
.get();
using Qdrant.Client;
using Qdrant.Client.Grpc;
await client.UpsertAsync(
collectionName: "{collection_name}",
points: new List<PointStruct>
{
new()
{
Id = 1,
Vectors = new Dictionary<string, Vector>
{
["image"] = new() {
Data = {0.9f, 0.1f, 0.1f, 0.2f}
},
["text"] = new() {
Data = {0.4f, 0.7f, 0.1f, 0.8f, 0.1f}
},
["text-sparse"] = ([0.1f, 0.2f, 0.3f, 0.4f], [1, 3, 5, 7]),
}
}
}
);
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client.Upsert(context.Background(), &qdrant.UpsertPoints{
CollectionName: "{collection_name}",
Points: []*qdrant.PointStruct{
{
Id: qdrant.NewIDNum(1),
Vectors: qdrant.NewVectorsMap(map[string]*qdrant.Vector{
"image": qdrant.NewVector(0.9, 0.1, 0.1, 0.2),
"text": qdrant.NewVector(0.4, 0.7, 0.1, 0.8, 0.1),
"text-sparse": qdrant.NewVectorSparse(
[]uint32{1, 3, 5, 7},
[]float32{0.1, 0.2, 0.3, 0.4}),
}),
},
},
})
使用命名向量进行搜索(在 query
API 中可用)
POST /collections/{collection_name}/points/query
{
"query": [0.2, 0.1, 0.9, 0.7],
"using": "image",
"limit": 3
}
from qdrant_client import QdrantClient
client = QdrantClient(url="http://localhost:6333")
client.query_points(
collection_name="{collection_name}",
query=[0.2, 0.1, 0.9, 0.7],
using="image",
limit=3,
)
import { QdrantClient } from "@qdrant/js-client-rest";
const client = new QdrantClient({ host: "localhost", port: 6333 });
client.query("{collection_name}", {
query: [0.2, 0.1, 0.9, 0.7],
using: "image",
limit: 3,
});
use qdrant_client::qdrant::QueryPointsBuilder;
use qdrant_client::Qdrant;
let client = Qdrant::from_url("http://localhost:6334").build()?;
client
.query(
QueryPointsBuilder::new("{collection_name}")
.query(vec![0.2, 0.1, 0.9, 0.7])
.limit(3)
.using("image"),
)
.await?;
import java.util.List;
import io.qdrant.client.QdrantClient;
import io.qdrant.client.QdrantGrpcClient;
import io.qdrant.client.grpc.Points.QueryPoints;
import static io.qdrant.client.QueryFactory.nearest;
QdrantClient client =
new QdrantClient(QdrantGrpcClient.newBuilder("localhost", 6334, false).build());
client.queryAsync(QueryPoints.newBuilder()
.setCollectionName("{collection_name}")
.setQuery(nearest(0.2f, 0.1f, 0.9f, 0.7f))
.setUsing("image")
.setLimit(3)
.build()).get();
using Qdrant.Client;
var client = new QdrantClient("localhost", 6334);
await client.QueryAsync(
collectionName: "{collection_name}",
query: new float[] { 0.2f, 0.1f, 0.9f, 0.7f },
usingVector: "image",
limit: 3
);
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.Query(context.Background(), &qdrant.QueryPoints{
CollectionName: "{collection_name}",
Query: qdrant.NewQuery(0.2, 0.1, 0.9, 0.7),
Using: qdrant.PtrOf("image"),
})
数据类型
最新版本的嵌入模型生成具有非常高维度的向量。使用 OpenAI 的 text-embedding-3-large
嵌入模型,维度可以达到 3072。
存储此类向量所需的内存量随维度线性增长,因此为向量选择正确的数据类型非常重要。
数据类型之间的选择是内存消耗和向量精度之间的权衡。
Qdrant 支持多种数据类型,包括密集向量和稀疏向量
Float32
这是 Qdrant 中向量的默认数据类型。它是一个 32 位(4 字节)浮点数。标准 OpenAI 1536 维度的嵌入在 Float32 中存储需要 6KB 内存。
在 Qdrant 中,您无需为向量指定数据类型,因为它默认为 Float32。
Float16
这是一个 16 位(2 字节)浮点数。它也称为半精度浮点数。直观上,它看起来像这样
float32 -> float16 delta (float32 - float16).abs
0.79701585 -> 0.796875 delta 0.00014084578
0.7850789 -> 0.78515625 delta 0.00007736683
0.7775044 -> 0.77734375 delta 0.00016063452
0.85776305 -> 0.85791016 delta 0.00014710426
0.6616839 -> 0.6616211 delta 0.000062823296
Float16 的主要优势在于它所需的内存是 Float32 的一半,同时对向量搜索的质量几乎没有影响。
要使用 Float16,您需要在集合配置中为向量指定数据类型
PUT /collections/{collection_name}
{
"vectors": {
"size": 128,
"distance": "Cosine",
"datatype": "float16" // <-- For dense vectors
},
"sparse_vectors": {
"text": {
"index": {
"datatype": "float16" // <-- And for sparse vectors
}
}
}
}
from qdrant_client import QdrantClient, models
client = QdrantClient(url="http://localhost:6333")
client.create_collection(
collection_name="{collection_name}",
vectors_config=models.VectorParams(
size=128,
distance=models.Distance.COSINE,
datatype=models.Datatype.FLOAT16
),
sparse_vectors_config={
"text": models.SparseVectorParams(
index=models.SparseIndexParams(datatype=models.Datatype.FLOAT16)
),
},
)
import { QdrantClient } from "@qdrant/js-client-rest";
const client = new QdrantClient({ host: "localhost", port: 6333 });
client.createCollection("{collection_name}", {
vectors: {
size: 128,
distance: "Cosine",
datatype: "float16"
},
sparse_vectors: {
text: {
index: {
datatype: "float16"
}
}
}
});
use qdrant_client::qdrant::{
CreateCollectionBuilder, Datatype, Distance, SparseIndexConfigBuilder, SparseVectorParamsBuilder, SparseVectorsConfigBuilder, VectorParamsBuilder
};
use qdrant_client::Qdrant;
let client = Qdrant::from_url("http://localhost:6334").build()?;
let mut sparse_vector_config = SparseVectorsConfigBuilder::default();
sparse_vector_config.add_named_vector_params(
"text",
SparseVectorParamsBuilder::default()
.index(SparseIndexConfigBuilder::default().datatype(Datatype::Float32)),
);
let create_collection = CreateCollectionBuilder::new("{collection_name}")
.sparse_vectors_config(sparse_vector_config)
.vectors_config(
VectorParamsBuilder::new(128, Distance::Cosine).datatype(Datatype::Float16),
);
client.create_collection(create_collection).await?;
import io.qdrant.client.QdrantClient;
import io.qdrant.client.QdrantGrpcClient;
import io.qdrant.client.grpc.Collections.CreateCollection;
import io.qdrant.client.grpc.Collections.Datatype;
import io.qdrant.client.grpc.Collections.Distance;
import io.qdrant.client.grpc.Collections.SparseIndexConfig;
import io.qdrant.client.grpc.Collections.SparseVectorConfig;
import io.qdrant.client.grpc.Collections.SparseVectorParams;
import io.qdrant.client.grpc.Collections.VectorParams;
import io.qdrant.client.grpc.Collections.VectorsConfig;
QdrantClient client = new QdrantClient(QdrantGrpcClient.newBuilder("localhost", 6334, false).build());
client
.createCollectionAsync(
CreateCollection.newBuilder()
.setCollectionName("{collection_name}")
.setVectorsConfig(VectorsConfig.newBuilder()
.setParams(VectorParams.newBuilder()
.setSize(128)
.setDistance(Distance.Cosine)
.setDatatype(Datatype.Float16)
.build())
.build())
.setSparseVectorsConfig(
SparseVectorConfig.newBuilder()
.putMap("text", SparseVectorParams.newBuilder()
.setIndex(SparseIndexConfig.newBuilder()
.setDatatype(Datatype.Float16)
.build())
.build()))
.build())
.get();
using Qdrant.Client;
using Qdrant.Client.Grpc;
var client = new QdrantClient("localhost", 6334);
await client.CreateCollectionAsync(
collectionName: "{collection_name}",
vectorsConfig: new VectorParams {
Size = 128,
Distance = Distance.Cosine,
Datatype = Datatype.Float16
},
sparseVectorsConfig: (
"text",
new SparseVectorParams {
Index = new SparseIndexConfig {
Datatype = Datatype.Float16
}
}
)
);
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.CreateCollection(context.Background(), &qdrant.CreateCollection{
CollectionName: "{collection_name}",
VectorsConfig: qdrant.NewVectorsConfig(&qdrant.VectorParams{
Size: 128,
Distance: qdrant.Distance_Cosine,
Datatype: qdrant.Datatype_Float16.Enum(),
}),
SparseVectorsConfig: qdrant.NewSparseVectorsConfig(
map[string]*qdrant.SparseVectorParams{
"text": {
Index: &qdrant.SparseIndexConfig{
Datatype: qdrant.Datatype_Float16.Enum(),
},
},
}),
})
Uint8
内存优化的另一个步骤是为向量使用 Uint8 数据类型。与 Float16 不同,Uint8 不是浮点数,而是 0 到 255 范围内的整数。
并非所有嵌入模型都生成范围在 0 到 255 的向量,因此在使用 Uint8 数据类型时需要小心。
为了将浮点范围内的数字转换为 Uint8 范围,你需要应用一个称为量化的过程。
一些嵌入提供商可能会提供预量化格式的嵌入。最显著的例子之一是Cohere int8 和二进制嵌入。
对于其他嵌入,您将需要自行应用量化。
PUT /collections/{collection_name}
{
"vectors": {
"size": 128,
"distance": "Cosine",
"datatype": "uint8" // <-- For dense vectors
},
"sparse_vectors": {
"text": {
"index": {
"datatype": "uint8" // <-- For sparse vectors
}
}
}
}
from qdrant_client import QdrantClient, models
client = QdrantClient(url="http://localhost:6333")
client.create_collection(
collection_name="{collection_name}",
vectors_config=models.VectorParams(
size=128, distance=models.Distance.COSINE, datatype=models.Datatype.UINT8
),
sparse_vectors_config={
"text": models.SparseVectorParams(
index=models.SparseIndexParams(datatype=models.Datatype.UINT8)
),
},
)
import { QdrantClient } from "@qdrant/js-client-rest";
const client = new QdrantClient({ host: "localhost", port: 6333 });
client.createCollection("{collection_name}", {
vectors: {
size: 128,
distance: "Cosine",
datatype: "uint8"
},
sparse_vectors: {
text: {
index: {
datatype: "uint8"
}
}
}
});
use qdrant_client::qdrant::{
CreateCollectionBuilder, Datatype, Distance, SparseIndexConfigBuilder,
SparseVectorParamsBuilder, SparseVectorsConfigBuilder, VectorParamsBuilder,
};
use qdrant_client::Qdrant;
let client = Qdrant::from_url("http://localhost:6334").build()?;
let mut sparse_vector_config = SparseVectorsConfigBuilder::default();
sparse_vector_config.add_named_vector_params(
"text",
SparseVectorParamsBuilder::default()
.index(SparseIndexConfigBuilder::default().datatype(Datatype::Uint8)),
);
let create_collection = CreateCollectionBuilder::new("{collection_name}")
.sparse_vectors_config(sparse_vector_config)
.vectors_config(
VectorParamsBuilder::new(128, Distance::Cosine)
.datatype(Datatype::Uint8)
);
client.create_collection(create_collection).await?;
import io.qdrant.client.QdrantClient;
import io.qdrant.client.QdrantGrpcClient;
import io.qdrant.client.grpc.Collections.CreateCollection;
import io.qdrant.client.grpc.Collections.Datatype;
import io.qdrant.client.grpc.Collections.Distance;
import io.qdrant.client.grpc.Collections.SparseIndexConfig;
import io.qdrant.client.grpc.Collections.SparseVectorConfig;
import io.qdrant.client.grpc.Collections.SparseVectorParams;
import io.qdrant.client.grpc.Collections.VectorParams;
import io.qdrant.client.grpc.Collections.VectorsConfig;
QdrantClient client = new QdrantClient(QdrantGrpcClient.newBuilder("localhost", 6334, false).build());
client
.createCollectionAsync(
CreateCollection.newBuilder()
.setCollectionName("{collection_name}")
.setVectorsConfig(VectorsConfig.newBuilder()
.setParams(VectorParams.newBuilder()
.setSize(128)
.setDistance(Distance.Cosine)
.setDatatype(Datatype.Uint8)
.build())
.build())
.setSparseVectorsConfig(
SparseVectorConfig.newBuilder()
.putMap("text", SparseVectorParams.newBuilder()
.setIndex(SparseIndexConfig.newBuilder()
.setDatatype(Datatype.Uint8)
.build())
.build()))
.build())
.get();
using Qdrant.Client;
using Qdrant.Client.Grpc;
var client = new QdrantClient("localhost", 6334);
await client.CreateCollectionAsync(
collectionName: "{collection_name}",
vectorsConfig: new VectorParams {
Size = 128,
Distance = Distance.Cosine,
Datatype = Datatype.Uint8
},
sparseVectorsConfig: (
"text",
new SparseVectorParams {
Index = new SparseIndexConfig {
Datatype = Datatype.Uint8
}
}
)
);
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334,
})
client.CreateCollection(context.Background(), &qdrant.CreateCollection{
CollectionName: "{collection_name}",
VectorsConfig: qdrant.NewVectorsConfig(&qdrant.VectorParams{
Size: 128,
Distance: qdrant.Distance_Cosine,
Datatype: qdrant.Datatype_Uint8.Enum(),
}),
SparseVectorsConfig: qdrant.NewSparseVectorsConfig(
map[string]*qdrant.SparseVectorParams{
"text": {
Index: &qdrant.SparseIndexConfig{
Datatype: qdrant.Datatype_Uint8.Enum(),
},
},
}),
})
量化
除了改变原始向量的数据类型,Qdrant 还可以在原始向量旁边创建向量的量化表示。这种量化表示可用于快速选择候选向量,以便使用原始向量重新评分,甚至直接用于搜索。
量化在后台优化过程中应用。
有关量化过程的更多信息,请参阅量化部分。
向量存储
根据应用的需求,Qdrant 可以使用其中一种数据存储选项。请记住,您需要在搜索速度和占用的 RAM 大小之间进行权衡。
有关存储选项的更多信息,请参阅存储部分。