Обзор анализаторов
В обработке текстов анализатор - это важнейший компонент, преобразующий необработанный текст в структурированный, доступный для поиска формат. Каждый анализатор обычно состоит из двух основных элементов: токенизатора и фильтра. Вместе они преобразуют входной текст в лексемы, уточняют эти лексемы и готовят их к эффективному индексированию и поиску.
В Milvus анализаторы настраиваются во время создания коллекции, когда вы добавляете поля VARCHAR в схему коллекции. Токены, созданные анализатором, могут быть использованы для построения индекса для поиска по ключевым словам или преобразованы в разреженные вкрапления для полнотекстового поиска. Дополнительные сведения см. в разделе Полнотекстовый поиск, поиск по фразе или поиск по тексту.
Использование анализаторов может повлиять на производительность:
Полнотекстовый поиск: При полнотекстовом поиске каналы DataNode и QueryNode потребляют данные медленнее, поскольку им приходится ждать завершения токенизации. В результате только что поступившие данные становятся доступными для поиска дольше.
Поиск по ключевым словам: Для поиска по ключевым словам создание индекса также происходит медленнее, поскольку перед созданием индекса необходимо завершить токенизацию.
Анатомия анализатора
Анализатор в Milvus состоит ровно из одного токенизатора и нуля или более фильтров.
Токенизатор: Токенизатор разбивает входной текст на дискретные единицы, называемые лексемами. Эти лексемы могут быть словами или фразами, в зависимости от типа токенизатора.
Фильтры: Фильтры могут применяться к лексемам для их дальнейшего уточнения, например, для перевода их в нижний регистр или удаления общих слов.
Токенизаторы поддерживают только формат UTF-8. Поддержка других форматов будет добавлена в будущих релизах.
Приведенный ниже рабочий процесс показывает, как анализатор обрабатывает текст.
Рабочий процесс анализатора
Типы анализаторов
Milvus предоставляет два типа анализаторов для удовлетворения различных потребностей в обработке текста:
Встроенный анализатор: Это предопределенные конфигурации, которые решают общие задачи обработки текста с минимальной настройкой. Встроенные анализаторы идеально подходят для поиска общего назначения, поскольку не требуют сложной настройки.
Пользовательский анализатор: Для более сложных задач пользовательские анализаторы позволяют задать собственную конфигурацию, указав как токенизатор, так и ноль или более фильтров. Такой уровень настройки особенно полезен в специализированных случаях, когда требуется точный контроль над обработкой текста.
- Если вы не указываете конфигурацию анализатора при создании коллекции, Milvus по умолчанию использует анализатор
standardдля обработки всего текста. Подробности см. в разделе Стандартный анализатор. - Для оптимальной производительности поиска и запросов выбирайте анализатор, соответствующий языку ваших текстовых данных. Например, несмотря на универсальность анализатора
standard, он может оказаться не лучшим выбором для языков с уникальными грамматическими структурами, таких как китайский, японский или корейский. В таких случаях лучше использовать анализатор для конкретного языка, напримерchineseили пользовательских анализаторов со специализированными токенизаторами (такими какlindera,icu) и фильтрами, чтобы обеспечить точную токенизацию и лучшие результаты поиска.
Встроенный анализатор
Встроенные анализаторы в Milvus предварительно сконфигурированы с определенными токенизаторами и фильтрами, что позволяет использовать их сразу, без необходимости определять эти компоненты самостоятельно. Каждый встроенный анализатор представляет собой шаблон, включающий в себя предустановленные токенизаторы и фильтры, а также дополнительные параметры для настройки.
Например, чтобы использовать встроенный анализатор standard, достаточно указать его имя standard в качестве type и опционально включить дополнительные конфигурации, специфичные для этого типа анализатора, например 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"]
}'
Чтобы проверить результат выполнения анализатора, используйте метод 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
На выходе мы получим следующее:
['efficient', 'system', 'relies', 'on', 'robust', 'analyzer', 'to', 'correctly', 'process', 'text', 'various', 'applications']
Это свидетельствует о том, что анализатор правильно выполняет токенизацию входного текста, отфильтровывая стоп-слова "a", "an", и "for", и возвращая оставшиеся осмысленные лексемы.
Конфигурация встроенного анализатора standard, приведенная выше, эквивалентна настройке пользовательского анализатора со следующими параметрами, где опции tokenizer и filter явно определены для достижения аналогичной функциональности:
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 предлагает следующие встроенные анализаторы, каждый из которых предназначен для конкретных задач обработки текста:
standard: : Подходит для обработки текста общего назначения, применяя стандартную токенизацию и фильтрацию строчных букв.english: Оптимизирован для англоязычных текстов, с поддержкой английских стоп-слов.chinese: Специализирован для обработки китайского текста, включая токенизацию, адаптированную к структурам китайского языка.
Пользовательский анализатор
Для более сложной обработки текста пользовательские анализаторы в Milvus позволяют создать индивидуальный конвейер обработки текста, указав токенизатор и фильтры. Такая настройка идеально подходит для специализированных случаев, когда требуется точный контроль.
Токенизатор
Токенизатор - это обязательный компонент пользовательского анализатора, который запускает конвейер анализатора, разбивая входной текст на дискретные единицы или токены. В зависимости от типа токенизатора токенизация выполняется по определенным правилам, таким как разбиение на пробельные символы или знаки препинания. Этот процесс позволяет более точно и независимо обрабатывать каждое слово или фразу.
Например, токенизатор преобразует текст "Vector Database Built for Scale" в отдельные лексемы:
["Vector", "Database", "Built", "for", "Scale"]
Пример указания токенизатора:
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"
}'
Фильтр
Фильтры - это необязательные компоненты, работающие с токенами, полученными токенизатором, преобразуя или уточняя их по мере необходимости. Например, после применения фильтра lowercase к токенизированным терминам ["Vector", "Database", "Built", "for", "Scale"], результат может быть следующим:
["vector", "database", "built", "for", "scale"]
Фильтры в пользовательском анализаторе могут быть как встроенными, так и пользовательскими, в зависимости от потребностей конфигурации.
Встроенные фильтры: Предварительно сконфигурированы Milvus и требуют минимальной настройки. Вы можете использовать эти фильтры из коробки, указав их имена. Приведенные ниже фильтры являются встроенными для прямого использования:
lowercase: Преобразует текст в нижний регистр, обеспечивая сопоставление без учета регистра. Подробнее см. в разделе Нижний регистр.asciifolding: Преобразует не ASCII-символы в ASCII-эквиваленты, упрощая работу с многоязычным текстом. Подробнее см. в разделе Сложение ASCII.alphanumonly: Сохраняет только алфавитно-цифровые символы, удаляя остальные. Подробнее см. в разделе "Только алфавитно-цифровые".cnalphanumonly: Удаляет лексемы, содержащие любые символы, кроме китайских, английских букв или цифр. Подробнее см. в разделе Cnalphanumonly.cncharonly: Удаляет маркеры, содержащие любые некитайские символы. Подробнее см. в разделе Cncharonly.
Пример использования встроенного фильтра:
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"] }'Пользовательские фильтры: Пользовательские фильтры позволяют создавать специализированные конфигурации. Вы можете определить пользовательский фильтр, выбрав подходящий тип фильтра (
filter.type) и добавив специальные настройки для каждого типа фильтра. Примеры типов фильтров, поддерживающих настройку:stop: Удаляет указанные общие слова, задавая список стоп-слов (например,"stop_words": ["of", "to"]). Подробнее см. в разделе "Стоп-слова".length: Исключает лексемы на основе критериев длины, например, задавая максимальную длину лексемы. Подробнее см. в разделе Длина.stemmer: Сокращает слова до их корневых форм для более гибкого подбора. Подробнее см. в разделе Стеммер.
Пример настройки пользовательского фильтра:
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"] } ] }'
Пример использования
В этом примере вы создадите схему коллекции, которая будет включать:
Векторное поле для вкраплений.
Два поля
VARCHARдля обработки текста:Одно поле использует встроенный анализатор.
Другое использует пользовательский анализатор.
Перед включением этих конфигураций в коллекцию вы проверите каждый анализатор с помощью метода run_analyzer.
Шаг 1: Инициализация MilvusClient и создание схемы
Начните с настройки клиента Milvus и создания новой схемы.
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
Шаг 2: Определите и проверьте конфигурацию анализатора
Настройте и проверьте встроенный анализатор (
english):Конфигурация: Определите параметры анализатора для встроенного анализатора английского языка.
Проверка: Используйте
run_analyzer, чтобы проверить, что конфигурация производит ожидаемую токенизацию.
# 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 }# restfulНастройка и проверка пользовательского анализатора:
Конфигурация: Определите пользовательский анализатор, который использует стандартный токенизатор вместе со встроенным фильтром строчных букв и пользовательскими фильтрами для длины токена и стоп-слов.
Проверка: Используйте
run_analyzer, чтобы убедиться, что пользовательская конфигурация обрабатывает текст так, как нужно.
# 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
Шаг 3: Добавьте поля в схему
Теперь, когда вы проверили свои конфигурации анализатора, добавьте их в поля схемы:
# 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
Шаг 4: Подготовьте параметры индекса и создайте коллекцию
# 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
Что дальше
После настройки анализатора вы можете интегрировать его с функциями текстового поиска, предоставляемыми Milvus. Подробнее: