Ferramenta de consulta de similaridade
1. Fundo
Caso queira implementar uma função de busca por similaridade de vetores de características, o projeto é desenvolvido em Java e o banco de dados é PostgreSQL. As opções disponíveis são:
- Banco de dados vetorial - Milvus é fácil de implantar, com interface visual Attu e JavaSDK (mas requer implantação especial).
- Plug-in PostgreSQL (Cube suporta 100 dimensões, Pase suporta 512 dimensões e Vector suporta 16.000 dimensões).
Como o vetor de características da imagem extraída tem 1024 dimensões, apenas o Milvus e o plug-in Vector do PostgreSQL podem ser usados.
2.Aplicação
2.1 pipa
O site oficial do Milvus tem um processo de instalação detalhado e um código que não entrarei em detalhes aqui. Use o Docker para instalá-lo. A versão é. 2.2.9
Aqui fornecemos uma classe de ferramenta simples. Os parâmetros de conexão do banco de dados não são parametrizados. Os amigos podem otimizar e simplificar os dados do resultado. Formatação:
Encapsulamento de resultado:
@Data
@Builder
public class MilvusRes {
public float score;
public String imagePath;
}
Ferramentas:
@Slf4j
@Component
public class MilvusUtil {
public MilvusServiceClient milvusServiceClient;
@PostConstruct
private void connectToServer() {
milvusServiceClient = new MilvusServiceClient(
ConnectParam.newBuilder()
.withHost("your service host")
.withPort(19530)
.build());
// 加载数据
LoadCollectionParam faceSearchNewLoad = LoadCollectionParam.newBuilder().withCollectionName("CollectionName").build();
R<RpcStatus> rpcStatusR = milvusServiceClient.loadCollection(faceSearchNewLoad);
log.info("Milvus LoadCollection [{}]", rpcStatusR.getStatus());
}
public int insertDataToMilvus(String id, String path, float[] feature) {
List<InsertParam.Field> fields = new ArrayList<>();
List<Float> featureList = new ArrayList<>(feature.length);
for (float v : feature) {
featureList.add(v);
}
fields.add(new InsertParam.Field("field1", Collections.singletonList(id)));
fields.add(new InsertParam.Field("field2", Collections.singletonList(path)));
fields.add(new InsertParam.Field("field3", Collections.singletonList(featureList)));
InsertParam insertParam = InsertParam.newBuilder()
.withCollectionName("CollectionName")
//.withPartitionName("novel")
.withFields(fields)
.build();
R<MutationResult> insert = milvusServiceClient.insert(insertParam);
return insert.getStatus();
}
public List<MilvusRes> searchImageByFeature(float[] feature) {
List<Float> featureList = new ArrayList<>(feature.length);
for (float v : feature) {
featureList.add(v);
}
List<String> queryOutputFields = Arrays.asList("field");
SearchParam faceSearch = SearchParam.newBuilder()
.withCollectionName("CollectionName")
.withMetricType(MetricType.IP)
.withVectorFieldName("VectorFieldName")
.withVectors(Collections.singletonList(featureList))
.withOutFields(queryOutputFields)
.withTopK(10).build();
// 执行搜索
long l = System.currentTimeMillis();
R<SearchResults> respSearch = milvusServiceClient.search(faceSearch);
log.info("MilvusServiceClient.search cost [{}]", System.currentTimeMillis() - l);
// 解析结果数据
SearchResultData results = respSearch.getData().getResults();
int scoresCount = results.getScoresCount();
SearchResultsWrapper wrapperSearch = new SearchResultsWrapper(results);
List<MilvusRes> milvusResList = new ArrayList<>();
for (int i = 0; i < scoresCount; i++) {
float score = wrapperSearch.getIDScore(0).get(i).getScore();
Object imagePath = wrapperSearch.getFieldData("field1", 0).get(i);
MilvusRes milvusRes = MilvusRes.builder().score(score).imagePath(imagePath.toString()).build();
milvusResList.add(milvusRes);
}
return milvusResList;
}
}
A quantidade é conforme mostrado na imagem:
Os resultados do teste de desempenho são os seguintes:
MilvusServiceClient.search cost [24]
2.2 Vetor
As informações básicas são explicadas nos sites a seguir, por isso não entrarei em detalhes aqui.
O banco de dados PostgreSQL é implantado usando Docker e a versão é 12.12. O processo de instalação do plug-in é o seguinte:
# 进入容器
docker exec -it CONTAINER ID /bin/bash
# 1.更新 apt-get
apt-get update
# 未更新直接安装会报错
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
E: Unable to locate package postgresql-12-postgis-3
E: Unable to locate package postgresql-12-postgis-3-dbgsym
E: Unable to locate package postgresql-12-postgis-3-scripts
# 2.安装插件
apt-get install postgresql-12-pgvector
Operações de banco de dados:
-- 添加 vector 扩展
CREATE EXTENSION vector;
-- 查询可使用的扩展
SELECT * FROM pg_available_extensions;
-- 创建表
CREATE TABLE "public"."test" (
"field1" VARCHAR ( 64 ),
"field2" VARCHAR ( 128 ),
"field3" vector ( 1024 ),
CONSTRAINT "test_pkey" PRIMARY KEY ( "field1" )
);
Ao criar um índice, siga o algoritmo utilizado:
-- 创建索引
CREATE INDEX ON test USING ivfflat ( field3);
CREATE INDEX ON test USING ivfflat ( field3 vector_ip_ops) WITH (lists = 50);
CREATE INDEX ON test USING ivfflat ( field3 vector_ip_ops) WITH (lists = 500);
CREATE INDEX ON test USING ivfflat ( field3 vector_ip_ops) WITH (lists = 1024);
Aqui está um método para escrever SQL em um arquivo mapeador [consultar as dez principais semelhanças]:
<select id="queryId" resultType="map">
SELECT
field1,
field2,
field3 <![CDATA[ <#> ]]> CAST ( #{featrue} AS vector ) AS "score"
FROM test
ORDER BY field1 <![CDATA[ <#> ]]> CAST ( #{featrue} AS vector )
LIMIT 10;
</select>
Descrição do símbolo:
- Distância L2 (<->): A distância L2, também conhecida como distância euclidiana ou distância euclidiana, é usada para medir a distância em linha reta entre dois vetores. A distância L2 é calculada somando os quadrados das diferenças entre os elementos correspondentes de dois vetores e, em seguida, extraindo a raiz quadrada. Uma distância L2 menor indica que os vetores estão mais próximos uns dos outros.
- Produto interno (<#>): A distância do produto interno, também conhecida como distância do cosseno ou similaridade do produto interno, é usada para medir o valor do cosseno do ângulo entre dois vetores. A distância do produto interno é calculada como o produto escalar de dois vetores dividido pelo produto das normas dos dois vetores. Quanto maior for a distância do produto interno, menor será o ângulo entre os vetores e maior será a similaridade.
- Distância do cosseno (<=>): Distância do cosseno, também conhecida como complemento da similaridade do cosseno. A distância cosseno é um índice de distância que mede o ângulo entre dois vetores. O valor varia de 0 a 2, onde 0 significa completamente semelhante e 2 significa completamente diferente. A distância do cosseno é calculada como o produto escalar de dois vetores dividido pelo complemento do produto das normas dos dois vetores.
O teste de desempenho é o seguinte:
PostgreSQL.vector.search cost [30]
3. Resumo
Cada um tem suas próprias vantagens: Milvus não precisa reconstruir o índice e a velocidade de consulta é mais rápida; Vector não requer implantação especial e é fácil de manter.