Recherche en texte intégral
La recherche en texte intégral est une fonctionnalité qui permet de récupérer des documents contenant des termes ou des expressions spécifiques dans des ensembles de données textuelles, puis de classer les résultats en fonction de leur pertinence. Cette fonctionnalité pallie les limites de la recherche sémantique, qui peut passer à côté de termes précis, vous garantissant ainsi d'obtenir les résultats les plus précis et les plus pertinents d'un point de vue contextuel. De plus, elle simplifie les recherches vectorielles en acceptant la saisie de texte brut, convertissant automatiquement vos données textuelles en représentations vectorielles clairsemées sans qu'il soit nécessaire de générer manuellement ces représentations.
Utilisant l’algorithme BM25 pour le calcul de la pertinence, cette fonctionnalité est particulièrement utile dans les scénarios de génération augmentée par la recherche (RAG), où elle donne la priorité aux documents qui correspondent étroitement à des termes de recherche spécifiques.
En intégrant la recherche en texte intégral à la recherche vectorielle dense basée sur la sémantique, vous pouvez améliorer la précision et la pertinence des résultats de recherche. Pour plus d’informations, consultez la section Recherche hybride.
Implémentation de BM25
Milvus propose une recherche en texte intégral optimisée par l’algorithme de pertinence BM25, une fonction de notation largement adoptée dans les systèmes de recherche d’informations, et l’intègre dans le flux de travail de recherche afin de fournir des résultats textuels précis et classés par pertinence.
La recherche en texte intégral dans Milvus suit le flux de travail ci-dessous :
Saisie de texte brut: vous insérez des documents textuels ou saisissez une requête en texte brut, sans qu’aucun modèle d’encodage ne soit nécessaire.
Analyse du texte: Milvus utilise un analyseur pour traiter votre texte en termes significatifs pouvant être indexés et recherchés.
Traitement par la fonction BM25: une fonction intégrée transforme ces termes en représentations vectorielles creuses optimisées pour le calcul de score BM25.
Stockage dans une collection: Milvus stocke les représentations vectorielles clairsemées obtenues dans une collection afin de permettre une récupération et un classement rapides.
Notation de pertinence BM25: au moment de la recherche, Milvus applique la fonction de notation BM25 pour calculer la pertinence des documents et renvoyer les résultats classés qui correspondent le mieux aux termes de la requête.
Recherche en texte intégral
Pour utiliser la recherche en texte intégral, suivez ces étapes principales :
Créer une collection: configurez les champs requis et définissez une fonction BM25 qui convertit le texte brut en représentations clairsemées.
Insérez les données: importez vos documents de texte brut dans la collection.
Effectuez des recherches: utilisez une requête en langage naturel pour obtenir des résultats classés en fonction de la pertinence BM25.
Créer une collection pour la recherche en texte intégral BM25
Pour activer la recherche en texte intégral basée sur BM25, vous devez préparer une collection avec les champs requis, définir une fonction BM25 pour générer des vecteurs clairsemés, configurer un index, puis créer la collection.
Définir les champs du schéma
Le schéma de votre collection doit inclure au moins trois champs obligatoires :
Champ principal: identifie de manière unique chaque entité de la collection.
Champ de type chaîne (
VARCHARouTEXT) : stocke les documents texte bruts. Vous devez définir l'enable_analyzer=Trueafin que Milvus puisse traiter le texte pour le classement par pertinence BM25. Par défaut, Milvus utilise l'standardanalyzer pour l’analyse de texte. Pour configurer un autre analyzer, consultez la section Présentation des analyzers. Les exemples de cette page utilisentVARCHAR; pour les textes longs, vous pouvez définir le champ d’entrée commeTEXTet omettremax_length. Pour un exemple complet, consultez la section Champ de texte.Champ de vecteurs clairsemés (
SPARSE_FLOAT_VECTOR) : stocke les représentations clairsemées générées automatiquement par la fonction BM25.
from pymilvus import MilvusClient, DataType, Function, FunctionType
client = MilvusClient(
uri="http://localhost:19530",
token="root:Milvus"
)
schema = client.create_schema()
schema.add_field(field_name="id", datatype=DataType.INT64, is_primary=True, auto_id=True) # Primary field
schema.add_field(field_name="text", datatype=DataType.VARCHAR, max_length=1000, enable_analyzer=True) # Text field
schema.add_field(field_name="sparse", datatype=DataType.SPARSE_FLOAT_VECTOR) # Sparse vector field; no dim required for sparse vectors
import io.milvus.v2.common.DataType;
import io.milvus.v2.service.collection.request.AddFieldReq;
import io.milvus.v2.service.collection.request.CreateCollectionReq;
CreateCollectionReq.CollectionSchema schema = CreateCollectionReq.CollectionSchema.builder()
.build();
schema.addField(AddFieldReq.builder()
.fieldName("id")
.dataType(DataType.Int64)
.isPrimaryKey(true)
.autoID(true)
.build());
schema.addField(AddFieldReq.builder()
.fieldName("text")
.dataType(DataType.VarChar)
.maxLength(1000)
.enableAnalyzer(true)
.build());
schema.addField(AddFieldReq.builder()
.fieldName("sparse")
.dataType(DataType.SparseFloatVector)
.build());
import (
"context"
"fmt"
"github.com/milvus-io/milvus/client/v2/column"
"github.com/milvus-io/milvus/client/v2/entity"
"github.com/milvus-io/milvus/client/v2/index"
"github.com/milvus-io/milvus/client/v2/milvusclient"
)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
milvusAddr := "localhost:19530"
client, err := milvusclient.New(ctx, &milvusclient.ClientConfig{
Address: milvusAddr,
})
if err != nil {
fmt.Println(err.Error())
// handle error
}
defer client.Close(ctx)
schema := entity.NewSchema()
schema.WithField(entity.NewField().
WithName("id").
WithDataType(entity.FieldTypeInt64).
WithIsPrimaryKey(true).
WithIsAutoID(true),
).WithField(entity.NewField().
WithName("text").
WithDataType(entity.FieldTypeVarChar).
WithEnableAnalyzer(true).
WithMaxLength(1000),
).WithField(entity.NewField().
WithName("sparse").
WithDataType(entity.FieldTypeSparseVector),
)
import { MilvusClient, DataType } from "@zilliz/milvus2-sdk-node";
const address = "http://localhost:19530";
const token = "root:Milvus";
const client = new MilvusClient({address, token});
const schema = [
{
name: "id",
data_type: DataType.Int64,
is_primary_key: true,
},
{
name: "text",
data_type: "VarChar",
enable_analyzer: true,
enable_match: true,
max_length: 1000,
},
{
name: "sparse",
data_type: DataType.SparseFloatVector,
},
];
console.log(res.results)
export schema='{
"autoId": true,
"enabledDynamicField": false,
"fields": [
{
"fieldName": "id",
"dataType": "Int64",
"isPrimary": true
},
{
"fieldName": "text",
"dataType": "VarChar",
"elementTypeParams": {
"max_length": 1000,
"enable_analyzer": true
}
},
{
"fieldName": "sparse",
"dataType": "SparseFloatVector"
}
]
}'
Dans la configuration ci-dessus,
id: sert de clé primaire et est généré automatiquement avecauto_id=True.text: stocke vos données textuelles brutes pour les opérations de recherche en texte intégral. Le champ peut utiliserVARCHARpour le texte court ouTEXTpour les contenus sources volumineux.sparse: un champ vectoriel réservé au stockage d’embeddings clairsemés générés en interne pour les opérations de recherche en texte intégral. Le type de données doit êtreSPARSE_FLOAT_VECTOR.
Définir la fonction BM25
La fonction BM25 convertit le texte tokenisé en vecteurs clairsemés prenant en charge le calcul de score BM25.
Définissez la fonction et ajoutez-la à votre schéma :
bm25_function = Function(
name="text_bm25_emb", # Function name
input_field_names=["text"], # Name of the VARCHAR or TEXT field containing raw text data
output_field_names=["sparse"], # Name of the SPARSE_FLOAT_VECTOR field reserved to store generated embeddings
function_type=FunctionType.BM25, # Set to `BM25`
)
schema.add_function(bm25_function)
import io.milvus.common.clientenum.FunctionType;
import io.milvus.v2.service.collection.request.CreateCollectionReq.Function;
import java.util.*;
schema.addFunction(Function.builder()
.functionType(FunctionType.BM25)
.name("text_bm25_emb")
.inputFieldNames(Collections.singletonList("text"))
.outputFieldNames(Collections.singletonList("sparse"))
.build());
function := entity.NewFunction().
WithName("text_bm25_emb").
WithInputFields("text").
WithOutputFields("sparse").
WithType(entity.FunctionTypeBM25)
schema.WithFunction(function)
const functions = [
{
name: 'text_bm25_emb',
description: 'bm25 function',
type: FunctionType.BM25,
input_field_names: ['text'],
output_field_names: ['sparse'],
params: {},
},
];
export schema='{
"autoId": true,
"enabledDynamicField": false,
"fields": [
{
"fieldName": "id",
"dataType": "Int64",
"isPrimary": true
},
{
"fieldName": "text",
"dataType": "VarChar",
"elementTypeParams": {
"max_length": 1000,
"enable_analyzer": true
}
},
{
"fieldName": "sparse",
"dataType": "SparseFloatVector"
}
],
"functions": [
{
"name": "text_bm25_emb",
"type": "BM25",
"inputFieldNames": ["text"],
"outputFieldNames": ["sparse"],
"params": {}
}
]
}'
Paramètre |
Description |
|---|---|
|
Nom de la fonction. Cette fonction convertit votre texte brut issu du champ « |
|
Nom du champ « |
|
Nom du champ dans lequel les vecteurs creux générés en interne seront stockés. Pour |
|
Le type de fonction à utiliser. Doit être |
Si plusieurs champs de texte nécessitent un traitement BM25, définissez une fonction BM25 par champ, chacune avec un nom et un champ de sortie uniques.
Configurer l’index
Après avoir défini le schéma avec les champs nécessaires et la fonction intégrée, configurez l’index de votre collection.
index_params = client.prepare_index_params()
index_params.add_index(
field_name="sparse",
index_type="SPARSE_INVERTED_INDEX",
metric_type="BM25",
params={
"inverted_index_algo": "DAAT_MAXSCORE",
"bm25_k1": 1.2,
"bm25_b": 0.75
}
)
import io.milvus.v2.common.IndexParam;
Map<String,Object> params = new HashMap<>();
params.put("inverted_index_algo", "DAAT_MAXSCORE");
params.put("bm25_k1", 1.2);
params.put("bm25_b", 0.75);
List<IndexParam> indexes = new ArrayList<>();
indexes.add(IndexParam.builder()
.fieldName("sparse")
.indexType(IndexParam.IndexType.AUTOINDEX)
.metricType(IndexParam.MetricType.BM25)
.extraParams(params)
.build());
indexOption := milvusclient.NewCreateIndexOption("my_collection", "sparse",
index.NewAutoIndex(entity.MetricType(entity.BM25)))
.WithExtraParam("inverted_index_algo", "DAAT_MAXSCORE")
.WithExtraParam("bm25_k1", 1.2)
.WithExtraParam("bm25_b", 0.75)
const index_params = [
{
field_name: "sparse",
metric_type: "BM25",
index_type: "SPARSE_INVERTED_INDEX",
params: {
"inverted_index_algo": "DAAT_MAXSCORE",
"bm25_k1": 1.2,
"bm25_b": 0.75
}
},
];
export indexParams='[
{
"fieldName": "sparse",
"metricType": "BM25",
"indexType": "AUTOINDEX",
"params":{
"inverted_index_algo": "DAAT_MAXSCORE",
"bm25_k1": 1.2,
"bm25_b": 0.75
}
}
]'
Paramètre |
Description |
|---|---|
|
Nom du champ vectoriel à indexer. Pour la recherche en texte intégral, il doit s'agir du champ qui stocke les vecteurs clairsemés générés. Dans cet exemple, définissez la valeur sur |
|
Type d’index à créer. Pour la recherche en texte intégral BM25 dans Milvus, définissez cette valeur sur |
|
La valeur de ce paramètre doit être définie sur « |
|
Un dictionnaire de paramètres supplémentaires spécifiques à l’index. |
|
L'algorithme utilisé pour la construction et l'interrogation de l'index inversé clairsemé BM25. Valeurs valides :
|
|
Contrôle la saturation de la fréquence des termes. Des valeurs plus élevées augmentent l’importance de la fréquence des termes dans le classement des documents. Plage recommandée : [1,2 ; 2,0]. Valeur par défaut : 1,2. |
|
Contrôle le degré de normalisation de la longueur des documents. On utilise généralement des valeurs comprises entre 0 et 1, la valeur par défaut étant 0,75. Une valeur de 0 signifie qu’il n’y a pas de normalisation de la longueur, tandis qu’une valeur de 1 signifie une normalisation complète de la longueur. |
Créer la collection
Créez maintenant la collection à l’aide du schéma et des paramètres d’index définis.
client.create_collection(
collection_name='my_collection',
schema=schema,
index_params=index_params
)
import io.milvus.v2.service.collection.request.CreateCollectionReq;
CreateCollectionReq requestCreate = CreateCollectionReq.builder()
.collectionName("my_collection")
.collectionSchema(schema)
.indexParams(indexes)
.build();
client.createCollection(requestCreate);
err = client.CreateCollection(ctx,
milvusclient.NewCreateCollectionOption("my_collection", schema).
WithIndexOptions(indexOption))
if err != nil {
fmt.Println(err.Error())
// handle error
}
await client.create_collection(
collection_name: 'my_collection',
schema: schema,
index_params: index_params,
functions: functions
);
export CLUSTER_ENDPOINT="http://localhost:19530"
export TOKEN="root:Milvus"
curl --request POST \
--url "${CLUSTER_ENDPOINT}/v2/vectordb/collections/create" \
--header "Authorization: Bearer ${TOKEN}" \
--header "Content-Type: application/json" \
--header "Request-Timeout: 10" \
-d "{
\"collectionName\": \"my_collection\",
\"schema\": $schema,
\"indexParams\": $indexParams
}"
Insérer des données textuelles
Une fois votre collection et votre index configurés, vous êtes prêt à insérer des données textuelles. Pour cela, il vous suffit de fournir le texte brut. La fonction intégrée que nous avons définie précédemment génère automatiquement le vecteur creux correspondant à chaque entrée de texte.
client.insert('my_collection', [
{'text': 'information retrieval is a field of study.'},
{'text': 'information retrieval focuses on finding relevant information in large datasets.'},
{'text': 'data mining and information retrieval overlap in research.'},
])
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import io.milvus.v2.service.vector.request.InsertReq;
Gson gson = new Gson();
List<JsonObject> rows = Arrays.asList(
gson.fromJson("{\"text\": \"information retrieval is a field of study.\"}", JsonObject.class),
gson.fromJson("{\"text\": \"information retrieval focuses on finding relevant information in large datasets.\"}", JsonObject.class),
gson.fromJson("{\"text\": \"data mining and information retrieval overlap in research.\"}", JsonObject.class)
);
client.insert(InsertReq.builder()
.collectionName("my_collection")
.data(rows)
.build());
// go
await client.insert({
collection_name: 'my_collection',
data: [
{'text': 'information retrieval is a field of study.'},
{'text': 'information retrieval focuses on finding relevant information in large datasets.'},
{'text': 'data mining and information retrieval overlap in research.'},
]);
curl --request POST \
--url "${CLUSTER_ENDPOINT}/v2/vectordb/entities/insert" \
--header "Authorization: Bearer ${TOKEN}" \
--header "Content-Type: application/json" \
--header "Request-Timeout: 10" \
-d '{
"data": [
{"text": "information retrieval is a field of study."},
{"text": "information retrieval focuses on finding relevant information in large datasets."},
{"text": "data mining and information retrieval overlap in research."}
],
"collectionName": "my_collection"
}'
Effectuer une recherche en texte intégral
Une fois les données insérées dans votre collection, vous pouvez effectuer des recherches en texte intégral à l’aide de requêtes en texte brut. Milvus convertit automatiquement votre requête en vecteur creux et classe les résultats de recherche correspondants à l’aide de l’algorithme BM25, puis renvoie les topK (limit) résultats.
Vous pouvez mettre en évidence les termes correspondants dans les résultats de recherche en configurant un surligneur de texte. Consultez la section « Surligneur de texte » pour plus de détails.
res = client.search(
collection_name='my_collection',
data=['whats the focus of information retrieval?'],
anns_field='sparse',
output_fields=['text'], # Fields to return in search results; sparse field cannot be output
limit=3,
)
print(res)
import io.milvus.v2.service.vector.request.SearchReq;
import io.milvus.v2.service.vector.request.data.EmbeddedText;
import io.milvus.v2.service.vector.response.SearchResp;
Map<String,Object> searchParams = new HashMap<>();
SearchResp searchResp = client.search(SearchReq.builder()
.collectionName("my_collection")
.data(Collections.singletonList(new EmbeddedText("whats the focus of information retrieval?")))
.annsField("sparse")
.topK(3)
.searchParams(searchParams)
.outputFields(Collections.singletonList("text"))
.build());
annSearchParams := index.NewCustomAnnParam()
resultSets, err := client.Search(ctx, milvusclient.NewSearchOption(
"my_collection", // collectionName
3, // limit
[]entity.Vector{entity.Text("whats the focus of information retrieval?")},
).WithConsistencyLevel(entity.ClStrong).
WithANNSField("sparse").
WithAnnParam(annSearchParams).
WithOutputFields("text"))
if err != nil {
fmt.Println(err.Error())
// handle error
}
for _, resultSet := range resultSets {
fmt.Println("IDs: ", resultSet.IDs.FieldData().GetScalars())
fmt.Println("Scores: ", resultSet.Scores)
fmt.Println("text: ", resultSet.GetColumn("text").FieldData().GetScalars())
}
await client.search(
collection_name: 'my_collection',
data: ['whats the focus of information retrieval?'],
anns_field: 'sparse',
output_fields: ['text'],
limit: 3,
)
curl --request POST \
--url "${CLUSTER_ENDPOINT}/v2/vectordb/entities/search" \
--header "Authorization: Bearer ${TOKEN}" \
--header "Content-Type: application/json" \
--header "Request-Timeout: 10" \
--data-raw '{
"collectionName": "my_collection",
"data": [
"whats the focus of information retrieval?"
],
"annsField": "sparse",
"limit": 3,
"outputFields": [
"text"
],
"searchParams":{
"params":{}
}
}'
Paramètre |
Description |
|---|---|
|
Dictionnaire contenant les paramètres de recherche. |
|
Proportion de termes de faible importance à ignorer lors de la recherche. La valeur doit être comprise dans l'intervalle [0,0 ; 1,0). Pour plus de détails, consultez la section « Vecteur clairsemé ». |
|
Texte brut de la requête en langage naturel. Milvus convertit automatiquement votre requête textuelle en vecteurs clairsemés à l’aide de la fonction BM25 — ne fournissez pas de vecteurs précalculés. |
|
Nom du champ contenant les vecteurs clairsemés générés en interne. |
|
Liste des noms de champs à renvoyer dans les résultats de recherche. Prend en charge tous les champs à l'exception du champ de vecteurs clairsemés contenant les représentations générées par BM25. Les champs de sortie courants incluent le champ de clé primaire (par exemple, |
|
Nombre maximal de résultats les plus pertinents à renvoyer. |
FAQ
Puis-je générer ou accéder aux vecteurs clairsemés générés par la fonction BM25 dans la recherche en texte intégral ?
Non, les vecteurs creux générés par la fonction BM25 ne sont ni directement accessibles ni exportables dans la recherche en texte intégral. Voici les détails :
La fonction BM25 génère en interne des vecteurs creux à des fins de classement et de recherche
Ces vecteurs sont stockés dans le champ « sparse », mais ne peuvent pas être inclus dans
output_fieldsVous ne pouvez exporter que les champs de texte d'origine et les métadonnées (telles que
id,text)
Exemple :
# ❌ This throws an error - you cannot output the sparse field
client.search(
collection_name='my_collection',
data=['query text'],
anns_field='sparse',
output_fields=['text', 'sparse'] # 'sparse' causes an error
limit=3,
search_params=search_params
)
# ✅ This works - output text fields only
client.search(
collection_name='my_collection',
data=['query text'],
anns_field='sparse',
output_fields=['text']
limit=3,
search_params=search_params
)
Pourquoi dois-je définir un champ de vecteur clairsemé si je ne peux pas y accéder ?
Le champ de vecteurs creux sert d’index de recherche interne, à l’instar des index de base de données avec lesquels les utilisateurs n’interagissent pas directement.
Justification de la conception:
Séparation des préoccupations : vous travaillez avec du texte (entrée/sortie), Milvus gère les vecteurs (traitement interne)
Performances : les vecteurs creux précalculés permettent un classement BM25 rapide lors des requêtes
Expérience utilisateur : les opérations vectorielles complexes sont masquées derrière une interface textuelle simple
Si vous avez besoin d'accéder aux vecteurs:
Utilisez des opérations manuelles sur les vecteurs creux plutôt que la recherche en texte intégral
Créez des collections distinctes pour les workflows personnalisés impliquant des vecteurs creux
Pour plus de détails, consultez la section « Vecteurs creux ».