๊ฐ์
OpenSearch์ RRF(Reciprocal Rank Fusion)๋ ํ์ด๋ธ๋ฆฌ๋ ๊ฒ์์์ ์ฌ๋ฌ ์ฟผ๋ฆฌ์ ๊ฒฐ๊ณผ๋ฅผ ์์ ๊ธฐ๋ฐ์ผ๋ก ๊ฒฐํฉํ๋ ๊ธฐ๋ฅ์ด๋ค. OpenSearch 2.19์์ score-ranker-processor
์ ์ผ๋ถ๋ก ๋์
๋์์ผ๋ฉฐ1, Neural Search ํ๋ฌ๊ทธ์ธ์ ํตํด ์ ๊ณต๋๋ค.
RRF๋ ์ ์(score) ๋์ ์์(rank)๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ๊ฒฐ๊ณผ๋ฅผ ๊ฒฐํฉํ์ฌ, ์๋ก ๋ค๋ฅธ ๊ฒ์ ๋ฐฉ์(ํค์๋ ๊ฒ์, ๋ฒกํฐ ๊ฒ์ ๋ฑ)์ ์ ์ ์ค์ผ์ผ ์ฐจ์ด ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๋ค.
๋ฐฐ๊ฒฝ
๊ธฐ์กด ํ์ด๋ธ๋ฆฌ๋ ๊ฒ์์ ํ๊ณ
OpenSearch 2.10๋ถํฐ ์ง์๋ ํ์ด๋ธ๋ฆฌ๋ ๊ฒ์์ normalization-processor
๋ฅผ ์ฌ์ฉํ์ฌ ์ ์๋ฅผ ์ ๊ทํํ๊ณ ๊ฒฐํฉํ๋ค. ์ด ๋ฐฉ์์ ๋ค์๊ณผ ๊ฐ์ ๋ฌธ์ ๊ฐ ์์๋ค:
- ์ ์ ์ ๊ทํ์ ๋ณต์ก์ฑ: Min-max, L2 ๋ฑ ์ ๊ทํ ๊ธฐ๋ฒ ์ ํ ํ์
- ์ด์์น ๋ฏผ๊ฐ์ฑ: ๊ทน๋จ์ ์ธ ์ ์ ๊ฐ์ด ๊ฒฐ๊ณผ๋ฅผ ์๊ณก
- ๊ฐ์ค์น ์กฐ์ ํ์: ๊ฐ ๊ฒ์ ๋ฐฉ์์ ๊ฐ์ค์น๋ฅผ ์๋์ผ๋ก ํ๋
RRF์ ํด๊ฒฐ์ฑ
RRF๋ ์ ์๊ฐ ์๋ ์์๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ๊ฒฐ๊ณผ๋ฅผ ๊ฒฐํฉํ์ฌ:
- ์ ์ ์ ๊ทํ ๋ถํ์
- ์ด์์น์ ๊ฐ๊ฑด
- ํ๋ผ๋ฏธํฐ ํ๋ ์ต์ํ (๊ธฐ๋ณธ๊ฐ k=60)
์๋ ์๋ฆฌ
RRF ๊ณต์
๊ฐ ๋ฌธ์์ ์ต์ข ์ ์๋ ๋ค์๊ณผ ๊ฐ์ด ๊ณ์ฐ๋๋ค:
์ฌ๊ธฐ์:
- = ๋ฌธ์
- = ์ฟผ๋ฆฌ ์งํฉ (hybrid query์ ์๋ธ์ฟผ๋ฆฌ๋ค)
- = ์์ ์์ (๊ธฐ๋ณธ๊ฐ: 60)
- = ์ฟผ๋ฆฌ ์์ ๋ฌธ์ ์ ์์ (1๋ถํฐ ์์)
์์
ํ์ด๋ธ๋ฆฌ๋ ์ฟผ๋ฆฌ ๊ตฌ์ฑ:
- ์ฟผ๋ฆฌ 1 (BM25): ๋ฌธ์ A(1์), ๋ฌธ์ B(2์), ๋ฌธ์ C(3์)
- ์ฟผ๋ฆฌ 2 (Neural): ๋ฌธ์ C(1์), ๋ฌธ์ A(2์), ๋ฌธ์ D(3์)
RRF ์ ์ ๊ณ์ฐ (k=60):
- ๋ฌธ์ A:
- ๋ฌธ์ B:
- ๋ฌธ์ C:
- ๋ฌธ์ D:
์ต์ข ์์: A โ C โ B โ D
๊ตฌ์ฑ ๋ฐฉ๋ฒ
1. Search Pipeline ์์ฑ
RRF๋ฅผ ์ฌ์ฉํ๋ ค๋ฉด ๋จผ์ score-ranker-processor
๋ฅผ ํฌํจํ๋ search pipeline์ ์์ฑํด์ผ ํ๋ค.
๊ธฐ๋ณธ ์ค์ (k=60)
PUT /_search/pipeline/rrf-pipeline
{
"description": "RRF ๊ธฐ๋ฐ ํ์ด๋ธ๋ฆฌ๋ ๊ฒ์ ํ์ดํ๋ผ์ธ",
"phase_results_processors": [
{
"score-ranker-processor": {
"combination": {
"technique": "rrf"
}
}
}
]
}
์ปค์คํ rank constant
PUT /_search/pipeline/rrf-pipeline
{
"description": "์ปค์คํ
rank constant๋ฅผ ์ฌ์ฉํ๋ RRF",
"phase_results_processors": [
{
"score-ranker-processor": {
"combination": {
"technique": "rrf",
"rank_constant": 40
}
}
}
]
}
rank_constant์ ์ญํ :
- ๊ฐ์ด ์์์๋ก: ์์ ๋ฌธ์์ ์ํฅ๋ ฅ ์ฆ๊ฐ (์์ ์ฐจ์ด ๊ฐ์กฐ)
- ๊ฐ์ด ํด์๋ก: ์์ ๊ฐ ์ ์ ์ฐจ์ด ํํํ (๊ท ๋ฑํ ์ํฅ)
- ์ ์ฝ: 1 ์ด์์ด์ด์ผ ํจ
๊ฐ์ค์น ์ ์ฉ
์๋ธ์ฟผ๋ฆฌ๋ง๋ค ๋ค๋ฅธ ๊ฐ์ค์น๋ฅผ ๋ถ์ฌํ ์ ์๋ค (๊ธฐ๋ณธ๊ฐ์ ๋ชจ๋ 1.0):
PUT /_search/pipeline/weighted-rrf-pipeline
{
"description": "๊ฐ์ค์น๊ฐ ์ ์ฉ๋ RRF",
"phase_results_processors": [
{
"score-ranker-processor": {
"combination": {
"technique": "rrf",
"rank_constant": 60,
"parameters": {
"weights": [0.7, 0.3]
}
}
}
}
]
}
์ ์์์์:
- ์ฒซ ๋ฒ์งธ ์ฟผ๋ฆฌ (์: BM25): ๊ฐ์ค์น 0.7
- ๋ ๋ฒ์งธ ์ฟผ๋ฆฌ (์: Neural): ๊ฐ์ค์น 0.3
2. ํ์ด๋ธ๋ฆฌ๋ ์ฟผ๋ฆฌ ์คํ
์์ฑํ pipeline์ ์ฌ์ฉํ์ฌ ํ์ด๋ธ๋ฆฌ๋ ์ฟผ๋ฆฌ๋ฅผ ์คํํ๋ค.
BM25 + Neural Search
GET /my-index/_search
{
"query": {
"hybrid": {
"queries": [
{
"match": {
"content": "OpenSearch vector search"
}
},
{
"neural": {
"content_embedding": {
"query_text": "OpenSearch vector search",
"model_id": "your-model-id",
"k": 10
}
}
}
]
}
},
"search_pipeline": "rrf-pipeline"
}
์ฌ๋ฌ ๊ฒ์ ๋ฐฉ์ ๊ฒฐํฉ
GET /my-index/_search
{
"query": {
"hybrid": {
"queries": [
{
"match": {
"title": "machine learning"
}
},
{
"neural": {
"embedding": {
"query_text": "machine learning",
"model_id": "model-id"
}
}
},
{
"neural_sparse": {
"sparse_embedding": {
"query_text": "machine learning",
"model_id": "sparse-model-id"
}
}
}
]
}
},
"search_pipeline": "weighted-rrf-pipeline"
}
์ฌ์ฉ ์ฌ๋ก
1. ํค์๋ + ์๋ฏธ ๊ฒ์
์๋๋ฆฌ์ค: ์ ํํ ํค์๋ ๋งค์นญ๊ณผ ์๋ฏธ์ ์ ์ฌ์ฑ์ ๋ชจ๋ ๊ณ ๋ ค
{
"query": {
"hybrid": {
"queries": [
{
"multi_match": {
"query": "OpenSearch",
"fields": ["title^2", "content"]
}
},
{
"neural": {
"content_embedding": {
"query_text": "OpenSearch",
"model_id": "dense-model"
}
}
}
]
}
},
"search_pipeline": "rrf-pipeline"
}
ํจ๊ณผ:
- BM25๊ฐ โOpenSearchโ ํค์๋๋ฅผ ์ ํํ ํฌํจํ ๋ฌธ์ ์ฐ์
- Neural search๊ฐ โElasticsearchโ, โvector databaseโ ๋ฑ ๊ด๋ จ ๊ฐ๋ ํฌํจ
2. Sparse + Dense ๋ฒกํฐ ๊ฒ์
์๋๋ฆฌ์ค: ํฌ์ ๋ฒกํฐ์ ๋ฐ์ง ๋ฒกํฐ์ ์ฅ์ ๊ฒฐํฉ
{
"query": {
"hybrid": {
"queries": [
{
"neural_sparse": {
"sparse_embedding": {
"query_text": "vector similarity search",
"model_id": "opensearch-neural-sparse-encoding-v2"
}
}
},
{
"neural": {
"dense_embedding": {
"query_text": "vector similarity search",
"model_id": "sentence-transformers"
}
}
}
]
}
},
"search_pipeline": "rrf-pipeline"
}
ํจ๊ณผ:
- Sparse: ๋ช ์์ ๋จ์ด ๋งค์นญ + term expansion
- Dense: ๊น์ ์๋ฏธ์ ์ ์ฌ์ฑ
3. ๋ค์ค ํ๋ ๊ฒ์
์๋๋ฆฌ์ค: ์ ๋ชฉ๊ณผ ๋ณธ๋ฌธ์ ์๋ก ๋ค๋ฅธ ๊ฒ์ ์ ๋ต ์ ์ฉ
{
"query": {
"hybrid": {
"queries": [
{
"match": {
"title": {
"query": "OpenSearch tutorial",
"boost": 2.0
}
}
},
{
"match": {
"content": "OpenSearch tutorial"
}
},
{
"neural": {
"content_embedding": {
"query_text": "OpenSearch tutorial",
"model_id": "model-id"
}
}
}
]
}
},
"search_pipeline": "weighted-rrf-pipeline"
}
RRF vs Normalization
OpenSearch 2.10~2.18์์ ์ฌ์ฉ๋ normalization ๋ฐฉ์๊ณผ ๋น๊ต:
์ธก๋ฉด | Normalization | RRF |
---|---|---|
๊ธฐ๋ฐ | ์ ์(score) | ์์(rank) |
์ ๊ทํ | ํ์ (min-max, L2 ๋ฑ) | ๋ถํ์ |
์ด์์น ์ํฅ | ๋์ | ๋ฎ์ |
ํ๋ผ๋ฏธํฐ ํ๋ | ๋ณต์ก (๊ธฐ๋ฒ, ๊ฐ์ค์น) | ๋จ์ (k ๊ฐ) |
ํฌ๋ช ์ฑ | ๋ฎ์ (์ ์ ๋ณํ) | ๋์ (์์ ๊ธฐ๋ฐ) |
๋์ ๋ฒ์ | 2.10 | 2.19 |
3.0 ์ถ๊ฐ ๊ธฐ๋ฅ | Z-score normalization ์ถ๊ฐ | ๊ณ์ ์ง์ |
์ธ์ RRF๋ฅผ ์ฌ์ฉํ ๊น?
RRF ๊ถ์ฅ ์ํฉ:
- ์๋ก ๋ค๋ฅธ ์ ์ ์ค์ผ์ผ์ ๊ฒ์ ๋ฐฉ์ ๊ฒฐํฉ
- ํ๋ผ๋ฏธํฐ ํ๋ ์๊ฐ์ด ์ ํ์ ์ธ ๊ฒฝ์ฐ
- ๊ฐ๊ฑดํ ๊ฒ์ ๊ฒฐ๊ณผ๊ฐ ์ค์ํ ๊ฒฝ์ฐ
- ์์๊ฐ ์ ์๋ณด๋ค ์ ๋ขฐํ ๋งํ ๊ฒฝ์ฐ
Normalization ๊ณ ๋ ค ์ํฉ:
- ์ ์ ์์ฒด์ ์๋ฏธ๊ฐ ์ค์ํ ๊ฒฝ์ฐ
- ๋งค์ฐ ์ธ๋ฐํ ๊ฐ์ค์น ์กฐ์ ์ด ํ์ํ ๊ฒฝ์ฐ
- ํน์ ์ ๊ทํ ๊ธฐ๋ฒ์ ๋ํ ๋๋ฉ์ธ ์ง์์ด ์๋ ๊ฒฝ์ฐ
๋ชจ๋ฒ ์ฌ๋ก
1. rank_constant ์ ํ
{
"combination": {
"technique": "rrf",
"rank_constant": 60 // ๊ธฐ๋ณธ๊ฐ, ๋๋ถ๋ถ์ ๊ฒฝ์ฐ ์ ํฉ
}
}
- k=60: ๋๋ถ๋ถ์ ๊ฒฝ์ฐ ๊ถ์ฅ (์ ๋ ผ๋ฌธ ๊ธฐ์ค)
- k=20~40: ์์ ๋ฌธ์ ์ํฅ๋ ฅ ์ฆ๊ฐ, ๋ช ํํ ๊ด๋ จ์ฑ ์ฐจ์ด ์์ ๋
- k=80~100: ์์ ๊ฐ ์ฐจ์ด ์ํ, ํ์์ ๊ฒ์
2. ๊ฐ์ค์น ์ค์
{
"combination": {
"technique": "rrf",
"parameters": {
"weights": [0.6, 0.4] // BM25: 60%, Neural: 40%
}
}
}
๊ฐ์ค์น ๊ฒฐ์ ๊ธฐ์ค:
- ํค์๋ ์ค์ฌ ๋๋ฉ์ธ (๋ฒ๋ฅ , ๊ธฐ์ ๋ฌธ์): BM25 ๊ฐ์ค์น ๋์ (0.7~0.8)
- ์๋ฏธ ์ค์ฌ ๋๋ฉ์ธ (๋ด์ค, ๋ธ๋ก๊ทธ): Neural ๊ฐ์ค์น ๋์ (0.6~0.7)
- ๊ท ํ ์ ๊ทผ: ๋์ผ ๊ฐ์ค์น (0.5, 0.5) ๋๋ ์๋ต (๊ธฐ๋ณธ๊ฐ 1.0)
3. ์ฟผ๋ฆฌ ์์
{
"hybrid": {
"queries": [
{ "match": { ... } }, // 1๋ฒ: BM25 (๊ฐ์ค์น weights[0])
{ "neural": { ... } } // 2๋ฒ: Neural (๊ฐ์ค์น weights[1])
]
}
}
์ค์: weights
๋ฐฐ์ด์ ์์๋ queries
๋ฐฐ์ด ์์์ ์ผ์นํด์ผ ํจ.
4. ์ฑ๋ฅ ์ต์ ํ
์ธ๋ฑ์ค ํฌ๊ธฐ์ ๋ฐ๋ฅธ ์ ๋ต:
- ์ํ ์ธ๋ฑ์ค (< 100๋ง ๋ฌธ์): ๋ชจ๋ ์ฟผ๋ฆฌ ๋๋ฑํ๊ฒ ์คํ
- ๋ํ ์ธ๋ฑ์ค (> 1000๋ง ๋ฌธ์):
- BM25๋ก 1์ฐจ ํํฐ๋ง (์์ 1000๊ฐ)
- Neural๋ก 2์ฐจ ์ ๋ฐ ๊ฒ์
- RRF๋ก ์ต์ข ์์ ๊ฒฐ์
์ ํ์ฌํญ
1. ๋ฒ์ ์๊ตฌ์ฌํญ
- OpenSearch 2.19 ์ด์ ํ์
- Neural Search ํ๋ฌ๊ทธ์ธ ์ค์น ํ์
2. ์ฟผ๋ฆฌ ์ ์ฝ
hybrid
์ฟผ๋ฆฌ ํ์ ์ฌ์ฉ ํ์- ๊ฐ ์๋ธ์ฟผ๋ฆฌ๋ ๋ ๋ฆฝ์ ์ผ๋ก ์คํ ๊ฐ๋ฅํด์ผ ํจ
- ์ผ๋ถ ์ฟผ๋ฆฌ ํ์ ๊ณผ ํธํ ๋ถ๊ฐ (bool ์ฟผ๋ฆฌ ๋ด๋ถ ๋ฑ)
3. ๊ฐ์ค์น ์ ์ฝ
- ๊ฐ์ค์น ๋ฐฐ์ด ๊ธธ์ด๋ ์ฟผ๋ฆฌ ๊ฐ์์ ๋์ผํด์ผ ํจ
- ๊ฐ์ค์น๋ ์์์ฌ์ผ ํจ (์์ ๋ถ๊ฐ)
- ๊ฐ์ค์น ํฉ์ด 1์ผ ํ์๋ ์์ (์๋ ์ ๊ทํ)
๊ด๋ จ ๊ธฐ์
- RRF (Reciprocal Rank Fusion)
- OpenSearch Neural Sparse Search
- ํฌ์ ๋ฒกํฐ์ ๋ฐ์ง ๋ฒกํฐ
- BM25
- Learned Sparse Retrieval
์ฐธ๊ณ ์๋ฃ
๊ณต์ ๋ฌธ์
- Score ranker processor - OpenSearch Documentation
- Hybrid search - OpenSearch Documentation
- Normalization processor - OpenSearch Documentation
๋ธ๋ก๊ทธ ๋ฐ ํํ ๋ฆฌ์ผ
- Explore OpenSearch 2.19 - OpenSearch
- Improve search relevance with hybrid search - OpenSearch
- Hybrid Search with Amazon OpenSearch Service - AWS Blog
GitHub
- RFC: Design for Incorporating RRF into Neural Search
- Feature: Add Rank based combination technique
- Feature: Add custom weights for RRF