Analyzer라는 이름만 들으면 numeric data와 자연스럽게 연결되는 나의 편견 때문에 아직도 왜 검색엔진의 Analyzer인지 명확하게 와닿지는 않지만 ( 아마 '분석' 후 가공 정도의 의미일 것으로 추측한다. ) , Opensearch에서의 Analyzer는 Tokenizer와 불용어 등의 filter 를 하는 즉 python 내 nltk ( Natural Language Tookit ) 과 비슷한 Pre-Processing 역할을 해준다.
정확히는 Character Filter -> Tokenizer -> Token Filters의 일련의 과정들을 한 번에 처리해주는 Piple-line역할을 한다.
**Charactor Filter : 토크나이징 전에 텍스트를 전처리 (예: HTML 태그 제거, 특정 문자 치환 등)
** Tokenizer : 텍스트를 토큰(단어 등)으로 나누는 역할
** Token Filters : Tokenizer가 생성한 토큰들을 추가로 변형 (소문자화, 불용어 제거 등 )
단, 모든 analyzer는 Mapping시, text 타입에만 적용이 가능하다. 애초에 keyword 부터 integer, boolean 등등은 text analyze가 필요없기 때문이다.
| Analyzer | 처리 방식 | 토큰 분리 예시 |
| standard | - 단어 경계에서 토큰화- 대부분의 구두점 제거- 소문자 변환 | [it’s, fun, to, contribute, a, brand, new, pr, or, 2, to, opensearch] |
| simple | - 비문자(숫자·문자 외) 기준 토큰화- 비문자 제거- 소문자 변환 | [it, s, fun, to, contribute, a, brand, new, pr, or, to, opensearch] |
| whitespace | - 공백(스페이스, 탭 등) 기준 토큰화 | [It’s, fun, to, contribute, a, brand-new, PR, or, 2, to, OpenSearch!] |
| stop | - simple 분석 + 불용어(예: “to”, “or”, “a”) 제거- 소문자 변환 | [s, fun, contribute, brand, new, pr, opensearch] |
| keyword | - 전체 문자열을 단일 토큰으로 취급 (no-op) | [It’s fun to contribute a brand-new PR or 2 to OpenSearch!] |
| pattern | - 정규표현식 기반 토큰화- 소문자 변환- (옵션) 불용어 제거 | [it, s, fun, to, contribute, a, brand, new, pr, or, 2, to, opensearch] |
| language | - 언어별 형태소 분석·스테밍·불용어 제거 (영어 기준), 한국어는 nori 플러그인 사용 | [fun, contribut, brand, new, pr, 2, opensearch] |
| fingerprint | - 비문자 기준 토큰화- ASCII 정규화(asciifolding)- 소문자 변환- 토큰 중복 제거·정렬·단일 토큰화 | [2 a brand contribute fun it's new opensearch or pr to] |
다만 위는 전통적인 bm25를 위한 기법이며, 문맥이 중요할 수도 있는 벡터검색에서는 오히려 임베딩 및 검색 품질이 저해될 수 있다.
예시 )
"How do I go to the airport?"
→ "go airport" 로 줄이면 의미 흐름 망가짐
따라서 아래와 같이 다른 데이터셋을 사용하여 hybrid search를 하는 방법도 고려해봐야한다. 각각 데이터셋이나 목적이 다르기 때문에 결과물에 따라 휴리스틱하게 적용하면 될 것 같다.
custom_bm25_analyzer 정의
PUT my-index
{
"settings": {
"analysis": {
"analyzer": {
"custom_bm25_analyzer": {
"type": "custom",
"tokenizer": "standard",
"filter": ["lowercase", "stop", "stemmer"]
}
}
}
},
데이터셋 분리 및 정의 ( bm25 analyzer / raw / 임베딩 vector )
"mappings": {
"properties": {
"content_bm25": {
"type": "text",
"analyzer": "custom_bm25_analyzer"
},
"content_raw": {
"type": "text",
"index": false
},
"content_vector": {
"type": "dense_vector",
"dims": 768
}
}
}
}
데이터 Ingest 시 순서
POST my-index/_doc
{
"content_bm25": "OpenSearch is fast and flexible for text search.",
"content_raw": "OpenSearch is fast and flexible for text search.",
"content_vector": [0.12, -0.34, ..., 0.56] // 길이 768짜리 벡터
}
검색 시, Hybrid search 적용
POST my-index/_search
{
"query": {
"hybrid": {
"queries": [
{
"lexical": {
"field": "content_bm25",
"query": "flexible text engine"
}
},
{
"semantic": {
"field": "content_vector",
"query": "flexible text engine",
"model_id": "my-huggingface-model"
}
}
]
}
}
}
'LLM' 카테고리의 다른 글
| OpenSearch - Hybrid Search에 대해서 (0) | 2025.05.14 |
|---|---|
| OpenSearch 란 ? - AWS의 ElasticSearch, 신흥 Vector 검색의 강자가 될 것인가 (0) | 2025.05.14 |
| bm25란 ? - TF IDF의 진화형 (0) | 2025.05.13 |
| aNN(Approximate Nearest Neighbor) (0) | 2025.05.12 |
| kNN이란 ? ( k-Nearest Neighbors ) (0) | 2025.05.12 |