SpringBoot は Milvus を統合します| (ベクトル ストレージとクエリを実装)
第 1章の
リンク: SpringBoot は Milvus を統合します| (ベクトル ストレージとクエリを実装します)
序文
インターネットの継続的な発展に伴い、電子メール、論文、IoT センサー データ、ソーシャル メディアの写真、タンパク質の分子構造などの非構造化データがますます一般的になりました。コンピューターを使用してこれらのデータを処理する場合は、埋め込みテクノロジーを使用してこれらのデータをベクトルに変換する必要があります。次に、Milvus はこれらのベクトルを保存し、インデックスを付けます。Milvus は、2 つのベクトル間の距離に応じて、2 つのベクトルの相関関係を分析できます。2 つのベクトルが非常に似ている場合、ベクトルによって表されるソース データも非常に似ていることを意味します。
1. ミルバスの紹介
Milvus は、高可用性、高性能、簡単な拡張を備えたクラウドネイティブのベクトル データベースであり、大量のベクトル データをリアルタイムに呼び出すために使用されます。
Milvus は FAISS、Annoy、HNSW およびその他のベクトル検索ライブラリに基づいて構築されており、その中心は高密度ベクトル類似性検索の問題を解決することです。ベクトル検索ライブラリに基づいて、Milvus はデータ分割、データ永続化、増分データ取り込み、スカラー ベクトル ハイブリッド クエリ、タイム トラベルなどの機能をサポートし、ベクトル検索シナリオのアプリケーション要件を満たすためにベクトル検索のパフォーマンスを大幅に最適化します。 。一般に、最高の可用性と回復力を得るために、Kubernetes を使用して Milvus をデプロイすることをお勧めします。
2. Milvus データベースのインストール
1. Milvusのインストール環境準備(centos7)
1. サーバーは docker と docker-compose をデプロイする必要があります
2. 新しい作業ディレクトリを作成します
3. Milvus のインストールと起動用の YAML ファイルを取得します
4. ファイル wget https://github.com/milvus-io/milvus/ を取得しますreleases/download/v2. 1.4/milvus-standalone-docker-compose.yml -O docker-compose.yml
5. docker イメージを起動します sudo docker-compose up -d
6. 起動後のコンテナのステータス
7. Milvus コンテナを停止します
sudo docker-compose ダウン
2. Milvus クライアントのインストール
1. Docker ウェアハウスで、対応するバージョンの Milvus のクライアント イメージを見つけます。
2. イメージをプルします (docker pull zilliz/attu:v2.1.0)
3. attu
docker run -itd --restart=always -p 13000:3000 - を実行します。 e HOST_URL =http://{ip}:13000 -e MILVUS_URL={ip}:19530 zilliz/attu:v2.1.0
ip は展開 IP です
。 4. ブラウザのログイン アクセス: http://{ip}::13000
3. Attu が新しい Milvus コレクションを作成
新しいコレクションの名前は content3 で、フィールド ID は主キー、content はコンテンツに対応するテキスト ID を格納し、content_intro はコンテンツ ベクトルを格納し、新しいパターン content3_partion は新しく作成されたパーティション
です
。
3. ミルバスの統合
1. 依存関係の導入
JavaでMilvusを操作するためのコンポーネントツール
<!-- milvus向量数据库 -->
<dependency>
<groupId>io.milvus</groupId>
<artifactId>milvus-sdk-java</artifactId>
<version>2.2.4</version>
<exclusions>
<exclusion>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
</exclusion>
</exclusions>
</dependency>
2. クライアントの初期化
クライアント使用インスタンスを初期化してコンテナに配置します
@Configuration
public class MilvusConfig {
@Value("${milvus.host}")
private String host;
@Value("${milvus.port}")
private Integer port;
@Bean
public MilvusServiceClient milvusServiceClient() {
return new MilvusServiceClient(
ConnectParam.newBuilder()
.withHost(host)
.withPort(port)
.build());
}
}
3. コード作成例集
コード初期化コレクション構造体
public class PushMaterielsConst {
/**
* 集合名称(库名)
*/
public static final String COLLECTION_NAME = "content3";
public static final String PARTITION_NAME = "content3_partion";
/**
* 分片数量
*/
public static final Integer SHARDS_NUM = 1;
/**
* 分区数量
*/
public static final Integer PARTITION_NUM = 1;
/**
* 分区前缀
*/
public static final String PARTITION_PREFIX = "shards_";
/**
* 向量值长度
*/
public static final Integer FEATURE_DIM = 256;
public static final Boolean TRUE = true;
/**
* 字段
*/
public static class Field {
/**
* 主键idID
*/
public static final String ID = "id";
/**
* 文本id
*/
public static final String CONTENT = "content";
/**
* 向量值
*/
public static final String CONTENT_INTRO = "content_intro";
}
}
バインディングコードを作成する
public void creatCollection() {
FieldType fieldType1 = FieldType.newBuilder()
.withName(PushMaterielsConst.Field.ID)
.withDataType(DataType.Int64)
.withPrimaryKey(true)
.withAutoID(false)
.build();
FieldType fieldType2 = FieldType.newBuilder()
.withName(PushMaterielsConst.Field.CONTENT)
.withDataType(DataType.VarChar)
.withMaxLength(255)
.build();
FieldType fieldType3 = FieldType.newBuilder()
.withName(PushMaterielsConst.Field.CONTENT_INTRO)
.withDataType(DataType.FloatVector)
.withDimension(2)
.build();
CreateCollectionParam createCollectionReq = CreateCollectionParam.newBuilder()
.withCollectionName(PushMaterielsConst.COLLECTION_NAME)
.withDescription("user content search")
.withShardsNum(2)
.addFieldType(fieldType1)
.addFieldType(fieldType2)
.addFieldType(fieldType3)
.build();
MilvusServiceClient milvusClient = getClient();
R<RpcStatus> response = milvusClient.createCollection(createCollectionReq);
log.info(PushMaterielsConst.COLLECTION_NAME + "是否成功创建集合——>>" + response.getStatus());
}
データコードを挿入する
/**
* 插入数据
*
* @param milvusParamVoList
* @return
*/
public void insertPrepare(List<MilvusParamBo> milvusParamVoList) {
List<String> content_array = new ArrayList<>();
List<List<Float>> vector_array = new ArrayList<>();
for (MilvusParamBo vo : milvusParamVoList) {
content_array.add(vo.getContentId());
vector_array.add(vo.getVector());
}
List<InsertParam.Field> fields = new ArrayList<>();
fields.add(new InsertParam.Field(PushMaterielsConst.Field.CONTENT, content_array));
fields.add(new InsertParam.Field(PushMaterielsConst.Field.CONTENT_INTRO, vector_array));
InsertParam insertParam = InsertParam.newBuilder()
.withCollectionName(PushMaterielsConst.COLLECTION_NAME)
.withPartitionName(PushMaterielsConst.PARTITION_NAME)
.withFields(fields)
.build();
R<MutationResult> insert = milvusClient.insert(insertParam);
Integer status = insert.getStatus();
System.out.println("数据插入状态:" + status);
}
クエリデータコード
public List<String> searchTallestSimilarity(List<Float> arcsoftToFloat) {
log.info("----Milvus数据加载到内存------");
loadCollection();
List<List<Float>> list = new ArrayList<>();
list.add(arcsoftToFloat);
SearchParam searchParam = SearchParam.newBuilder()
//集合名称
.withCollectionName(PushMaterielsConst.COLLECTION_NAME)
.withPartitionNames(Arrays.asList(PushMaterielsConst.PARTITION_NAME))
//计算方式
// 欧氏距离 (L2)
// 内积 (IP)
.withMetricType(MetricType.L2)
.withOutFields(Arrays.asList(PushMaterielsConst.Field.CONTENT))
//返回多少条结果
.withTopK(5)
//搜索的向量值
.withVectors(list)
//搜索的Field
.withVectorFieldName(PushMaterielsConst.Field.CONTENT_INTRO)
.withParams(SEARCH_PARAM)
.build();
log.info("----Milvus向量搜索------");
R<SearchResults> search = milvusClient.search(searchParam);
log.info("----搜索数据处理------");
if (search.getData() == null) return null;
List<String> returnList = Lists.newArrayList();
SearchResultsWrapper wrapper = new SearchResultsWrapper(search.getData().getResults());
for (int i = 0; i < list.size(); i++) {
List<?> fieldData = wrapper.getFieldData(PushMaterielsConst.Field.CONTENT, i);
for (int j = 0; j < fieldData.size(); j++) {
returnList.add((String) fieldData.get(j));
}
}
return returnList;
}
public void loadCollection() {
log.info("----Milvus加载到内存------");
R<RpcStatus> response = milvusClient.loadPartitions(
LoadPartitionsParam
.newBuilder()
//集合名称
.withCollectionName(PushMaterielsConst.COLLECTION_NAME)
//需要加载的分区名称
.withPartitionNames(Lists.newArrayList(PushMaterielsConst.PARTITION_NAME))
.build());
log.info("Milvus数据加载到内存状态:{}", response.getStatus());
}
複数の条件に従ってデータをクエリする
public List<MilvesSearchDate> searchTallestSimilarityWithExpr(List<Float> arcsoftToFloat, ContentParamVo paramVo,
String collectionName, String partionName) {
Integer top = paramVo.getTop();
if (top == null || top < 1) {
top = 5;
}
loadCollection(collectionName, partionName);
// 查询参数封装
//1、向量查询参数
List<List<Float>> list = new ArrayList<>();
list.add(arcsoftToFloat);
SearchParam.Builder builder = SearchParam.newBuilder()
//集合名称
.withCollectionName(collectionName)
.withPartitionNames(Arrays.asList(partionName))
//计算方式
// 欧氏距离 (L2)
// 内积 (IP)
.withMetricType(MetricType.L2)
.withOutFields(Arrays.asList(MilvusFileId.PARTID))
//返回多少条结果
.withTopK(top)
//搜索的向量值
.withVectors(list)
//搜索的Field
.withVectorFieldName(MilvusFileId.EMBEDDING)
.withParams(SEARCH_PARAM);
// .withExpr();
//2、类别查询参数
List<String> categorys = paramVo.getCategoryId();
if (categorys != null && categorys.size() > 0) {
String categoryExpr = StringUtils.join(MilvusFileId.CATEGORY, " in [", listToString(categorys), "]");
log.info("过滤条件:{}", categoryExpr);
builder.withExpr(categoryExpr);
}
//3、文章id查询参数
List<String> docs = paramVo.getDocId();
if (docs != null && docs.size() > 0) {
String docExpr = StringUtils.join(MilvusFileId.DOCID, " in [", listToString(docs), "]");
log.info("过滤条件:{}", docExpr);
builder.withExpr(docExpr);
}
log.info("----Milvus向量搜索------");
R<SearchResults> search = milvusClient.search(builder.build());
log.info("----搜索数据处理------");
if (search.getData() == null) {
log.error("搜索数据返回为空");
return null;
}
List<MilvesSearchDate> returnList = Lists.newArrayList();
SearchResultsWrapper wrapper = new SearchResultsWrapper(search.getData().getResults());
try {
for (int i = 0; i < list.size(); i++) {
List<SearchResultsWrapper.IDScore> scores = wrapper.getIDScore(i);
List<?> fieldData = wrapper.getFieldData(MilvusFileId.PARTID, i);
for (int j = 0; j < fieldData.size(); j++) {
returnList.add(MilvesSearchDate.builder()
.partId((String) fieldData.get(j))
.score(scores.get(j).getScore())
.build());
}
}
} catch (Exception e) {
log.error("解析向量匹配数据异常:{}", e.getMessage());
}
return returnList;
}
要約する
上記は Springboot 統合 Milvus データベースの実装です。クエリを実行するときに問題があります。クエリ実行者が最初の 5 個のデータを書き込み、実際には 3 個のデータしか見つからなかった場合、残りの 2 日はランダムに与えられます (与えられたデータはクエリ テキストとは無関係です。)