Aperçu de l'analyseur
Dans le traitement de texte, un analyseur est un composant crucial qui convertit le texte brut en un format structuré et consultable. Chaque analyseur se compose généralement de deux éléments principaux : le tokéniseur et le filtre. Ensemble, ils transforment le texte d'entrée en tokens, affinent ces tokens et les préparent pour une indexation et une recherche efficaces.
Dans Milvus, les analyseurs sont configurés lors de la création de la collection lorsque vous ajoutez des champs VARCHAR au schéma de la collection. Les jetons produits par un analyseur peuvent être utilisés pour construire un index pour la recherche par mot-clé ou convertis en encastrements épars pour la recherche en texte intégral. Pour plus d'informations, reportez-vous aux rubriques Recherche plein texte, Correspondance de phrases ou Correspondance de textes.
L'utilisation d'analyseurs peut avoir un impact sur les performances :
Recherche en texte intégral : Pour la recherche en texte intégral, les canaux DataNode et QueryNode consomment les données plus lentement car ils doivent attendre la fin de la tokenisation. Par conséquent, les données nouvellement ingérées mettent plus de temps à être disponibles pour la recherche.
Correspondance par mot-clé : Pour la correspondance par mot-clé, la création de l'index est également plus lente car la tokenisation doit être terminée avant qu'un index puisse être construit.
Anatomie d'un analyseur
Un analyseur dans Milvus se compose d'un tokenizer et de zéro ou plusieurs filtres.
Letokenizer: Le tokenizer décompose le texte d'entrée en unités discrètes appelées tokens. Ces jetons peuvent être des mots ou des phrases, selon le type de tokenizer.
Filtres: Des filtres peuvent être appliqués aux tokens pour les affiner, par exemple en les mettant en minuscules ou en supprimant les mots courants.
Les tokenizers ne prennent en charge que le format UTF-8. La prise en charge d'autres formats sera ajoutée dans les prochaines versions.
Le flux de travail ci-dessous montre comment un analyseur traite un texte.
Flux de travail du processus d'analyse
Types d'analyseurs
Milvus fournit deux types d'analyseurs pour répondre à différents besoins de traitement de texte :
Analyseur intégré: Il s'agit de configurations prédéfinies qui couvrent les tâches courantes de traitement de texte avec une configuration minimale. Les analyseurs intégrés sont idéaux pour les recherches générales, car ils ne nécessitent pas de configuration complexe.
Analyseur personnalisé: Pour des besoins plus avancés, les analyseurs personnalisés vous permettent de définir votre propre configuration en spécifiant à la fois le tokenizer et zéro ou plusieurs filtres. Ce niveau de personnalisation est particulièrement utile pour les cas d'utilisation spécialisés nécessitant un contrôle précis du traitement du texte.
- Si vous omettez les configurations de l'analyseur lors de la création de la collection, Milvus utilise par défaut l'analyseur
standardpour tous les traitements de texte. Pour plus de détails, voir Analyseur standard. - Pour des performances optimales en matière de recherche et de requête, choisissez un analyseur qui correspond à la langue de vos données textuelles. Par exemple, bien que l'analyseur
standardsoit polyvalent, il peut ne pas être le meilleur choix pour les langues ayant des structures grammaticales uniques, telles que le chinois, le japonais ou le coréen. Dans ce cas, il convient d'utiliser un analyseur spécifique à la langue, tel quechineseou des analyseurs personnalisés avec des tokenizers spécialisés (tels quelindera,icu) et des filtres est fortement recommandée pour garantir une tokenisation précise et de meilleurs résultats de recherche.
Analyseur intégré
Les analyseurs intégrés dans Milvus sont préconfigurés avec des tokenizers et des filtres spécifiques, ce qui vous permet de les utiliser immédiatement sans avoir à définir ces composants vous-même. Chaque analyseur intégré sert de modèle et comprend un tokenizer et des filtres prédéfinis, avec des paramètres facultatifs pour la personnalisation.
Par exemple, pour utiliser l'analyseur intégré standard, il suffit de spécifier son nom standard comme type et d'inclure éventuellement des configurations supplémentaires spécifiques à ce type d'analyseur, telles que stop_words:
analyzer_params = {
"type": "standard", # Uses the standard built-in analyzer
"stop_words": ["a", "an", "for"] # Defines a list of common words (stop words) to exclude from tokenization
}
Map<String, Object> analyzerParams = new HashMap<>();
analyzerParams.put("type", "standard");
analyzerParams.put("stop_words", Arrays.asList("a", "an", "for"));
const analyzer_params = {
"type": "standard", // Uses the standard built-in analyzer
"stop_words": ["a", "an", "for"] // Defines a list of common words (stop words) to exclude from tokenization
};
analyzerParams := map[string]any{"type": "standard", "stop_words": []string{"a", "an", "for"}}
export analyzerParams='{
"type": "standard",
"stop_words": ["a", "an", "for"]
}'
Pour vérifier le résultat de l'exécution d'un analyseur, utilisez la méthode run_analyzer:
# Sample text to analyze
text = "An efficient system relies on a robust analyzer to correctly process text for various applications."
# Run analyzer
result = client.run_analyzer(
text,
analyzer_params
)
import io.milvus.v2.service.vector.request.RunAnalyzerReq;
import io.milvus.v2.service.vector.response.RunAnalyzerResp;
List<String> texts = new ArrayList<>();
texts.add("An efficient system relies on a robust analyzer to correctly process text for various applications.");
RunAnalyzerResp resp = client.runAnalyzer(RunAnalyzerReq.builder()
.texts(texts)
.analyzerParams(analyzerParams)
.build());
List<RunAnalyzerResp.AnalyzerResult> results = resp.getResults();
// javascrip# Sample text to analyze
const text = "An efficient system relies on a robust analyzer to correctly process text for various applications."
// Run analyzer
const result = await client.run_analyzer({
text,
analyzer_params
});
import (
"context"
"encoding/json"
"fmt"
"github.com/milvus-io/milvus/client/v2/milvusclient"
)
bs, _ := json.Marshal(analyzerParams)
texts := []string{"An efficient system relies on a robust analyzer to correctly process text for various applications."}
option := milvusclient.NewRunAnalyzerOption(texts).
WithAnalyzerParams(string(bs))
result, err := client.RunAnalyzer(ctx, option)
if err != nil {
fmt.Println(err.Error())
// handle error
}
# restful
Le résultat sera le suivant :
['efficient', 'system', 'relies', 'on', 'robust', 'analyzer', 'to', 'correctly', 'process', 'text', 'various', 'applications']
Ceci démontre que l'analyseur tokenise correctement le texte d'entrée en filtrant les mots vides "a", "an", et "for", tout en retournant les tokens significatifs restants.
La configuration de l'analyseur intégré standard ci-dessus est équivalente à la configuration d'un analyseur personnalisé avec les paramètres suivants, où les options tokenizer et filter sont explicitement définies pour obtenir une fonctionnalité similaire :
analyzer_params = {
"tokenizer": "standard",
"filter": [
"lowercase",
{
"type": "stop",
"stop_words": ["a", "an", "for"]
}
]
}
Map<String, Object> analyzerParams = new HashMap<>();
analyzerParams.put("tokenizer", "standard");
analyzerParams.put("filter",
Arrays.asList("lowercase",
new HashMap<String, Object>() {{
put("type", "stop");
put("stop_words", Arrays.asList("a", "an", "for"));
}}));
const analyzer_params = {
"tokenizer": "standard",
"filter": [
"lowercase",
{
"type": "stop",
"stop_words": ["a", "an", "for"]
}
]
};
analyzerParams = map[string]any{"tokenizer": "standard",
"filter": []any{"lowercase", map[string]any{
"type": "stop",
"stop_words": []string{"a", "an", "for"},
}}}
export analyzerParams='{
"type": "standard",
"filter": [
"lowercase",
{
"type": "stop",
"stop_words": ["a", "an", "for"]
}
]
}'
Milvus propose les analyseurs intégrés suivants, chacun conçu pour des besoins de traitement de texte spécifiques :
standard: Adapté au traitement de texte général, appliquant la tokenisation standard et le filtrage des minuscules.english: Optimisé pour les textes en anglais, avec prise en charge des mots vides en anglais.chinese: Spécialisé dans le traitement de textes chinois, avec une tokénisation adaptée aux structures de la langue chinoise.
Analyseur personnalisé
Pour un traitement de texte plus avancé, les analyseurs personnalisés de Milvus vous permettent de construire un pipeline de traitement de texte sur mesure en spécifiant à la fois un tokenizer et des filtres. Cette configuration est idéale pour les cas d'utilisation spécialisés nécessitant un contrôle précis.
Tokenizer
Le tokenizer est un composant obligatoire pour un analyseur personnalisé, qui initie le pipeline de l'analyseur en décomposant le texte d'entrée en unités discrètes ou en tokens. La tokenisation suit des règles spécifiques, telles que la division par des espaces blancs ou la ponctuation, en fonction du type de tokenizer. Ce processus permet un traitement plus précis et indépendant de chaque mot ou phrase.
Par exemple, un tokenizer convertit le texte "Vector Database Built for Scale" en jetons distincts :
["Vector", "Database", "Built", "for", "Scale"]
Exemple de spécification d'un tokenizer:
analyzer_params = {
"tokenizer": "whitespace",
}
Map<String, Object> analyzerParams = new HashMap<>();
analyzerParams.put("tokenizer", "whitespace");
const analyzer_params = {
"tokenizer": "whitespace",
};
analyzerParams = map[string]any{"tokenizer": "whitespace"}
export analyzerParams='{
"type": "whitespace"
}'
Filtre
Lesfiltres sont des composants optionnels qui travaillent sur les jetons produits par le tokenizer, en les transformant ou en les affinant si nécessaire. Par exemple, après avoir appliqué un filtre lowercase aux termes tokenizés ["Vector", "Database", "Built", "for", "Scale"], le résultat pourrait être :
["vector", "database", "built", "for", "scale"]
Les filtres d'un analyseur personnalisé peuvent être intégrés ou personnalisés, en fonction des besoins de configuration.
Filtres intégrés: Préconfigurés par Milvus, ils ne nécessitent qu'une configuration minimale. Vous pouvez utiliser ces filtres prêts à l'emploi en spécifiant leur nom. Les filtres ci-dessous sont intégrés pour une utilisation directe :
lowercase: Convertit le texte en minuscules pour garantir une correspondance insensible à la casse. Pour plus de détails, voir Minuscules.asciifolding: Convertit les caractères non ASCII en équivalents ASCII, ce qui simplifie la gestion des textes multilingues. Pour plus d'informations, reportez-vous à la section Pliage ASCII.alphanumonly: Conserve uniquement les caractères alphanumériques en supprimant les autres. Pour plus de détails, voir Alphanumonly.cnalphanumonly: Supprime les jetons contenant des caractères autres que des caractères chinois, des lettres anglaises ou des chiffres. Pour plus de détails, voir Cnalphanumonly.cncharonly: Supprime les jetons contenant des caractères non chinois. Pour plus de détails, voir Cncharonly.
Exemple d'utilisation d'un filtre intégré :
analyzer_params = { "tokenizer": "standard", # Mandatory: Specifies tokenizer "filter": ["lowercase"], # Optional: Built-in filter that converts text to lowercase }Map<String, Object> analyzerParams = new HashMap<>(); analyzerParams.put("tokenizer", "standard"); analyzerParams.put("filter", Collections.singletonList("lowercase"));const analyzer_params = { "tokenizer": "standard", // Mandatory: Specifies tokenizer "filter": ["lowercase"], // Optional: Built-in filter that converts text to lowercase }analyzerParams = map[string]any{"tokenizer": "standard", "filter": []any{"lowercase"}}export analyzerParams='{ "type": "standard", "filter": ["lowercase"] }'Filtres personnalisés: Les filtres personnalisés permettent des configurations spécialisées. Vous pouvez définir un filtre personnalisé en choisissant un type de filtre valide (
filter.type) et en ajoutant des paramètres spécifiques pour chaque type de filtre. Exemples de types de filtres qui prennent en charge la personnalisation :stop: Supprime les mots courants spécifiés en définissant une liste de mots d'arrêt (par exemple,"stop_words": ["of", "to"]). Pour plus d'informations, reportez-vous à la section Stop.length: Exclut les tokens en fonction de critères de longueur, tels que la définition d'une longueur de token maximale. Pour plus d'informations, reportez-vous à la section Longueur.stemmer: Réduit les mots à leur forme racine pour une correspondance plus souple. Pour plus d'informations, reportez-vous à la rubrique Stemmer.
Exemple de configuration d'un filtre personnalisé :
analyzer_params = { "tokenizer": "standard", # Mandatory: Specifies tokenizer "filter": [ { "type": "stop", # Specifies 'stop' as the filter type "stop_words": ["of", "to"], # Customizes stop words for this filter type } ] }Map<String, Object> analyzerParams = new HashMap<>(); analyzerParams.put("tokenizer", "standard"); analyzerParams.put("filter", Collections.singletonList(new HashMap<String, Object>() {{ put("type", "stop"); put("stop_words", Arrays.asList("a", "an", "for")); }}));const analyzer_params = { "tokenizer": "standard", // Mandatory: Specifies tokenizer "filter": [ { "type": "stop", // Specifies 'stop' as the filter type "stop_words": ["of", "to"], // Customizes stop words for this filter type } ] };analyzerParams = map[string]any{"tokenizer": "standard", "filter": []any{map[string]any{ "type": "stop", "stop_words": []string{"of", "to"}, }}}export analyzerParams='{ "type": "standard", "filter": [ { "type": "stop", "stop_words": ["a", "an", "for"] } ] }'
Exemple d'utilisation
Dans cet exemple, vous allez créer un schéma de collection qui inclut :
Un champ vectoriel pour les embeddings.
Deux champs
VARCHARpour le traitement du texte :Un champ utilise un analyseur intégré.
L'autre utilise un analyseur personnalisé.
Avant d'incorporer ces configurations dans votre collection, vous vérifierez chaque analyseur à l'aide de la méthode run_analyzer.
Etape 1 : Initialiser MilvusClient et créer un schéma
Commencez par configurer le client Milvus et créez un nouveau schéma.
from pymilvus import MilvusClient, DataType
# Set up a Milvus client
client = MilvusClient(uri="http://localhost:19530")
# Create a new schema
schema = client.create_schema(auto_id=True, enable_dynamic_field=False)
import io.milvus.v2.client.ConnectConfig;
import io.milvus.v2.client.MilvusClientV2;
import io.milvus.v2.common.DataType;
import io.milvus.v2.common.IndexParam;
import io.milvus.v2.service.collection.request.AddFieldReq;
import io.milvus.v2.service.collection.request.CreateCollectionReq;
// Set up a Milvus client
ConnectConfig config = ConnectConfig.builder()
.uri("http://localhost:19530")
.build();
MilvusClientV2 client = new MilvusClientV2(config);
// Create schema
CreateCollectionReq.CollectionSchema schema = CreateCollectionReq.CollectionSchema.builder()
.enableDynamicField(false)
.build();
import { MilvusClient, DataType } from "@zilliz/milvus2-sdk-node";
// Set up a Milvus client
const client = new MilvusClient("http://localhost:19530");
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()
cli, err := milvusclient.New(ctx, &milvusclient.ClientConfig{
Address: "localhost:19530",
})
if err != nil {
fmt.Println(err.Error())
// handle err
}
defer client.Close(ctx)
schema := entity.NewSchema().WithAutoID(true).WithDynamicFieldEnabled(false)
# restful
Étape 2 : Définir et vérifier les configurations de l'analyseur
Configurez et vérifiez un analyseur intégré (
english):Configuration : Définir les paramètres de l'analyseur pour l'analyseur anglais intégré.
Vérification : Utilisez
run_analyzerpour vérifier que la configuration produit la tokenisation attendue.
# Built-in analyzer configuration for English text processing analyzer_params_built_in = { "type": "english" } # Verify built-in analyzer configuration sample_text = "Milvus simplifies text analysis for search." result = client.run_analyzer(sample_text, analyzer_params_built_in) print("Built-in analyzer output:", result) # Expected output: # Built-in analyzer output: ['milvus', 'simplifi', 'text', 'analysi', 'search']Map<String, Object> analyzerParamsBuiltin = new HashMap<>(); analyzerParamsBuiltin.put("type", "english"); List<String> texts = new ArrayList<>(); texts.add("Milvus simplifies text ana lysis for search."); RunAnalyzerResp resp = client.runAnalyzer(RunAnalyzerReq.builder() .texts(texts) .analyzerParams(analyzerParams) .build()); List<RunAnalyzerResp.AnalyzerResult> results = resp.getResults();// Use a built-in analyzer for VARCHAR field `title_en` const analyzerParamsBuiltIn = { type: "english", }; const sample_text = "Milvus simplifies text analysis for search."; const result = await client.run_analyzer({ text: sample_text, analyzer_params: analyzer_params_built_in });analyzerParams := map[string]any{"type": "english"} bs, _ := json.Marshal(analyzerParams) texts := []string{"Milvus simplifies text analysis for search."} option := milvusclient.NewRunAnalyzerOption(texts). WithAnalyzerParams(string(bs)) result, err := client.RunAnalyzer(ctx, option) if err != nil { fmt.Println(err.Error()) // handle error }# restfulConfigurer et vérifier un analyseur personnalisé :
Configuration : Définissez un analyseur personnalisé qui utilise un tokenizer standard avec un filtre minuscule intégré et des filtres personnalisés pour la longueur du token et les mots vides.
Vérification : Utilisez
run_analyzerpour vous assurer que la configuration personnalisée traite le texte comme prévu.
# Custom analyzer configuration with a standard tokenizer and custom filters analyzer_params_custom = { "tokenizer": "standard", "filter": [ "lowercase", # Built-in filter: convert tokens to lowercase { "type": "length", # Custom filter: restrict token length "max": 40 }, { "type": "stop", # Custom filter: remove specified stop words "stop_words": ["of", "for"] } ] } # Verify custom analyzer configuration sample_text = "Milvus provides flexible, customizable analyzers for robust text processing." result = client.run_analyzer(sample_text, analyzer_params_custom) print("Custom analyzer output:", result) # Expected output: # Custom analyzer output: ['milvus', 'provides', 'flexible', 'customizable', 'analyzers', 'robust', 'text', 'processing']// Configure a custom analyzer Map<String, Object> analyzerParams = new HashMap<>(); analyzerParams.put("tokenizer", "standard"); analyzerParams.put("filter", Arrays.asList("lowercase", new HashMap<String, Object>() {{ put("type", "length"); put("max", 40); }}, new HashMap<String, Object>() {{ put("type", "stop"); put("stop_words", Arrays.asList("of", "for")); }} ) ); List<String> texts = new ArrayList<>(); texts.add("Milvus provides flexible, customizable analyzers for robust text processing."); RunAnalyzerResp resp = client.runAnalyzer(RunAnalyzerReq.builder() .texts(texts) .analyzerParams(analyzerParams) .build()); List<RunAnalyzerResp.AnalyzerResult> results = resp.getResults();// Configure a custom analyzer for VARCHAR field `title` const analyzerParamsCustom = { tokenizer: "standard", filter: [ "lowercase", { type: "length", max: 40, }, { type: "stop", stop_words: ["of", "to"], }, ], }; const sample_text = "Milvus provides flexible, customizable analyzers for robust text processing."; const result = await client.run_analyzer({ text: sample_text, analyzer_params: analyzer_params_built_in });analyzerParams = map[string]any{"tokenizer": "standard", "filter": []any{"lowercase", map[string]any{ "type": "length", "max": 40, map[string]any{ "type": "stop", "stop_words": []string{"of", "to"}, }}} bs, _ := json.Marshal(analyzerParams) texts := []string{"Milvus provides flexible, customizable analyzers for robust text processing."} option := milvusclient.NewRunAnalyzerOption(texts). WithAnalyzerParams(string(bs)) result, err := client.RunAnalyzer(ctx, option) if err != nil { fmt.Println(err.Error()) // handle error }# curl
Étape 3 : ajouter des champs au schéma
Maintenant que vous avez vérifié les configurations de votre analyseur, ajoutez-les aux champs de votre schéma :
# Add VARCHAR field 'title_en' using the built-in analyzer configuration
schema.add_field(
field_name='title_en',
datatype=DataType.VARCHAR,
max_length=1000,
enable_analyzer=True,
analyzer_params=analyzer_params_built_in,
enable_match=True,
)
# Add VARCHAR field 'title' using the custom analyzer configuration
schema.add_field(
field_name='title',
datatype=DataType.VARCHAR,
max_length=1000,
enable_analyzer=True,
analyzer_params=analyzer_params_custom,
enable_match=True,
)
# Add a vector field for embeddings
schema.add_field(field_name="embedding", datatype=DataType.FLOAT_VECTOR, dim=3)
# Add a primary key field
schema.add_field(field_name="id", datatype=DataType.INT64, is_primary=True)
schema.addField(AddFieldReq.builder()
.fieldName("title")
.dataType(DataType.VarChar)
.maxLength(1000)
.enableAnalyzer(true)
.analyzerParams(analyzerParams)
.enableMatch(true) // must enable this if you use TextMatch
.build());
// Add vector field
schema.addField(AddFieldReq.builder()
.fieldName("embedding")
.dataType(DataType.FloatVector)
.dimension(3)
.build());
// Add primary field
schema.addField(AddFieldReq.builder()
.fieldName("id")
.dataType(DataType.Int64)
.isPrimaryKey(true)
.autoID(true)
.build());
// Create schema
const schema = {
auto_id: true,
fields: [
{
name: "id",
type: DataType.INT64,
is_primary: true,
},
{
name: "title_en",
data_type: DataType.VARCHAR,
max_length: 1000,
enable_analyzer: true,
analyzer_params: analyzerParamsBuiltIn,
enable_match: true,
},
{
name: "title",
data_type: DataType.VARCHAR,
max_length: 1000,
enable_analyzer: true,
analyzer_params: analyzerParamsCustom,
enable_match: true,
},
{
name: "embedding",
data_type: DataType.FLOAT_VECTOR,
dim: 4,
},
],
};
schema.WithField(entity.NewField().
WithName("id").
WithDataType(entity.FieldTypeInt64).
WithIsPrimaryKey(true).
WithIsAutoID(true),
).WithField(entity.NewField().
WithName("embedding").
WithDataType(entity.FieldTypeFloatVector).
WithDim(3),
).WithField(entity.NewField().
WithName("title").
WithDataType(entity.FieldTypeVarChar).
WithMaxLength(1000).
WithEnableAnalyzer(true).
WithAnalyzerParams(analyzerParams).
WithEnableMatch(true),
)
# restful
Étape 4 : Préparer les paramètres de l'index et créer la collection
# Set up index parameters for the vector field
index_params = client.prepare_index_params()
index_params.add_index(field_name="embedding", metric_type="COSINE", index_type="AUTOINDEX")
# Create the collection with the defined schema and index parameters
client.create_collection(
collection_name="my_collection",
schema=schema,
index_params=index_params
)
// Set up index params for vector field
List<IndexParam> indexes = new ArrayList<>();
indexes.add(IndexParam.builder()
.fieldName("embedding")
.indexType(IndexParam.IndexType.AUTOINDEX)
.metricType(IndexParam.MetricType.COSINE)
.build());
// Create collection with defined schema
CreateCollectionReq requestCreate = CreateCollectionReq.builder()
.collectionName("my_collection")
.collectionSchema(schema)
.indexParams(indexes)
.build();
client.createCollection(requestCreate);
// Set up index params for vector field
const indexParams = [
{
name: "embedding",
metric_type: "COSINE",
index_type: "AUTOINDEX",
},
];
// Create collection with defined schema
await client.createCollection({
collection_name: "my_collection",
schema: schema,
index_params: indexParams,
});
console.log("Collection created successfully!");
idx := index.NewAutoIndex(index.MetricType(entity.COSINE))
indexOption := milvusclient.NewCreateIndexOption("my_collection", "embedding", idx)
err = client.CreateCollection(ctx,
milvusclient.NewCreateCollectionOption("my_collection", schema).
WithIndexOptions(indexOption))
if err != nil {
fmt.Println(err.Error())
// handle error
}
# restful
Prochaines étapes
Après avoir configuré un analyseur, vous pouvez l'intégrer aux fonctions d'extraction de texte fournies par Milvus. Pour plus d'informations :