アナライザーの概要
テキスト処理において、アナライザーは生テキストを構造化された検索可能な形式に変換する重要なコンポーネントである。アナライザーは通常、トークナイザーと フィルターという2つのコア要素で構成される。これらは共に入力テキストをトークンに変換し、トークンを洗練させ、効率的なインデックス作成と検索に備えます。
Milvusでは、アナライザはコレクション作成時にVARCHAR フィールドをコレクションスキーマに追加する際に設定されます。アナライザによって生成されたトークンは、キーワードマッチングのためのインデックスを構築するために使用したり、全文検索のためにスパース埋め込みに変換したりすることができます。詳細については、全文検索、フレーズ一致、またはテキスト一致を参照してください。
アナライザーの使用はパフォーマンスに影響する場合があります:
全文検索:全文検索:全文検索では、トークン化の完了を待つ必要があるため、DataNodeと QueryNodeチャネルはデータをより遅く消費します。その結果、新しく取り込まれたデータが検索に利用できるようになるまでに時間がかかる。
キーワードマッチ:キーワードマッチの場合、インデックスを構築する前にトークン化が完了する必要があるため、インデックス作成も遅くなります。
アナライザーの構造
Milvusのアナライザーは1つのトークナイザーと 0つ以上のフィルターから構成されます。
トークナイザー:トークナイザーは入力テキストをトークンと呼ばれる個別の単位に分割します。トークンはトークン化の種類によって、単語であったりフレーズであったりします。
フィルター:たとえば、小文字にしたり、一般的な単語を削除したりします。
トークナイザーは UTF-8 形式のみをサポートしています。その他の形式については、今後のリリースでサポートが追加される予定です。
以下のワークフローは、アナライザーがテキストを処理する方法を示しています。
アナライザー・プロセスのワークフロー
アナライザの種類
Milvusでは、様々なテキスト処理ニーズに対応するため、2種類のアナライザを提供しています:
内蔵アナライザ:ビルトイン アナライザ: 最小限のセットアップで一般的なテキスト処理タスクに対応する定義済みコンフィギュレーションです。複雑な設定が不要なため、汎用的な検索に最適です。
カスタムアナライザー:より高度な要件に対応するカスタム・アナライザでは、トークナイザとゼロ個以上のフィルタの両方を指定することで、独自の設定を定義できます。このレベルのカスタマイズは、テキスト処理を正確に制御する必要がある特殊なユースケースで特に役立ちます。
- コレクション作成時にアナライザ設定を省略した場合、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"
}'
フィルター
フィルタは、トークナイザによって生成されたトークンに作用するオプションのコンポーネントで、必要に応じて変換や精製を行います。たとえば、トークン化された用語["Vector", "Database", "Built", "for", "Scale"] にlowercase フィルタを適用すると、次のようになります:
["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"] } ] }'
使用例
この例では、以下を含むコレクションスキーマを作成します:
埋め込み用のベクトル・フィールド。
テキスト処理用の2つの
VARCHARフィールド:1つのフィールドは組み込みのアナライザを使用する。
もう1つはカスタムアナライザを使用します。
これらの構成をコレクションに組み込む前に、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が提供するテキスト検索機能と統合することができます。詳細はこちら: